moss_generator 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: da606f58ca3548eb7ce5fbef96498730f1046575caff4c23e741f3a6d4699e70
4
+ data.tar.gz: babfb54c2a5c729390e336b243a2ddf86991ce84695922a744dfe00a2de2e00a
5
+ SHA512:
6
+ metadata.gz: 36de7663f16245f15ec8cddc423de8d686d5721fe5ce42f402f188a0e76a6582fe4b8df12eae36b6fd619af08927f7cafabc1fce6493c51fbd859262b936e222
7
+ data.tar.gz: 8cbbd70a3e2992ba354042813dc9a19e22ee62b38160466fd30ef0a3686114d8fa1fe26922e1f4d81ca08a9521e709af22c510ea1c0fa84464027bb207f10048
@@ -0,0 +1,37 @@
1
+ name: Gem push
2
+ on: [push]
3
+ jobs:
4
+ github:
5
+ name: Build + Publish to GitHub
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v2
9
+ - name: Set up Ruby 2.6
10
+ uses: actions/setup-ruby@v1
11
+ with:
12
+ version: 2.6.5
13
+ - name: Publish to GitHub
14
+ uses: jstastny/publish-gem-to-github@master
15
+ with:
16
+ token: ${{ secrets.GITHUB_TOKEN }}
17
+ owner: standout
18
+
19
+ rubygems:
20
+ name: Build + Publish to Rubygems
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v2
24
+ - name: Set up Ruby 2.6
25
+ uses: actions/setup-ruby@v1
26
+ with:
27
+ version: 2.6.x
28
+ - name: Publish to RubyGems
29
+ run: |
30
+ mkdir -p $HOME/.gem
31
+ touch $HOME/.gem/credentials
32
+ chmod 0600 $HOME/.gem/credentials
33
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
34
+ gem build *.gemspec
35
+ gem push *.gem
36
+ env:
37
+ GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
@@ -0,0 +1,18 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 2.6.5
14
+ - name: Run the default task
15
+ run: |
16
+ gem install bundler -v 2.2.3
17
+ bundle install
18
+ bundle exec rake
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,15 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+ - rubocop-rake
5
+
6
+ AllCops:
7
+ NewCops: enable
8
+ TargetRubyVersion: 2.5
9
+
10
+ Layout/LineLength:
11
+ Max: 80
12
+
13
+ Metrics/BlockLength:
14
+ Exclude:
15
+ - 'spec/**/*'
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in moss_generator.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+
10
+ gem 'rspec', '~> 3.0'
11
+
12
+ gem 'rubocop', '~> 1.7'
13
+ gem 'rubocop-performance', '~> 1.9'
14
+ gem 'rubocop-rake', '~> 0.5'
15
+ gem 'rubocop-rspec', '~> 2.1'
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ moss_generator (0.1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.1)
10
+ diff-lcs (1.4.4)
11
+ parallel (1.20.1)
12
+ parser (3.0.0.0)
13
+ ast (~> 2.4.1)
14
+ rainbow (3.0.0)
15
+ rake (13.0.3)
16
+ regexp_parser (2.0.3)
17
+ rexml (3.2.4)
18
+ rspec (3.10.0)
19
+ rspec-core (~> 3.10.0)
20
+ rspec-expectations (~> 3.10.0)
21
+ rspec-mocks (~> 3.10.0)
22
+ rspec-core (3.10.1)
23
+ rspec-support (~> 3.10.0)
24
+ rspec-expectations (3.10.1)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.10.0)
27
+ rspec-mocks (3.10.1)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-support (3.10.1)
31
+ rubocop (1.7.0)
32
+ parallel (~> 1.10)
33
+ parser (>= 2.7.1.5)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ regexp_parser (>= 1.8, < 3.0)
36
+ rexml
37
+ rubocop-ast (>= 1.2.0, < 2.0)
38
+ ruby-progressbar (~> 1.7)
39
+ unicode-display_width (>= 1.4.0, < 2.0)
40
+ rubocop-ast (1.3.0)
41
+ parser (>= 2.7.1.5)
42
+ rubocop-performance (1.9.1)
43
+ rubocop (>= 0.90.0, < 2.0)
44
+ rubocop-ast (>= 0.4.0)
45
+ rubocop-rake (0.5.1)
46
+ rubocop
47
+ rubocop-rspec (2.1.0)
48
+ rubocop (~> 1.0)
49
+ rubocop-ast (>= 1.1.0)
50
+ ruby-progressbar (1.10.1)
51
+ unicode-display_width (1.7.0)
52
+
53
+ PLATFORMS
54
+ x86_64-darwin-19
55
+
56
+ DEPENDENCIES
57
+ moss_generator!
58
+ rake (~> 13.0)
59
+ rspec (~> 3.0)
60
+ rubocop (~> 1.7)
61
+ rubocop-performance (~> 1.9)
62
+ rubocop-rake (~> 0.5)
63
+ rubocop-rspec (~> 2.1)
64
+
65
+ BUNDLED WITH
66
+ 2.2.3
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 frdrkolsson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ # MossGenerator
2
+
3
+ Generate CSV-formatted string from specified data to be able to create a [MOSS (Mini One Stop Shop)](https://www.skatteverket.se/foretagochorganisationer/moms/deklareramoms/mossredovisningavmomspadigitalatjanster.4.3aa8c78a1466c5845876a05.html) file for Skatteverket.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'moss_generator'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install moss_generator
20
+
21
+ ## Usage
22
+
23
+ ### Stripe
24
+ ```ruby
25
+ # charges - array of charges in json-format from stripe
26
+ # vat_number - the company's vat number
27
+ # period - for which period of the year this report should be for
28
+ # year - corresponding year for the period
29
+ MossGenerator::Stripe.call(charges, vat_number, period, year)
30
+ ```
31
+
32
+ ## Development
33
+
34
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
35
+
36
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
37
+
38
+ ## Vat Rates
39
+
40
+ ```
41
+ * Current Vat rates fetched at 2021/01/11
42
+ * Vat rates in spec file from 2021/01/11
43
+ ```
44
+
45
+
46
+ Vat rates currently needs to be updated manually in `config/vat_rates.json`.
47
+ [VatLayer](https://vatlayer.com) has a nice API one could use (100 requests for free each month) to make this process automatic. Until then, do a request through their api and copy JSON-response to file and remove status from response.
48
+
49
+ Rates can also be found on the official website of the European Union [https://europa.eu/youreurope/business/taxation/vat/vat-rules-rates/index_en.htm#shortcut-8](https://europa.eu/youreurope/business/taxation/vat/vat-rules-rates/index_en.htm#shortcut-8), updates two times a year.
50
+
51
+ ## Contributing
52
+
53
+ Bug reports and pull requests are welcome on GitHub at https://github.com/standout/moss_generator.
54
+
55
+ ## License
56
+
57
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'moss_generator'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,383 @@
1
+ {
2
+ "AT": {
3
+ "country_name": "Austria",
4
+ "standard_rate": 20,
5
+ "reduced_rates": {
6
+ "foodstuffs": 10,
7
+ "books": 5,
8
+ "pharmaceuticals": 10,
9
+ "passenger transport": 10,
10
+ "newspapers": 5,
11
+ "admission to cultural events": 5,
12
+ "hotels": 5,
13
+ "admission to entertainment events": 5
14
+ }
15
+ },
16
+ "BE": {
17
+ "country_name": "Belgium",
18
+ "standard_rate": 21,
19
+ "reduced_rates": {
20
+ "restaurants": 12,
21
+ "foodstuffs": 6,
22
+ "water": 6,
23
+ "pharmaceuticals": 6,
24
+ "medical": 6,
25
+ "books": 6,
26
+ "newspapers": 6,
27
+ "hotels": 6,
28
+ "admission to cultural events": 6,
29
+ "admission to entertainment events": 6
30
+ }
31
+ },
32
+ "BG": {
33
+ "country_name": "Bulgaria",
34
+ "standard_rate": 20,
35
+ "reduced_rates": {
36
+ "hotels": 9
37
+ }
38
+ },
39
+ "HR": {
40
+ "country_name": "Croatia",
41
+ "standard_rate": 25,
42
+ "reduced_rates": {
43
+ "hotels": 13,
44
+ "newspapers": 13
45
+ }
46
+ },
47
+ "CY": {
48
+ "country_name": "Cyprus",
49
+ "standard_rate": 19,
50
+ "reduced_rates": {
51
+ "hotels": 9,
52
+ "restaurants": 9,
53
+ "foodstuffs": 5,
54
+ "books": 5,
55
+ "pharmaceuticals": 5,
56
+ "medical": 5,
57
+ "passenger transport": 5,
58
+ "newspapers": 5,
59
+ "admission to cultural events": 5,
60
+ "admission to entertainment events": 5,
61
+ "admission to sporting events": 5
62
+ }
63
+ },
64
+ "CZ": {
65
+ "country_name": "Czech Republic",
66
+ "standard_rate": 21,
67
+ "reduced_rates": {
68
+ "foodstuffs": 15,
69
+ "medical": 10,
70
+ "pharmaceuticals": 10,
71
+ "passenger transport": 15,
72
+ "newspapers": 15,
73
+ "admission to cultural events": 15,
74
+ "admission to sporting events": 15,
75
+ "admission to entertainment events": 15,
76
+ "hotels": 15,
77
+ "books": 10,
78
+ "baby foodstuffs": 10
79
+ }
80
+ },
81
+ "DK": {
82
+ "country_name": "Denmark",
83
+ "standard_rate": 25,
84
+ "reduced_rates": null
85
+ },
86
+ "EE": {
87
+ "country_name": "Estonia",
88
+ "standard_rate": 20,
89
+ "reduced_rates": {
90
+ "books": 9,
91
+ "pharmaceuticals": 9,
92
+ "medical": 9,
93
+ "hotels": 9
94
+ }
95
+ },
96
+ "FI": {
97
+ "country_name": "Finland",
98
+ "standard_rate": 24,
99
+ "reduced_rates": {
100
+ "foodstuffs": 14,
101
+ "restaurants": 14,
102
+ "books": 10,
103
+ "pharmaceuticals": 10,
104
+ "passenger transport": 10,
105
+ "newspapers": 10,
106
+ "admission to cultural events": 10,
107
+ "admission to sporting events": 10,
108
+ "admission to entertainment events": 10,
109
+ "hotels": 10
110
+ }
111
+ },
112
+ "FR": {
113
+ "country_name": "France",
114
+ "standard_rate": 20,
115
+ "reduced_rates": {
116
+ "pharmaceuticals": 2.1,
117
+ "passenger transport": 10,
118
+ "admission to cultural events": 10,
119
+ "admission to sporting events": 10,
120
+ "admission to entertainment events": 10,
121
+ "hotels": 10,
122
+ "accommodation": 10,
123
+ "restaurants": 10,
124
+ "medical": 5.5,
125
+ "foodstuffs": 5.5,
126
+ "e-books": 5.5,
127
+ "books": 5.5,
128
+ "newspapers": 2.1
129
+ }
130
+ },
131
+ "DE": {
132
+ "country_name": "Germany",
133
+ "standard_rate": 19,
134
+ "reduced_rates": {
135
+ "foodstuffs": 7,
136
+ "books": 7,
137
+ "medical": 7,
138
+ "passenger transport": 7,
139
+ "newspapers": 7,
140
+ "admission to cultural events": 7,
141
+ "admission to entertainment events": 7,
142
+ "hotels": 7
143
+ }
144
+ },
145
+ "GR": {
146
+ "country_name": "Greece",
147
+ "standard_rate": 24,
148
+ "reduced_rates": {
149
+ "foodstuffs": 13,
150
+ "pharmaceuticals": 13,
151
+ "medical": 13,
152
+ "admission to cultural events": 13,
153
+ "admission to sporting events": 13,
154
+ "admission to entertainment events": 13,
155
+ "books": 6.5,
156
+ "newspapers": 6.5,
157
+ "hotels": 6.5
158
+ }
159
+ },
160
+ "HU": {
161
+ "country_name": "Hungary",
162
+ "standard_rate": 27,
163
+ "reduced_rates": {
164
+ "foodstuffs": 18,
165
+ "hotels": 18,
166
+ "books": 5,
167
+ "pharmaceuticals": 5,
168
+ "medical": 5
169
+ }
170
+ },
171
+ "IE": {
172
+ "country_name": "Ireland",
173
+ "standard_rate": 21,
174
+ "reduced_rates": {
175
+ "medical": 13.5,
176
+ "newspapers": 9,
177
+ "admission to cultural events": 13.5,
178
+ "admission to sporting events": 0,
179
+ "admission to entertainment events": 13.5,
180
+ "hotels": 13.5,
181
+ "restaurants": 13.5,
182
+ "foodstuffs": 4.8,
183
+ "books": 0,
184
+ "childrens clothing": 0
185
+ }
186
+ },
187
+ "IT": {
188
+ "country_name": "Italy",
189
+ "standard_rate": 22,
190
+ "reduced_rates": {
191
+ "pharmaceuticals": 10,
192
+ "passenger transport": 10,
193
+ "admission to cultural events": 10,
194
+ "admission to entertainment events": 10,
195
+ "hotels": 10,
196
+ "restaurants": 10,
197
+ "foodstuffs": 4,
198
+ "medical": 4,
199
+ "books": 4,
200
+ "e-books": 4
201
+ }
202
+ },
203
+ "LV": {
204
+ "country_name": "Latvia",
205
+ "standard_rate": 21,
206
+ "reduced_rates": {
207
+ "books": 12,
208
+ "pharmaceuticals": 12,
209
+ "medical": 12,
210
+ "newspapers": 12,
211
+ "hotels": 12
212
+ }
213
+ },
214
+ "LT": {
215
+ "country_name": "Lithuania",
216
+ "standard_rate": 21,
217
+ "reduced_rates": {
218
+ "books": 9,
219
+ "pharmaceuticals": 5,
220
+ "medical": 5
221
+ }
222
+ },
223
+ "LU": {
224
+ "country_name": "Luxembourg",
225
+ "standard_rate": 17,
226
+ "reduced_rates": {
227
+ "wine": 14,
228
+ "domestic fuel": 14,
229
+ "advertising": 14,
230
+ "bikes": 8,
231
+ "domestic services": 8,
232
+ "books": 3,
233
+ "foodstuffs": 3,
234
+ "pharmaceuticals": 3,
235
+ "medical": 3,
236
+ "passenger transport": 3,
237
+ "newspapers": 3,
238
+ "admission to cultural events": 3,
239
+ "admission to sporting events": 3,
240
+ "admission to entertainment events": 3,
241
+ "hotels": 3,
242
+ "restaurants": 3,
243
+ "e-books": 3
244
+ }
245
+ },
246
+ "MT": {
247
+ "country_name": "Malta",
248
+ "standard_rate": 18,
249
+ "reduced_rates": {
250
+ "hotels": 7,
251
+ "books": 5,
252
+ "medical": 5,
253
+ "newspapers": 5,
254
+ "admission to cultural events": 5,
255
+ "e-books": 5,
256
+ "foodstuffs": 0,
257
+ "pharmaceuticals": 0
258
+ }
259
+ },
260
+ "NL": {
261
+ "country_name": "Netherlands",
262
+ "standard_rate": 21,
263
+ "reduced_rates": {
264
+ "foodstuffs": 9,
265
+ "books": 9,
266
+ "pharmaceuticals": 9,
267
+ "medical": 9,
268
+ "passenger transport": 9,
269
+ "admission to cultural events": 9,
270
+ "admission to entertainment events": 9,
271
+ "hotels": 9,
272
+ "accommodation": 9
273
+ }
274
+ },
275
+ "PL": {
276
+ "country_name": "Poland",
277
+ "standard_rate": 23,
278
+ "reduced_rates": {
279
+ "pharmaceuticals": 8,
280
+ "medical": 8,
281
+ "passenger transport": 8,
282
+ "newspapers": 8,
283
+ "hotels": 8,
284
+ "restaurants": 8,
285
+ "admission to cultural events": 8,
286
+ "admission to sporting events": 8,
287
+ "admission to entertainment events": 8,
288
+ "foodstuffs": 5
289
+ }
290
+ },
291
+ "PT": {
292
+ "country_name": "Portugal",
293
+ "standard_rate": 23,
294
+ "reduced_rates": {
295
+ "foodstuffs": 6,
296
+ "agricultural supplies": 13,
297
+ "books": 6,
298
+ "pharmaceuticals": 6,
299
+ "medical": 6,
300
+ "newspapers": 6,
301
+ "hotels": 6,
302
+ "passenger transport": 6
303
+ }
304
+ },
305
+ "RO": {
306
+ "country_name": "Romania",
307
+ "standard_rate": 19,
308
+ "reduced_rates": {
309
+ "books": 9,
310
+ "pharmaceuticals": 9,
311
+ "medical": 9,
312
+ "newspapers": 9,
313
+ "admission to cultural events": 9,
314
+ "admission to entertainment events": 9,
315
+ "hotels": 9,
316
+ "foodstuffs": 9,
317
+ "social housing": 5
318
+ }
319
+ },
320
+ "SK": {
321
+ "country_name": "Slovakia",
322
+ "standard_rate": 20,
323
+ "reduced_rates": {
324
+ "books": 10,
325
+ "foodstuffs": 10,
326
+ "medical": 10,
327
+ "pharmaceuticals": 10,
328
+ "admission to cultural events": 10,
329
+ "admission to entertainment events": 10
330
+ }
331
+ },
332
+ "SI": {
333
+ "country_name": "Slovenia",
334
+ "standard_rate": 22,
335
+ "reduced_rates": {
336
+ "foodstuffs": 9.5,
337
+ "books": 9.5,
338
+ "pharmaceuticals": 9.5,
339
+ "medical": 9.5,
340
+ "newspapers": 9.5,
341
+ "admission to sporting events": 9.5,
342
+ "admission to cultural events": 9.5,
343
+ "admission to entertainment events": 9.5,
344
+ "hotels": 9.5
345
+ }
346
+ },
347
+ "ES": {
348
+ "country_name": "Spain",
349
+ "standard_rate": 21,
350
+ "reduced_rates": {
351
+ "medical": 10,
352
+ "pharmaceuticals": 10,
353
+ "passenger transport": 10,
354
+ "admission to cultural events": 10,
355
+ "admission to sporting events": 10,
356
+ "admission to entertainment events": 10,
357
+ "foodstuffs": 4,
358
+ "newspapers": 4
359
+ }
360
+ },
361
+ "SE": {
362
+ "country_name": "Sweden",
363
+ "standard_rate": 25,
364
+ "reduced_rates": {
365
+ "foodstuffs": 12,
366
+ "books": 6
367
+ }
368
+ },
369
+ "GB": {
370
+ "country_name": "United Kingdom",
371
+ "standard_rate": 20,
372
+ "reduced_rates": {
373
+ "property renovations": 5,
374
+ "foodstuffs": 0,
375
+ "books": 0,
376
+ "pharmaceuticals": 0,
377
+ "medical": 0,
378
+ "passenger transport": 0,
379
+ "newspapers": 0,
380
+ "childrens clothing": 0
381
+ }
382
+ }
383
+ }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'moss_generator/version'
4
+ require_relative 'moss_generator/vat_rate'
5
+ require_relative 'moss_generator/stripe'
6
+
7
+ # Generate CSV-formatted string for MOSS report
8
+ module MossGenerator
9
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'moss_generator/stripe_charge_row'
5
+
6
+ module MossGenerator
7
+ # Generate MOSS CSV string from Stripe charges
8
+ class Stripe
9
+ class NoTurnoverCountryError < StandardError; end
10
+
11
+ attr_reader :charges, :vat_number, :period, :year
12
+
13
+ def initialize(charges, vat_number, period, year)
14
+ @charges = charges
15
+ @vat_number = vat_number
16
+ @period = period
17
+ @year = year
18
+ end
19
+
20
+ def self.call(charges, vat_number, period, year)
21
+ new(charges, vat_number, period, year).call
22
+ end
23
+
24
+ def call
25
+ CSV.generate(csv_options) do |csv|
26
+ csv << first_row
27
+ csv << second_row
28
+ generate_charges_rows(csv)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def first_row
35
+ ['MOSS_001']
36
+ end
37
+
38
+ def second_row
39
+ [vat_number, period, year]
40
+ end
41
+
42
+ def generate_charges_rows(csv)
43
+ build_charges_rows.each { |row| csv << row }
44
+ end
45
+
46
+ def group_charges_rows
47
+ charges_rows = charges.map do |charge|
48
+ MossGenerator::StripeChargeRow.new(charge)
49
+ end
50
+ charges_rows.group_by(&:country_code)
51
+ end
52
+
53
+ def build_charges_rows
54
+ group_charges_rows.map do |country, charges|
55
+ next if country == 'SE' && turnover_country == 'SE'
56
+ next if charges.first.vat_rate.nil?
57
+
58
+ country_row(country, charges)
59
+ end.compact
60
+ end
61
+
62
+ def country_row(country, charges)
63
+ [turnover_country,
64
+ country,
65
+ format_to_two_decimals(charges.first.vat_rate),
66
+ format_to_two_decimals(charges.sum(&:amount)),
67
+ format_to_two_decimals(charges.sum(&:vat_amount))]
68
+ end
69
+
70
+ def csv_options
71
+ { col_sep: column_separator, row_sep: row_separator }
72
+ end
73
+
74
+ def row_separator
75
+ ";\r\n"
76
+ end
77
+
78
+ def column_separator
79
+ ';'
80
+ end
81
+
82
+ def format_to_two_decimals(number)
83
+ format('%.2f', number).sub('.', ',')
84
+ end
85
+
86
+ # vat_numbers first two characters shall contain the country code
87
+ def turnover_country
88
+ country_code = vat_number[0...2]
89
+ if country_code.nil?
90
+ raise NoTurnoverCountryError,
91
+ "vat_number: #{vat_number}"
92
+ end
93
+ return country_code if country_code == 'SE'
94
+
95
+ vat_number
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'moss_generator/vat_rate'
4
+
5
+ module MossGenerator
6
+ # Parse charge data from single Stripe charge
7
+ class StripeChargeRow
8
+ class NoConsumptionCountryError < StandardError; end
9
+
10
+ class NoVatRateForCountryError < StandardError; end
11
+
12
+ attr_reader :charge
13
+
14
+ def initialize(charge)
15
+ @charge = charge
16
+ end
17
+
18
+ def country_code
19
+ return fetch_country_code unless fetch_country_code.nil?
20
+
21
+ raise NoConsumptionCountryError, "charge: #{charge}"
22
+ end
23
+
24
+ def amount
25
+ charge.dig('balance_transaction', 'amount')
26
+ end
27
+
28
+ def vat_rate
29
+ @vat_rate = MossGenerator::VatRate.for(country_code)
30
+ end
31
+
32
+ def vat_amount
33
+ amount * vat_rate_calculatable_percent
34
+ end
35
+
36
+ private
37
+
38
+ def vat_rate_calculatable_percent
39
+ (vat_rate.to_f / 100)
40
+ end
41
+
42
+ def fetch_country_code
43
+ charge.dig('payment_method_details', 'card', 'country') ||
44
+ charge.dig('billing_details', 'address', 'country')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module MossGenerator
6
+ # Returns standard vat rate from vat_rates.json
7
+ class VatRate
8
+ class << self
9
+ def for(country_code)
10
+ vat_rates.dig(country_code, 'standard_rate')
11
+ end
12
+
13
+ private
14
+
15
+ def vat_rates
16
+ JSON.parse(File.read(vat_rates_path))
17
+ end
18
+
19
+ def vat_rates_path
20
+ File.join(File.dirname(File.expand_path(__FILE__)),
21
+ 'config/vat_rates.json')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MossGenerator
4
+ VERSION = '0.1.1'
5
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/moss_generator/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'moss_generator'
7
+ spec.version = MossGenerator::VERSION
8
+ spec.authors = ['frdrkolsson']
9
+ spec.email = ['fredrik.olsson@standout.se']
10
+
11
+ spec.summary = 'Skatteverket Mini One Stop Shop(MOSS) generator.'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/standout/moss_generator'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+ spec.metadata['changelog_uri'] = spec.homepage
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added
23
+ # into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{\A(?:test|spec|features)/})
27
+ end
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ # Uncomment to register a new dependency of your gem
34
+ # spec.add_dependency "example-gem", "~> 1.0"
35
+
36
+ # For more information and examples about making a new gem, checkout our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: moss_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - frdrkolsson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-01-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Skatteverket Mini One Stop Shop(MOSS) generator.
14
+ email:
15
+ - fredrik.olsson@standout.se
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/gempush.yml"
21
+ - ".github/workflows/main.yml"
22
+ - ".gitignore"
23
+ - ".rspec"
24
+ - ".rubocop.yml"
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - LICENSE.txt
28
+ - README.md
29
+ - Rakefile
30
+ - bin/console
31
+ - bin/setup
32
+ - config/vat_rates.json
33
+ - lib/moss_generator.rb
34
+ - lib/moss_generator/stripe.rb
35
+ - lib/moss_generator/stripe_charge_row.rb
36
+ - lib/moss_generator/vat_rate.rb
37
+ - lib/moss_generator/version.rb
38
+ - moss_generator.gemspec
39
+ homepage: https://github.com/standout/moss_generator
40
+ licenses:
41
+ - MIT
42
+ metadata:
43
+ homepage_uri: https://github.com/standout/moss_generator
44
+ source_code_uri: https://github.com/standout/moss_generator
45
+ changelog_uri: https://github.com/standout/moss_generator
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.5.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.1.4
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Skatteverket Mini One Stop Shop(MOSS) generator.
65
+ test_files: []