Class: Ksef::KsefNumber
- Inherits:
-
Data
- Object
- Data
- Ksef::KsefNumber
- Defined in:
- lib/ksef/ksef_number.rb,
lib/ksef/ksef_number.rb
Overview
Reopened rather than using a Data.define block so the constants land on the class.
Constant Summary collapse
- LENGTH =
Both lengths the contract accepts. 35 is what KSeF 2.0 generates; 36 is the KSeF 1.0-era form, kept accepted for backward compatibility, with a hyphen splitting the technical part 6-6 (§13.1).
35- LEGACY_LENGTH =
36- LENGTHS =
[LENGTH, LEGACY_LENGTH].freeze
- FORMAT =
Uppercase hex only, in both the technical part and the checksum. The optional hyphen is what admits the legacy form — taken from the contract's own pattern rather than invented.
/\A(\d{10})-(\d{8})-([0-9A-F]{6})-?([0-9A-F]{6})-([0-9A-F]{2})\z/- POLYNOMIAL =
CRC-8 with polynomial
0x07, initial value0x00, no input or output reflection and no final XOR — computed over the first 32 characters, i.e. everything before the final hyphen (§13). Verified against the Ministry's own documented example, which doubles as this implementation's golden vector. 0x07- CHECKSUM_INPUT_LENGTH =
32
Instance Attribute Summary collapse
-
#assigned_on ⇒ Object
readonly
Returns the value of attribute assigned_on.
-
#checksum ⇒ Object
readonly
Returns the value of attribute checksum.
-
#nip ⇒ Object
readonly
Returns the value of attribute nip.
-
#technical ⇒ Object
readonly
Returns the value of attribute technical.
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Class Method Summary collapse
-
.checksum_for(text) ⇒ String
The checksum a given number should carry, as the two uppercase hex characters the format uses.
-
.crc8(text) ⇒ Integer
0..255.
- .parse(value) ⇒ KsefNumber
-
.valid?(value) ⇒ Boolean
True when KsefNumber.parse would succeed.
Instance Method Summary collapse
-
#checksum_verified? ⇒ Boolean
Whether the CRC-8 was actually checked.
-
#legacy? ⇒ Boolean
True for the KSeF 1.0-era 36-character form, which the API still accepts but never generates (§13.1).
-
#to_s ⇒ Object
The date KSeF accepted the invoice, which is its official receipt date — not the date it was downloaded, and not the invoice's own issue date.
-
#to_str ⇒ Object
Convenience for the common case of holding a number only to look an invoice up.
Instance Attribute Details
#assigned_on ⇒ Object (readonly)
Returns the value of attribute assigned_on
27 28 29 |
# File 'lib/ksef/ksef_number.rb', line 27 def assigned_on @assigned_on end |
#checksum ⇒ Object (readonly)
Returns the value of attribute checksum
27 28 29 |
# File 'lib/ksef/ksef_number.rb', line 27 def checksum @checksum end |
#nip ⇒ Object (readonly)
Returns the value of attribute nip
27 28 29 |
# File 'lib/ksef/ksef_number.rb', line 27 def nip @nip end |
#technical ⇒ Object (readonly)
Returns the value of attribute technical
27 28 29 |
# File 'lib/ksef/ksef_number.rb', line 27 def technical @technical end |
#value ⇒ Object (readonly)
Returns the value of attribute value
27 28 29 |
# File 'lib/ksef/ksef_number.rb', line 27 def value @value end |
Class Method Details
.checksum_for(text) ⇒ String
The checksum a given number should carry, as the two uppercase hex characters the format uses.
102 103 104 |
# File 'lib/ksef/ksef_number.rb', line 102 def checksum_for(text) format("%02X", crc8(text.to_s[0, CHECKSUM_INPUT_LENGTH])) end |
.crc8(text) ⇒ Integer
Returns 0..255.
83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ksef/ksef_number.rb', line 83 def crc8(text) text.each_byte.reduce(0) do |crc, byte| register = crc ^ byte 8.times do register = if register.nobits?(0x80) (register << 1) & 0xFF else ((register << 1) ^ POLYNOMIAL) & 0xFF end end register end end |
.parse(value) ⇒ KsefNumber
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/ksef/ksef_number.rb', line 55 def parse(value) text = value.to_s match = FORMAT.match(text) or raise ValidationError, malformed(text) nip, date, first, second, checksum = match.captures # Only the 35-character form has a known checksum rule (§13.1), so only it is # verified. Guessing the legacy rule would reject numbers the API accepts. verify_checksum!(text, checksum) if text.length == LENGTH new( value: text, nip: nip, assigned_on: parse_date(date, text), technical: "#{first}#{second}", checksum: checksum ) end |
.valid?(value) ⇒ Boolean
Returns true when parse would succeed.
74 75 76 77 78 79 |
# File 'lib/ksef/ksef_number.rb', line 74 def valid?(value) parse(value) true rescue ValidationError false end |
Instance Method Details
#checksum_verified? ⇒ Boolean
Whether the CRC-8 was actually checked. False for a legacy number: §13's "first 32 characters" rule describes the 35-character layout, and nothing upstream states the input for the longer one — so it is accepted for lookup but not vouched for.
141 |
# File 'lib/ksef/ksef_number.rb', line 141 def checksum_verified? = !legacy? |
#legacy? ⇒ Boolean
True for the KSeF 1.0-era 36-character form, which the API still accepts but never generates (§13.1).
136 |
# File 'lib/ksef/ksef_number.rb', line 136 def legacy? = value.length == LEGACY_LENGTH |
#to_s ⇒ Object
The date KSeF accepted the invoice, which is its official receipt date — not the date it was downloaded, and not the invoice's own issue date.
145 |
# File 'lib/ksef/ksef_number.rb', line 145 def to_s = value |
#to_str ⇒ Object
Convenience for the common case of holding a number only to look an invoice up.
148 |
# File 'lib/ksef/ksef_number.rb', line 148 def to_str = value |