organisationsnummer 1.0.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 +7 -0
- data/.github/renovate.json +6 -0
- data/.github/workflows/publish.yml +52 -0
- data/.github/workflows/ruby.yml +43 -0
- data/.meta.yaml +4 -0
- data/LICENSE +20 -0
- data/README.md +64 -0
- data/Rakefile +8 -0
- data/lib/organisationsnummer.rb +120 -0
- data/organisationsnummer.gemspec +23 -0
- data/test/test_organisationsnummer.rb +211 -0
- metadata +98 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e57509e3ea4134989ddca7f80f3cc6411e4646ba6b90f3251fad07d752e45a0e
|
|
4
|
+
data.tar.gz: 76f1c3d4cf60148f4f4c268f3c5c812598193443a2a90493ae6b031795a21170
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 49521a9ad9366fda9742b8073ca34e83a8c8993fb73aee8b5649d7a706ff6b33fd292551c9b96935a47dd03d12a4c3e6b74ffac852b12fbb9484269af50c381a
|
|
7
|
+
data.tar.gz: 783b9e0690bccd99b1ce49754e8beaf55b091743b01344bd5bab9d0a1058894961854cbb5326bed1444e50b6c46a59f987199b6d9189d7e5d4f321b3c96c6985
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
name: publish
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
# Publishing a GitHub release creates the tag and pushes the gem.
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
# A manual run tests, builds and signs in to RubyGems, but does not push.
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
rubygems:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
# Trusted publishing: RubyGems swaps the OIDC token for a short-lived API key.
|
|
22
|
+
id-token: write
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
- name: Set up Ruby
|
|
27
|
+
uses: ruby/setup-ruby@b3aaceaef7e0ee231b499e7b5f49c03fc1d32199 # v1.338.0
|
|
28
|
+
with:
|
|
29
|
+
ruby-version: '4.0'
|
|
30
|
+
- name: gem install
|
|
31
|
+
run: |
|
|
32
|
+
gem install personnummer --version '~> 3.1, >= 3.1.1'
|
|
33
|
+
gem install minitest
|
|
34
|
+
- name: test
|
|
35
|
+
run: rake
|
|
36
|
+
# A published version cannot be replaced, so stop before the push if the
|
|
37
|
+
# release was tagged without the version bump.
|
|
38
|
+
- name: Check that the tag matches the gemspec version
|
|
39
|
+
if: github.event_name == 'release'
|
|
40
|
+
run: |
|
|
41
|
+
version=$(ruby -e 'puts Gem::Specification.load("organisationsnummer.gemspec").version')
|
|
42
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
43
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match gemspec version $version"
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
- name: build
|
|
47
|
+
run: gem build organisationsnummer.gemspec
|
|
48
|
+
- name: Sign in to RubyGems
|
|
49
|
+
uses: rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a # v2.1.0
|
|
50
|
+
- name: push
|
|
51
|
+
if: github.event_name == 'release'
|
|
52
|
+
run: gem push organisationsnummer-*.gem
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
name: build
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
# Run tests for any PRs.
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
# Quoted, since YAML would read an unquoted 4.0 as the number 4.
|
|
19
|
+
ruby-version: ['3.3', '3.4', '4.0']
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
24
|
+
uses: ruby/setup-ruby@b3aaceaef7e0ee231b499e7b5f49c03fc1d32199 # v1.338.0
|
|
25
|
+
with:
|
|
26
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
27
|
+
- name: gem install
|
|
28
|
+
run: |
|
|
29
|
+
gem install personnummer --version '~> 3.1, >= 3.1.1'
|
|
30
|
+
gem install minitest
|
|
31
|
+
- name: test
|
|
32
|
+
run: rake
|
|
33
|
+
|
|
34
|
+
# Single stable check for branch protection, so the ruleset does not need
|
|
35
|
+
# to change when the matrix does.
|
|
36
|
+
tests-passed:
|
|
37
|
+
if: always()
|
|
38
|
+
needs: [build]
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- name: Fail if any test job failed or was cancelled
|
|
42
|
+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
|
43
|
+
run: exit 1
|
data/.meta.yaml
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Organisationsnummer and Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
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, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# organisationsnummer [](https://github.com/organisationsnummer/ruby/actions)
|
|
2
|
+
|
|
3
|
+
Validate Swedish organization numbers. Follows version 1.1 of the [specification](https://github.com/organisationsnummer/meta#package-specification-v11).
|
|
4
|
+
|
|
5
|
+
A personal identity number is also a valid organization number. The gem uses the [personnummer](https://github.com/personnummer/ruby) gem for those.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this to your `Gemfile`
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
gem 'organisationsnummer'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then run `bundle install`
|
|
16
|
+
|
|
17
|
+
## Example
|
|
18
|
+
|
|
19
|
+
A company number:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require 'organisationsnummer'
|
|
23
|
+
|
|
24
|
+
Organisationsnummer.valid?("556016-0680")
|
|
25
|
+
# => true
|
|
26
|
+
|
|
27
|
+
org = Organisationsnummer.parse("5560160680")
|
|
28
|
+
org.format # => "556016-0680"
|
|
29
|
+
org.format(false) # => "5560160680"
|
|
30
|
+
org.type # => "Aktiebolag"
|
|
31
|
+
org.vat_number # => "SE556016068001"
|
|
32
|
+
org.is_personnummer? # => false
|
|
33
|
+
org.personnummer # => nil
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A personal identity number:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
org = Organisationsnummer.parse("900101-0017")
|
|
40
|
+
org.type # => "Enskild firma"
|
|
41
|
+
org.vat_number # => "SE900101001701"
|
|
42
|
+
org.is_personnummer? # => true
|
|
43
|
+
org.personnummer # => a Personnummer::Personnummer
|
|
44
|
+
org.personnummer.get_date.to_s # => "1990-01-01"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`parse` raises `Organisationsnummer::Error` when the input is not a valid number. The input can be a string or an integer, other input is invalid.
|
|
48
|
+
|
|
49
|
+
### Rules
|
|
50
|
+
|
|
51
|
+
- The input is ten digits with an optional `-` or `+` before the last four. A leading `16` is allowed and removed.
|
|
52
|
+
- The first two digits are 10 or higher and the third and fourth digit are 20 or higher.
|
|
53
|
+
- The last digit is a Luhn check digit.
|
|
54
|
+
- Anything else is parsed as a personal identity number, with the default options of the `personnummer` gem.
|
|
55
|
+
|
|
56
|
+
See [test/test_organisationsnummer.rb](test/test_organisationsnummer.rb) for more examples.
|
|
57
|
+
|
|
58
|
+
## In memoriam
|
|
59
|
+
|
|
60
|
+
Fredrik "Frozzare" Forsmo (1991-2026) was the initiator, co-founder and a core contributor of the organisationsnummer project. This library carries his work. He is missed.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'personnummer'
|
|
3
|
+
|
|
4
|
+
module Organisationsnummer
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
class Organisationsnummer
|
|
8
|
+
# ASCII digits only, and \A and \z since ^ and $ match at line boundaries in Ruby.
|
|
9
|
+
# The input is matched here before anything is handed to Personnummer.
|
|
10
|
+
PATTERN = /\A([0-9]{2})?([0-9]{2})([0-9]{2})([0-9]{2})[-+]?([0-9]{4})\z/
|
|
11
|
+
|
|
12
|
+
TYPES = {
|
|
13
|
+
'1' => 'Dödsbon',
|
|
14
|
+
'2' => 'Stat, landsting, kommun eller församling',
|
|
15
|
+
'3' => 'Utländska företag som bedriver näringsverksamhet eller äger fastigheter i Sverige',
|
|
16
|
+
'5' => 'Aktiebolag',
|
|
17
|
+
'6' => 'Enkelt bolag',
|
|
18
|
+
'7' => 'Ekonomisk förening eller bostadsrättsförening',
|
|
19
|
+
'8' => 'Ideella förening och stiftelse',
|
|
20
|
+
'9' => 'Handelsbolag, kommanditbolag och enkelt bolag'
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def initialize(organisationsnummer)
|
|
24
|
+
input = organisationsnummer.to_s
|
|
25
|
+
match = input.valid_encoding? ? PATTERN.match(input) : nil
|
|
26
|
+
|
|
27
|
+
if !match
|
|
28
|
+
raise Error, "Invalid Organisationsnummer"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
prefix = match[1]
|
|
32
|
+
first = match[2]
|
|
33
|
+
second = match[3]
|
|
34
|
+
@number = first + second + match[4] + match[5]
|
|
35
|
+
@personnummer = nil
|
|
36
|
+
|
|
37
|
+
# May only be prefixed with 16, may not start with a leading zero and
|
|
38
|
+
# the third and fourth digit must be 20 or more.
|
|
39
|
+
if (prefix.nil? || prefix == '16') && first.to_i >= 10 && second.to_i >= 20 && luhn?(@number)
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A personal identity number is also a valid organization number.
|
|
44
|
+
begin
|
|
45
|
+
@personnummer = ::Personnummer.parse(input)
|
|
46
|
+
rescue ::Personnummer::Error
|
|
47
|
+
raise Error, "Invalid Organisationsnummer"
|
|
48
|
+
end
|
|
49
|
+
pnr = @personnummer
|
|
50
|
+
@number = pnr.year + pnr.month + pnr.day + pnr.num + pnr.check
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the formatted number, NNNNNN-NNNN, or NNNNNNNNNN when separator is false.
|
|
54
|
+
# A personal identity number of someone 100 years or older gets + as separator.
|
|
55
|
+
# (String)
|
|
56
|
+
def format(separator=true)
|
|
57
|
+
return @number unless separator
|
|
58
|
+
return @personnummer.format if @personnummer
|
|
59
|
+
|
|
60
|
+
"#{@number[0, 6]}-#{@number[6, 4]}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Checks if the Organisationsnummer is a personal identity number (Enskild firma)
|
|
64
|
+
# (TrueClass/FalseClass)
|
|
65
|
+
def is_personnummer?
|
|
66
|
+
!@personnummer.nil?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns the Personnummer object, or nil if the number belongs to a legal entity
|
|
70
|
+
# (Personnummer::Personnummer, NilClass)
|
|
71
|
+
def personnummer
|
|
72
|
+
@personnummer
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the type of organization, in Swedish
|
|
76
|
+
# (String)
|
|
77
|
+
def type
|
|
78
|
+
return 'Enskild firma' if is_personnummer?
|
|
79
|
+
|
|
80
|
+
TYPES.fetch(@number[0], 'Okänt')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the VAT number
|
|
84
|
+
# (String)
|
|
85
|
+
def vat_number
|
|
86
|
+
"SE#{@number}01"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Checks that the last digit is a valid Luhn check digit
|
|
92
|
+
# (TrueClass/FalseClass)
|
|
93
|
+
def luhn?(digits)
|
|
94
|
+
sum = 0
|
|
95
|
+
|
|
96
|
+
digits.each_char.with_index do |c, i|
|
|
97
|
+
v = c.to_i * (2 - (i % 2))
|
|
98
|
+
v -= 9 if v > 9
|
|
99
|
+
sum += v
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
sum % 10 == 0
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Return Organisationsnummer object from given string/integer
|
|
107
|
+
# (Organisationsnummer)
|
|
108
|
+
def self.parse(organisationsnummer)
|
|
109
|
+
Organisationsnummer.new(organisationsnummer)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Check validity of string/integer input as Organisationsnummer
|
|
113
|
+
# (TrueClass/FalseClass)
|
|
114
|
+
def self.valid?(organisationsnummer)
|
|
115
|
+
parse(organisationsnummer)
|
|
116
|
+
true
|
|
117
|
+
rescue Error
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = 'organisationsnummer'
|
|
6
|
+
s.version = '1.0.0'
|
|
7
|
+
s.date = '2026-11-13'
|
|
8
|
+
s.summary = 'Validate Swedish organization numbers'
|
|
9
|
+
s.description = 'Validate Swedish organization numbers'
|
|
10
|
+
s.authors = ['Rasmus Graham']
|
|
11
|
+
s.files = `git ls-files`.split("\n")
|
|
12
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
13
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
14
|
+
s.require_paths = ["lib"]
|
|
15
|
+
s.homepage = 'https://github.com/organisationsnummer/ruby'
|
|
16
|
+
s.license = 'MIT'
|
|
17
|
+
s.metadata = { 'source_code_uri' => 'https://github.com/organisationsnummer/ruby' }
|
|
18
|
+
# The oldest Ruby in the CI matrix.
|
|
19
|
+
s.required_ruby_version = '>= 3.3'
|
|
20
|
+
s.add_runtime_dependency 'personnummer', '~> 3.1', '>= 3.1.1'
|
|
21
|
+
s.add_development_dependency 'rake'
|
|
22
|
+
s.add_development_dependency 'minitest'
|
|
23
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'organisationsnummer'
|
|
5
|
+
|
|
6
|
+
# Shared test data, also used by the other organisationsnummer packages.
|
|
7
|
+
# `long_format` has the separator (NNNNNN-NNNN), `short_format` does not.
|
|
8
|
+
TESTDATA_URL = 'https://raw.githubusercontent.com/organisationsnummer/meta/HEAD/testdata/list.json'.freeze
|
|
9
|
+
|
|
10
|
+
def load_testdata
|
|
11
|
+
response = Net::HTTP.get_response(URI(TESTDATA_URL))
|
|
12
|
+
raise "could not load testdata: HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
|
13
|
+
|
|
14
|
+
JSON.parse(response.body)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
TESTDATA = load_testdata.freeze
|
|
18
|
+
|
|
19
|
+
# Appends the Luhn check digit to nine digits.
|
|
20
|
+
def with_check_digit(digits)
|
|
21
|
+
sum = digits.each_char.with_index.sum do |c, i|
|
|
22
|
+
v = c.to_i * (2 - i % 2)
|
|
23
|
+
v > 9 ? v - 9 : v
|
|
24
|
+
end
|
|
25
|
+
digits + ((10 - sum % 10) % 10).to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class OrganisationsnummerTest < Minitest::Test
|
|
29
|
+
def inputs(item)
|
|
30
|
+
[item['input'], item['long_format'], item['short_format']]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_testdata_loaded
|
|
34
|
+
refute_empty TESTDATA
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_valid
|
|
38
|
+
TESTDATA.each do |item|
|
|
39
|
+
inputs(item).each do |input|
|
|
40
|
+
assert_equal item['valid'], Organisationsnummer.valid?(input), input
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_parse_raises
|
|
46
|
+
TESTDATA.reject { |item| item['valid'] }.each do |item|
|
|
47
|
+
inputs(item).each do |input|
|
|
48
|
+
assert_raises(Organisationsnummer::Error, input) { Organisationsnummer.parse(input) }
|
|
49
|
+
assert_raises(Organisationsnummer::Error, input) { Organisationsnummer::Organisationsnummer.new(input) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_format
|
|
55
|
+
TESTDATA.select { |item| item['valid'] }.each do |item|
|
|
56
|
+
# `short_format` is left out as input, a personnummer without
|
|
57
|
+
# separator has an ambiguous century.
|
|
58
|
+
[item['input'], item['long_format']].each do |input|
|
|
59
|
+
org = Organisationsnummer.parse(input)
|
|
60
|
+
assert_equal item['long_format'], org.format, input
|
|
61
|
+
assert_equal item['long_format'], org.format(true), input
|
|
62
|
+
assert_equal item['short_format'], org.format(false), input
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_format_old_personnummer
|
|
68
|
+
assert_equal '121212+1212', Organisationsnummer.parse('191212121212').format
|
|
69
|
+
assert_equal '1212121212', Organisationsnummer.parse('191212121212').format(false)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_type
|
|
73
|
+
TESTDATA.select { |item| item['valid'] }.each do |item|
|
|
74
|
+
assert_equal item['type'], Organisationsnummer.parse(item['input']).type, item['input']
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_type_by_first_digit
|
|
79
|
+
types = {
|
|
80
|
+
'1' => 'Dödsbon',
|
|
81
|
+
'2' => 'Stat, landsting, kommun eller församling',
|
|
82
|
+
'3' => 'Utländska företag som bedriver näringsverksamhet eller äger fastigheter i Sverige',
|
|
83
|
+
'4' => 'Okänt',
|
|
84
|
+
'5' => 'Aktiebolag',
|
|
85
|
+
'6' => 'Enkelt bolag',
|
|
86
|
+
'7' => 'Ekonomisk förening eller bostadsrättsförening',
|
|
87
|
+
'8' => 'Ideella förening och stiftelse',
|
|
88
|
+
'9' => 'Handelsbolag, kommanditbolag och enkelt bolag'
|
|
89
|
+
}
|
|
90
|
+
types.each do |digit, type|
|
|
91
|
+
number = with_check_digit(digit + '02100548')
|
|
92
|
+
org = Organisationsnummer.parse(number)
|
|
93
|
+
assert_equal type, org.type, number
|
|
94
|
+
assert_equal false, org.is_personnummer?, number
|
|
95
|
+
assert_nil org.personnummer, number
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_vat_number
|
|
100
|
+
TESTDATA.select { |item| item['valid'] }.each do |item|
|
|
101
|
+
assert_equal item['vat_number'], Organisationsnummer.parse(item['input']).vat_number, item['input']
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_personnummer
|
|
106
|
+
TESTDATA.select { |item| item['valid'] }.each do |item|
|
|
107
|
+
org = Organisationsnummer.parse(item['input'])
|
|
108
|
+
is_personnummer = item['type'] == 'Enskild firma'
|
|
109
|
+
assert_equal is_personnummer, org.is_personnummer?, item['input']
|
|
110
|
+
assert_equal is_personnummer, !org.personnummer.nil?, item['input']
|
|
111
|
+
if is_personnummer
|
|
112
|
+
assert_instance_of ::Personnummer::Personnummer, org.personnummer
|
|
113
|
+
assert_equal item['long_format'], org.personnummer.format, item['input']
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_century_prefix
|
|
119
|
+
assert_equal true, Organisationsnummer.valid?('165560160680')
|
|
120
|
+
assert_equal true, Organisationsnummer.valid?('16556016-0680')
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_prefix_other_than_16_is_invalid
|
|
124
|
+
%w[15 17 18 19 20 00].each do |prefix|
|
|
125
|
+
assert_equal false, Organisationsnummer.valid?(prefix + '5560160680'), prefix
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_invalid_input
|
|
130
|
+
[
|
|
131
|
+
'',
|
|
132
|
+
'-',
|
|
133
|
+
'556016068',
|
|
134
|
+
'55601606800',
|
|
135
|
+
'556016--0680',
|
|
136
|
+
'556016 0680',
|
|
137
|
+
' 5560160680',
|
|
138
|
+
'5560160680 ',
|
|
139
|
+
'556016-068A',
|
|
140
|
+
'xx5560160680yy',
|
|
141
|
+
'0560160680',
|
|
142
|
+
'0000000000',
|
|
143
|
+
'1234567890'
|
|
144
|
+
].each do |input|
|
|
145
|
+
assert_equal false, Organisationsnummer.valid?(input), input
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_anchoring
|
|
150
|
+
assert_equal false, Organisationsnummer.valid?("5560160680\n")
|
|
151
|
+
assert_equal false, Organisationsnummer.valid?("556016-0680\n")
|
|
152
|
+
assert_equal false, Organisationsnummer.valid?("\n5560160680")
|
|
153
|
+
assert_equal false, Organisationsnummer.valid?("foo\n5560160680")
|
|
154
|
+
assert_equal false, Organisationsnummer.valid?("5560160680\nfoo")
|
|
155
|
+
assert_equal false, Organisationsnummer.valid?("5560160680\n5560160680")
|
|
156
|
+
# The personnummer fallback must not see multi-line input either.
|
|
157
|
+
assert_equal false, Organisationsnummer.valid?("foo\n9001010017")
|
|
158
|
+
assert_equal false, Organisationsnummer.valid?("9001010017\n")
|
|
159
|
+
assert_equal false, Organisationsnummer.valid?("9001010017\nfoo")
|
|
160
|
+
assert_equal true, Organisationsnummer.valid?('9001010017')
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def test_non_ascii_digits
|
|
164
|
+
# Arabic-Indic digits.
|
|
165
|
+
assert_equal false, Organisationsnummer.valid?('٥٥٦٠١٦٠٦٨٠')
|
|
166
|
+
assert_equal false, Organisationsnummer.valid?('١٢١٢١٢١٢١٢')
|
|
167
|
+
# Fullwidth digits.
|
|
168
|
+
assert_equal false, Organisationsnummer.valid?('5560160680')
|
|
169
|
+
assert_equal false, Organisationsnummer.valid?('556016-0680')
|
|
170
|
+
assert_equal false, Organisationsnummer.valid?('9001010017')
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_invalid_encoding
|
|
174
|
+
assert_equal false, Organisationsnummer.valid?("5560160680\xff")
|
|
175
|
+
assert_raises(Organisationsnummer::Error) { Organisationsnummer.parse("\xff") }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def test_wrong_types
|
|
179
|
+
# Same as the personnummer gem, anything is converted with `to_s`.
|
|
180
|
+
assert_equal true, Organisationsnummer.valid?(5560160680)
|
|
181
|
+
assert_equal true, Organisationsnummer.valid?(9001010017)
|
|
182
|
+
assert_equal false, Organisationsnummer.valid?(nil)
|
|
183
|
+
assert_equal false, Organisationsnummer.valid?([])
|
|
184
|
+
assert_equal false, Organisationsnummer.valid?({})
|
|
185
|
+
assert_equal false, Organisationsnummer.valid?(false)
|
|
186
|
+
assert_equal false, Organisationsnummer.valid?(true)
|
|
187
|
+
assert_equal false, Organisationsnummer.valid?(0)
|
|
188
|
+
assert_equal false, Organisationsnummer.valid?(1234567890)
|
|
189
|
+
assert_equal '556016-0680', Organisationsnummer.parse(5560160680).format
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_personnummer_fallback
|
|
193
|
+
org = Organisationsnummer.parse('900101-0017')
|
|
194
|
+
assert_equal true, org.is_personnummer?
|
|
195
|
+
assert_equal 'Enskild firma', org.type
|
|
196
|
+
assert_equal '900101-0017', org.format
|
|
197
|
+
assert_equal '9001010017', org.format(false)
|
|
198
|
+
assert_equal 'SE900101001701', org.vat_number
|
|
199
|
+
# Coordination numbers are accepted, interim numbers are not.
|
|
200
|
+
assert_equal true, Organisationsnummer.valid?('850769-9810')
|
|
201
|
+
assert_equal false, Organisationsnummer.valid?('000101-T220')
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def test_error_class
|
|
205
|
+
assert_operator Organisationsnummer::Error, :<, StandardError
|
|
206
|
+
assert_equal 'Organisationsnummer::Error', Organisationsnummer::Error.name
|
|
207
|
+
error = assert_raises(Organisationsnummer::Error) { Organisationsnummer.parse('Just a string') }
|
|
208
|
+
assert_equal 'Invalid Organisationsnummer', error.message
|
|
209
|
+
assert_raises(Organisationsnummer::Error) { Organisationsnummer.parse('1234567890') }
|
|
210
|
+
end
|
|
211
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: organisationsnummer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rasmus Graham
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-11-13 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: personnummer
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.1'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 3.1.1
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '3.1'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 3.1.1
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: rake
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
type: :development
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: minitest
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
type: :development
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
description: Validate Swedish organization numbers
|
|
61
|
+
executables: []
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- ".github/renovate.json"
|
|
66
|
+
- ".github/workflows/publish.yml"
|
|
67
|
+
- ".github/workflows/ruby.yml"
|
|
68
|
+
- ".meta.yaml"
|
|
69
|
+
- LICENSE
|
|
70
|
+
- README.md
|
|
71
|
+
- Rakefile
|
|
72
|
+
- lib/organisationsnummer.rb
|
|
73
|
+
- organisationsnummer.gemspec
|
|
74
|
+
- test/test_organisationsnummer.rb
|
|
75
|
+
homepage: https://github.com/organisationsnummer/ruby
|
|
76
|
+
licenses:
|
|
77
|
+
- MIT
|
|
78
|
+
metadata:
|
|
79
|
+
source_code_uri: https://github.com/organisationsnummer/ruby
|
|
80
|
+
rdoc_options: []
|
|
81
|
+
require_paths:
|
|
82
|
+
- lib
|
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '3.3'
|
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '0'
|
|
93
|
+
requirements: []
|
|
94
|
+
rubygems_version: 3.6.2
|
|
95
|
+
specification_version: 4
|
|
96
|
+
summary: Validate Swedish organization numbers
|
|
97
|
+
test_files:
|
|
98
|
+
- test/test_organisationsnummer.rb
|