facturacr 1.0.9 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26cfff74b0373e0c9b53ce8d9866e50d4239dadf
4
- data.tar.gz: d9308283029c373835b4564dd5394488231da253
3
+ metadata.gz: c8bf5eb68e70263abef81cfdd2749fe4b94ad7f6
4
+ data.tar.gz: 2302cca660e93c2d1d4dd2f2072e5a60fafcd129
5
5
  SHA512:
6
- metadata.gz: 9811c5926c1b1573ce23f988c05453dc464aff3ea46dcce19919de5d98af5622f4248c8d24bf525719a94bfe838f11983cf0088b3106cc412903d5418b7983af
7
- data.tar.gz: 7efb47dd598d4d8eecd56095d26b27515126264a5d4e7d9f8bf9f628c86bfd72c8c6887de83fb92aceed4b30479e9984d7ca800de15ec0207f6b3d0f15e3c5c8
6
+ metadata.gz: 6b718daa726dc9d1171d896e9028891a30500bd683fa6c03c822757fc340e168b5afbb96be365ff3e736538414a17f47d027566cad51aa9e9281c49fdce2cb79
7
+ data.tar.gz: aee6da72dac27f6ed88ae05e95b84d0dc3052b45b16099e402ca3e6a6180a2d3b4a03f4a41c220a71fa8c32d456831ed851e3d6e6ad9cb07ab605350b340a037
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "rake", "~> 10.0"
22
22
  spec.add_development_dependency "minitest", '~> 5.11'
23
23
  spec.add_development_dependency "minitest-colorize", '~> 0.0'
24
-
24
+
25
25
  spec.add_dependency 'rest-client', '~> 2.0'
26
26
  spec.add_dependency 'nokogiri', '~> 1.8'
27
27
  spec.add_dependency 'colorize', '~> 0.8'
@@ -1,9 +1,13 @@
1
1
  require 'awesome_print'
2
+
2
3
  require 'facturacr'
3
4
  require 'facturacr/configuration'
5
+ require 'facturacr/element'
4
6
  require 'facturacr/document'
5
7
  require 'facturacr/invoice'
6
8
  require 'facturacr/credit_note'
9
+ require 'facturacr/export_invoice'
10
+ require 'facturacr/purchase_invoice'
7
11
  require 'facturacr/debit_note'
8
12
  require 'facturacr/ticket'
9
13
  require 'facturacr/signed_document'
@@ -14,12 +18,13 @@ require 'facturacr/version'
14
18
  require 'facturacr/data_provider'
15
19
  require 'facturacr/signer/signer'
16
20
  require 'facturacr/reception_message'
21
+ require 'facturacr/error'
17
22
 
18
23
  module FE
19
24
  class << self
20
25
  attr_accessor :configuration
21
26
  end
22
-
27
+
23
28
  def self.configure
24
29
  if self.configuration.nil?
25
30
  self.configuration ||= Configuration.new
@@ -27,13 +32,13 @@ module FE
27
32
  yield(configuration)
28
33
  configuration.read_config_file if configuration.file?
29
34
  end
30
-
35
+
31
36
  def self.root
32
37
  File.dirname __dir__
33
38
  end
34
-
39
+
35
40
  def self.bin
36
41
  File.join root, 'bin'
37
42
  end
38
-
43
+
39
44
  end
@@ -32,13 +32,13 @@ module FE
32
32
  @token
33
33
  rescue => e
34
34
  puts "AUTH ERROR: #{e.message}".red
35
- raise e
35
+ raise FE::Error.new("authentication error: #{e.message}",class: self.class)
36
36
  end
37
37
 
38
38
  def logout
39
39
  url = @authentication_endpoint
40
40
  if @authentication_endpoint.end_with?('token')
41
- url.gsub!("token","logout")
41
+ url = url.gsub("token","logout")
42
42
  else
43
43
  url += "/logout"
44
44
  end
@@ -57,7 +57,8 @@ module FE
57
57
  return true
58
58
  end
59
59
  rescue => e
60
- @errors[:request] = {message: e.message, response: e.response}
60
+ @errors[:request] = {message: e.message}
61
+ @errors[:response] = e.response if e.respond_to?(:response)
61
62
  return false
62
63
  end
63
64
 
@@ -2,7 +2,7 @@ require 'yaml'
2
2
  require 'erb'
3
3
  module FE
4
4
  class Configuration
5
-
5
+
6
6
  attr_accessor :api_username
7
7
  attr_accessor :api_password
8
8
  attr_accessor :key_path
@@ -13,8 +13,9 @@ module FE
13
13
  attr_accessor :environment
14
14
  attr_accessor :file_path
15
15
  attr_accessor :mode
16
-
17
-
16
+ attr_accessor :version
17
+
18
+
18
19
  def initialize
19
20
  @environment = "development"
20
21
  @mode = :manual
@@ -26,7 +27,7 @@ module FE
26
27
  @documents_endpoint = "https://api.comprobanteselectronicos.go.cr/recepcion-sandbox/v1"
27
28
  @authentication_endpoint = "https://idp.comprobanteselectronicos.go.cr/auth/realms/rut-stag/protocol/openid-connect"
28
29
  end
29
-
30
+
30
31
  def read_config_file
31
32
  if file? && @file_path && File.exists?(@file_path)
32
33
  template = ERB.new(File.read(@file_path))
@@ -38,14 +39,22 @@ module FE
38
39
  end
39
40
  end
40
41
  end
41
-
42
+
42
43
  def manual?
43
44
  @mode.to_sym.eql?(:manual)
44
45
  end
45
-
46
+
46
47
  def file?
47
48
  @mode.to_sym.eql?(:file)
48
49
  end
49
-
50
+
51
+ def version_42?
52
+ version.eql?('4.2')
53
+ end
54
+
55
+ def version_43?
56
+ version.eql?('4.3')
57
+ end
58
+
50
59
  end
51
- end
60
+ end
@@ -1,34 +1,44 @@
1
1
  require 'facturacr/document/regulation'
2
2
  module FE
3
-
3
+
4
4
  class CreditNote < Document
5
-
5
+ NAMESPACES ={
6
+ "4.2" => {
7
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
8
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
9
+ "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaCreditoElectronica"#,
10
+ },
11
+ "4.3" => {
12
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
13
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
14
+ "xmlns"=>"https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/notaCreditoElectronica"#,
15
+ }
16
+ }
17
+ DOCUMENT_TYPE = "03"
6
18
  def initialize(args={})
19
+ @version = args[:version]
20
+ @economic_activity = args[:economic_activity]
7
21
  @date = args[:date]
8
22
  @issuer = args[:issuer]
9
23
  @receiver = args[:receiver]
10
24
  @items = args[:items]
11
25
  @number = args[:number]
12
26
  @condition = args[:condition]
13
- @payment_type = args[:payment_type] || "01"
14
- @document_type = "03"
27
+ @payment_type = args[:payment_type] || ["01"]
28
+ @document_type = DOCUMENT_TYPE
15
29
  @credit_term = args[:credit_term]
16
30
  @summary = args[:summary]
17
31
  @regulation = args[:regulation] ||= FE::Document::Regulation.new
18
32
  @security_code = args[:security_code]
19
33
  @document_situation = args[:document_situation]
20
34
  @references = args[:references]
21
- @namespaces = {
22
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
23
- "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
24
- "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaCreditoElectronica"#,
25
- }
35
+ @namespaces = NAMESPACES[@version]
26
36
  @others = args[:others] || []
27
37
  end
28
-
38
+
29
39
  def document_tag
30
40
  "NotaCreditoElectronica"
31
41
  end
32
-
33
- end
34
- end
42
+
43
+ end
44
+ end
@@ -1,34 +1,45 @@
1
1
  require 'facturacr/document/regulation'
2
2
  module FE
3
-
3
+
4
4
  class DebitNote < Document
5
-
5
+
6
+ NAMESPACES ={
7
+ "4.2" => {
8
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
9
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
10
+ "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaDebitoElectronica"
11
+ },
12
+ "4.3" => {
13
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
14
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
15
+ "xmlns"=>"https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/notaDebitoElectronica"#,
16
+ }
17
+ }
18
+ DOCUMENT_TYPE = "02"
6
19
  def initialize(args={})
20
+ @version = args[:version]
21
+ @economic_activity = args[:economic_activity]
7
22
  @date = args[:date]
8
23
  @issuer = args[:issuer]
9
24
  @receiver = args[:receiver]
10
25
  @items = args[:items]
11
26
  @number = args[:number]
12
27
  @condition = args[:condition]
13
- @payment_type = args[:payment_type] || "01"
14
- @document_type = "02"
28
+ @payment_type = args[:payment_type] || ["01"]
29
+ @document_type = DOCUMENT_TYPE
15
30
  @credit_term = args[:credit_term]
16
31
  @summary = args[:summary]
17
32
  @regulation = args[:regulation] ||= FE::Document::Regulation.new
18
33
  @security_code = args[:security_code]
19
34
  @document_situation = args[:document_situation]
20
35
  @references = args[:references]
21
- @namespaces = {
22
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
23
- "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
24
- "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaDebitoElectronica"
25
- }
36
+ @namespaces = NAMESPACES[@version]
26
37
  @others = args[:others] || []
27
38
  end
28
-
39
+
29
40
  def document_tag
30
41
  "NotaDebitoElectronica"
31
42
  end
32
-
33
- end
34
- end
43
+
44
+ end
45
+ end
@@ -1,16 +1,19 @@
1
1
  require 'active_model'
2
2
 
3
- module FE
3
+ module FE
4
4
  class Document
5
5
  include ActiveModel::Validations
6
-
6
+
7
7
  CONDITIONS = {
8
- "01"=>"Contado",
9
- "02"=>"Crédito",
10
- "03"=>"Consignación",
11
- "04"=>"Apartado",
12
- "05"=>"Arrendamiento con Opción de Compra",
8
+ "01"=>"Contado",
9
+ "02"=>"Crédito",
10
+ "03"=>"Consignación",
11
+ "04"=>"Apartado",
12
+ "05"=>"Arrendamiento con Opción de Compra",
13
13
  "06"=>"Arrendamiento en Función Financiera",
14
+ "07"=>"Cobro a favor de un tercero",
15
+ "08"=>"Servicios prestados al Estado a crédito ",
16
+ "09"=>"Pago del servicios prestado al Estado ",
14
17
  "99"=>"Otros"
15
18
  }.freeze
16
19
  PAYMENT_TYPES = {
@@ -29,47 +32,49 @@ module FE
29
32
  "05"=> "Nota de despacho",
30
33
  "06"=> "Contrato",
31
34
  "07"=> "Procedimiento",
32
- "08"=> "Comprobante Emitido en Contingencia",
33
- "99"=> "Otros"
34
-
35
+ "08"=> "Factura Electrónica de compra",
36
+ "09"=> "Factura Electronica de exportación"
35
37
  }.freeze
36
38
  DOCUMENT_SITUATION = {
37
39
  "1" => "Normal",
38
40
  "2" => "Contingencia",
39
41
  "3" => "Sin Internet"
40
42
  }.freeze
41
-
42
- attr_accessor :serial, :date, :issuer, :receiver, :condition, :credit_term,
43
- :payment_type, :service_type, :reference_information,
44
- :regulation, :number, :document_type, :security_code,
45
- :items, :references, :namespaces, :summary, :document_situation,
46
- :headquarters, :terminal, :others, :key
43
+
44
+ attr_accessor :serial, :date, :issuer, :receiver, :condition, :credit_term,
45
+ :payment_type, :service_type, :reference_information,
46
+ :regulation, :number, :document_type, :security_code,
47
+ :items, :references, :namespaces, :summary, :document_situation,
48
+ :headquarters, :terminal, :others, :key, :economic_activity, :other_charges, :version
47
49
 
50
+ validates :version, presence: true
51
+ validates :economic_activity, presence: true, if: ->{ version.eql?("4.3") }
48
52
  validates :date, presence: true
49
53
  validates :number, presence: true
50
54
  validates :issuer, presence: true
55
+ validates :receiver, presence: true, if: -> {document_type.eql?("01") || document_type.eql?("08")}
51
56
  validates :condition, presence: true, inclusion: CONDITIONS.keys
52
- validates :credit_term, presence: true, if: ->{condition.eql?("02")}
53
- validates :payment_type, presence: true, inclusion: PAYMENT_TYPES.keys
57
+ validates :credit_term, presence: true, if: ->{ condition.eql?("02") }
54
58
  validates :document_type, presence: true, inclusion: DOCUMENT_TYPES.keys
55
59
  validates :document_situation, presence: true, inclusion: DOCUMENT_SITUATION.keys
56
60
  validates :summary, presence: true
57
- validates :regulation, presence: true
61
+ validates :regulation, presence: true, if: ->{ version.eql?("4.2") }
58
62
  validates :security_code, presence: true, length: {is: 8}
59
63
  validates :references, presence: true, if: -> {document_type.eql?("02") || document_type.eql?("03")}
60
-
64
+ validates :items, presence:true
65
+ validate :payment_types_ok?
61
66
 
62
67
  def initialize
63
- raise "Subclasses must implement this method"
68
+ raise FE::Error "Subclasses must implement this method"
64
69
  end
65
-
70
+
66
71
  def document_name
67
- raise "Subclasses must implement this method"
72
+ raise FE::Error "Subclasses must implement this method"
68
73
  end
69
-
74
+
70
75
  def key
71
76
  @key ||= begin
72
- raise "Documento inválido: #{errors.messages}" unless valid?
77
+ raise "Documento inválido: #{errors.messages}" unless valid?
73
78
  country = "506"
74
79
  day = "%02d" % @date.day
75
80
  month = "%02d" % @date.month
@@ -81,69 +86,87 @@ module FE
81
86
 
82
87
  result = "#{country}#{day}#{month}#{year}#{id_number}#{sequence}#{type}#{security_code}"
83
88
  raise "The key is invalid: #{result}" unless result.length.eql?(50)
84
-
89
+
85
90
  result
86
91
  end
87
92
  end
88
-
93
+
89
94
  def headquarters
90
95
  @headquarters ||= "001"
91
96
  end
92
-
97
+
93
98
  def terminal
94
99
  @terminal ||= "00001"
95
- end
96
-
100
+ end
101
+
97
102
  def sequence
98
103
  cons = ("%010d" % @number)
99
104
  "#{headquarters}#{terminal}#{@document_type}#{cons}"
100
105
  end
101
-
106
+
107
+ def version_42?
108
+ @version.eql?("4.2")
109
+ end
110
+
111
+ def version_43?
112
+ @version.eql?("4.3")
113
+ end
114
+
102
115
  def build_xml
103
- raise "Documento inválido: #{errors.messages}" unless valid?
116
+ raise FE::Error.new "Documento inválido", class: self.class, messages: errors.messages unless valid?
104
117
  builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8')
105
-
118
+
106
119
  builder.send(document_tag, @namespaces) do |xml|
107
120
  xml.Clave key
121
+ xml.CodigoActividad @economic_activity if version_43?
108
122
  xml.NumeroConsecutivo sequence
109
123
  xml.FechaEmision @date.xmlschema
110
- issuer.build_xml(xml)
111
- receiver.build_xml(xml) if receiver.present?
124
+ issuer.build_xml(xml, self)
125
+ receiver.build_xml(xml,self) if receiver.present?
112
126
  xml.CondicionVenta @condition
113
127
  xml.PlazoCredito @credit_term if @credit_term.present? && @condition.eql?("02")
114
- xml.MedioPago @payment_type
128
+
129
+ @payment_type.each do |pt|
130
+ @summary.with_credit_card = true if pt.eql?("02")
131
+ xml.MedioPago pt
132
+ end
133
+
134
+
115
135
  xml.DetalleServicio do |x|
116
136
  @items.each do |item|
117
- item.build_xml(x)
137
+ item.build_xml(x, self)
118
138
  end
119
139
  end
120
-
121
- summary.build_xml(xml)
122
-
140
+
141
+
142
+ other_charges.build_xml(xml,self) if other_charges.present? && version_43? # see this
143
+
144
+ summary.build_xml(xml, self)
145
+
123
146
  if references.present?
124
147
  references.each do |r|
125
- r.build_xml(xml)
148
+ r.build_xml(xml, self)
126
149
  end
127
150
  end
128
-
129
- regulation.build_xml(xml)
130
-
151
+
152
+ regulation.build_xml(xml,self) if version_42?
153
+
131
154
  if others.any?
132
155
  xml.Otros do |x|
133
156
  @others.each do |o|
134
- o.build_xml(x)
157
+ o.build_xml(x, self)
135
158
  end
136
159
  end
137
160
  end
138
161
  end
139
-
162
+
140
163
  builder
141
164
  end
142
-
165
+
143
166
  def generate
144
167
  build_xml.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)
145
168
  end
146
-
169
+
147
170
  def api_payload
148
171
  payload = {}
149
172
  payload[:clave] = key
@@ -158,13 +181,26 @@ module FE
158
181
  numeroIdentificacion: @receiver.identification_document.id_number
159
182
  }
160
183
  end
161
-
184
+
162
185
  payload
163
186
  end
164
-
187
+
188
+ private
189
+
190
+ def payment_types_ok?
191
+ errors.add :payment_type, "missing payment type" if @payment_type.nil?
192
+ if @payment_type.is_a?(Array)
193
+ errors.add :payment_type, "invalid payment types: not included" unless @payment_type.all? {|i| PAYMENT_TYPES.include?(i)}
194
+ else
195
+ errors.add :payment_type, "invalid payment type: not array"
196
+ end
197
+
198
+ end
199
+
165
200
  end
166
-
167
-
201
+
202
+
203
+
168
204
  end
169
205
 
170
206
  require 'facturacr/document/code'
@@ -183,3 +219,4 @@ require 'facturacr/document/summary'
183
219
  require 'facturacr/document/tax'
184
220
  require 'facturacr/document/other_text'
185
221
  require 'facturacr/document/other_content'
222
+ require 'facturacr/document/other_charges'