Module: IpsQrCode

Defined in:
lib/ips_qr_code.rb,
lib/ips_qr_code/utils.rb,
lib/ips_qr_code/version.rb

Defined Under Namespace

Modules: Utils

Constant Summary collapse

ORDER =
i[k v c r n i sf s m js ro rl rp].freeze
VERSION =
'0.1.0'

Class Method Summary collapse

Class Method Details

.generate(opts = {}) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ips_qr_code.rb', line 7

def self.generate(opts = {})
  opts = Utils.rename_keys(opts)
  opts = { k: 'PR', v: '01', c: '1' }.merge(opts)

  # Validate required fields
  i[r n i sf].each do |field|
    raise ArgumentError, "Missing required #{field}" unless opts[field]
  end

  # Normalize and validate racun primaoca (r)
  opts[:r] = Utils.(opts[:r])
  unless opts[:r] =~ /^\d{18}$/ && Utils.(opts[:r])
    raise ArgumentError, 'Invalid racunPrimaoca'
  end

  # Optional racun platioca (o)
  if opts[:o]
    opts[:o] = Utils.(opts[:o])
    unless opts[:o] =~ /^\d{18}$/ && Utils.(opts[:o])
      raise ArgumentError, 'Invalid racunPlatioca'
    end
  end

  # Validate naziv primaoca (n)
  n = opts[:n].to_s
  if n.length < 1 || n.length > 70
    raise ArgumentError, 'nazivPrimaoca must be between 1 and 70 characters'
  end
  unless n.match?(/^[a-zA-ZšđžčćŠĐŽČĆ0-9 \(\)\{\}\[\]<>\/\.,:;!@#\$%\^&\?„""""'`''_~=+\-\s]+$/u)
    raise ArgumentError, 'Only letters, numbers, and select special characers are allowed.'
  end
  if n.lines.count > 3
    raise ArgumentError, 'nazivPrimaoca must not contain more than 3 lines'
  end

  # Optional naziv platioca (p)
  if opts[:p]
    p = opts[:p].to_s
    if p.length > 70
      raise ArgumentError, 'nazivPlatioca must be at most 70 characters'
    end
  end

  # Validate iznos (i)
  i_val = opts[:i].to_s
  unless i_val.length.between?(5, 20) && i_val.match?(/^[A-Z]{3}[0-9]+,[0-9]{0,2}$/)
    raise ArgumentError, 'Invalid iznos format'
  end

  # Validate sifra placanja (sf)
  sf = opts[:sf].to_s
  unless sf.match?(/^[12][0-9]{2}$/)
    raise ArgumentError, 'Invalid sifraPlacanja'
  end

  # Optional svrha placanja (s)
  if opts[:s]
    s = opts[:s].to_s
    if s.length > 35
      raise ArgumentError, 'svrhaPlacanja must be at most 35 characters'
    end
  end

  # Optional merchant code category (m)
  if opts[:m]
    m = opts[:m].to_s
    unless m.match?(/^[0-9]{4}$/)
      raise ArgumentError, 'Invalid merchantCodeCategory'
    end
  end

  # Optional jednokratna sifra (js)
  if opts[:js]
    js = opts[:js].to_s
    unless js.match?(/^[0-9]{5}$/)
      raise ArgumentError, 'Invalid jednokratnaSifra'
    end
  end

  # Optional poziv na broj (ro)
  if opts[:ro]
    ro = opts[:ro].to_s
    if ro.length > 35
      raise ArgumentError, 'pozivNaBroj must be at most 35 characters'
    end
    ro = "00#{ro}" unless %w[97 22 11 00].include?(ro[0,2])
    unless Utils.validate_reference_number(ro)
      raise ArgumentError, 'Invalid pozivNaBroj'
    end
    opts[:ro] = ro
  end

  # Optional referenca primaoca (rl)
  if opts[:rl]
    rl = opts[:rl].to_s
    if rl.length > 140
      raise ArgumentError, 'referencaPrimaoca must be at most 140 characters'
    end
  end

  # Optional referenca placanja (rp)
  if opts[:rp]
    rp = opts[:rp].to_s
    unless rp.match?(/^[0-9]{19}$/)
      raise ArgumentError, 'Invalid referencaPlacanja'
    end
  end

  # Build IPS data string
  parts = ORDER.map do |key|
    val = opts[key]
    next unless val
    "#{key.to_s.upcase}:#{val.to_s.gsub('|', '-')}"
  end.compact

  parts.join('|')
end