facturacr 1.0.9 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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'