facturacr 1.0.6 → 1.1.1

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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/facturacr.gemspec +2 -2
  3. data/lib/facturacr.rb +25 -19
  4. data/lib/facturacr/api.rb +5 -4
  5. data/lib/facturacr/builder.rb +4 -4
  6. data/lib/facturacr/configuration.rb +18 -9
  7. data/lib/facturacr/credit_note.rb +24 -13
  8. data/lib/facturacr/data.rb +41 -0
  9. data/lib/facturacr/data_provider.rb +1 -1
  10. data/lib/facturacr/debit_note.rb +25 -13
  11. data/lib/facturacr/document.rb +120 -69
  12. data/lib/facturacr/document/exoneration.rb +17 -12
  13. data/lib/facturacr/document/fax.rb +1 -4
  14. data/lib/facturacr/document/identification_document.rb +7 -10
  15. data/lib/facturacr/document/issuer.rb +31 -32
  16. data/lib/facturacr/document/item.rb +77 -20
  17. data/lib/facturacr/document/location.rb +19 -23
  18. data/lib/facturacr/document/other_charges.rb +56 -0
  19. data/lib/facturacr/document/other_content.rb +30 -0
  20. data/lib/facturacr/document/other_content/price_smart.rb +58 -0
  21. data/lib/facturacr/document/other_text.rb +4 -8
  22. data/lib/facturacr/document/phone.rb +1 -3
  23. data/lib/facturacr/document/phone_type.rb +6 -9
  24. data/lib/facturacr/document/receiver.rb +28 -19
  25. data/lib/facturacr/document/reference.rb +36 -18
  26. data/lib/facturacr/document/regulation.rb +3 -3
  27. data/lib/facturacr/document/summary.rb +54 -22
  28. data/lib/facturacr/document/tax.rb +43 -21
  29. data/lib/facturacr/element.rb +9 -0
  30. data/lib/facturacr/error.rb +11 -0
  31. data/lib/facturacr/export_invoice.rb +38 -0
  32. data/lib/facturacr/invoice.rb +27 -15
  33. data/lib/facturacr/purchase_invoice.rb +39 -0
  34. data/lib/facturacr/reception_message.rb +73 -28
  35. data/lib/facturacr/signed_document.rb +4 -4
  36. data/lib/facturacr/signer/signer.rb +20 -8
  37. data/lib/facturacr/ticket.rb +27 -14
  38. data/lib/facturacr/version.rb +1 -1
  39. data/lib/facturacr/xml_document.rb +46 -29
  40. metadata +15 -9
@@ -1,54 +1,76 @@
1
1
  module FE
2
2
  class Document
3
- class Tax
3
+ class Tax < Element
4
4
  include ActiveModel::Validations
5
-
5
+
6
6
  TAX_CODES = {
7
- "01"=>"Impuesto General sobre las Ventas",
7
+ "01"=>"Impuesto al Valor Agregado",
8
8
  "02"=>"Impuesto Selectivo de Consumo",
9
9
  "03"=>"Impuesto Único a los combustibles",
10
10
  "04"=>"Impuesto específico de bebidas alcohólicas",
11
11
  "05"=>"Impuesto Específico sobre las bebidas envasadas sin contenido alcóholico y jabones de tocador",
12
12
  "06"=>"Impuesto a los Productos de Tabaco",
13
- "07"=>"Servicio",
13
+ "07"=>"IVA (cálculo especial)",
14
+ "08"=>"IVA Régimen de Bienes Usados (Factor)",
14
15
  "12"=>"Impuesto específico al cemento",
15
- "98"=>"Otros",
16
- "08"=>"Impuesto General sobre las ventas diplomáticos",
17
- "09"=>"Impuesto general sobre las ventas Compras autorizadas",
18
- "10"=>"Impuesto general sobre las ventas instituciones públicas y otros organismos",
19
- "11"=>"Impuesto Selectivo de Consumo Compras Autorizadas",
20
16
  "99"=>"Otros"
21
- }
22
- attr_accessor :code, :rate, :total, :exoneration
23
-
17
+ }.freeze
18
+ RATE_CODES ={
19
+ "01"=>"Tarifa 0% (Exento)",
20
+ "02"=>"Tarifa reducida 1%",
21
+ "03"=>"Tarifa reducida 2%",
22
+ "04"=>"Tarifa reducida 4%",
23
+ "05"=>"Transitorio 0%",
24
+ "06"=>"Transitorio 4% ",
25
+ "07"=>"Transitorio 8% ",
26
+ "08"=>"Tarifa general 13%"
27
+ }.freeze
28
+ attr_accessor :code, :rate_code ,:rate, :iva_factor, :total, :exoneration, :total_exportation
29
+
30
+ validates :rate_code, inclusion: RATE_CODES.keys, presence: true, if:->{ document.version_43?}
24
31
  validates :code, presence: true, inclusion: TAX_CODES.keys
32
+ # validates :total_exportation, presence: false, if:->{:document_type.eql?("01") || :document_type.eql?("08") || :document_type.eql?("04")}
25
33
  # It is a mandatory field when a tax is added. And it is a decimal number that can be composed of 4 integers and 2 decimals
26
34
  validates :rate, presence: true, format: { with: /\A\d{1,4}(\.\d{0,2})?\z/ }
27
35
  # It is a mandatory field when a tax is added, it is obtained from the multiplication of the "subtotal" field by "tax rate"
28
36
  # And is a decimal number that can be composed of 13 integers and 5 decimals
29
37
  validates :total, presence: true, format: { with: /\A\d{1,13}(\.\d{0,5})?\z/ }
38
+ #validates :exoneration, presence:false, if: ->{:document_type.eql?("09")}
39
+ # validates :iva_factor, presence: true, if: ->{ }
30
40
 
31
41
  def initialize(args={})
32
42
  @code = args[:code]
43
+ @rate_code = args[:rate_code]
33
44
  @rate = args[:rate]
45
+ @iva_factor = args[:iva_factor]
34
46
  @total = args[:total]
35
47
  @exoneration = args[:exoneration]
48
+ @total_exportation = args[:total_exportation]
49
+
36
50
  end
37
-
38
- def build_xml(node)
39
- raise "Invalida Record: #{errors.messages}" unless valid?
51
+
52
+
53
+ def build_xml(node, document)
54
+ @document = document
55
+ raise FE::Error.new("tax invalid",class: self.class, messages: errors.messages) unless valid?
40
56
  node = Nokogiri::XML::Builder.new if node.nil?
41
-
57
+
42
58
  node.Impuesto do |xml|
43
59
  xml.Codigo @code
60
+ xml.CodigoTarifa @rate_code if @rate_code.present? && document.version_43?
44
61
  xml.Tarifa @rate
62
+ xml.FactorIva @iva_factor if @iva_factor.present? && document.version_43?
45
63
  xml.Monto @total
46
- if @exoneration.present?
47
- @exoneration.build_xml(xml)
48
- end
64
+ xml.MontoExportacion @total_exportation if @total_exportation.present? && document.version_43?
65
+
66
+ if exoneration.present?
67
+ exoneration.build_xml(xml,document)
68
+ end
69
+
49
70
  end
50
71
  end
51
-
72
+
73
+
52
74
  end
53
75
  end
54
- end
76
+ end
@@ -0,0 +1,9 @@
1
+ module FE
2
+ class Element
3
+ attr_accessor :version, :document
4
+
5
+ def build_xml(node, document)
6
+ raise "must be implemented in sublcasses"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module FE
2
+ class Error < StandardError
3
+ attr_reader :messages, :klass
4
+
5
+ def initialize(msg, args = {})
6
+ @klass = args[:class]
7
+ @messages = args[:messages]
8
+ super(msg)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,38 @@
1
+ require 'facturacr/document'
2
+
3
+ module FE
4
+
5
+ class ExportInvoice < Document
6
+ validates :receiver, presence: true, if: -> { version.eql?("4.3") }
7
+
8
+ DOCUMENT_TYPE = "09"
9
+ def initialize(args={})
10
+ @version = args[:version]
11
+ @economic_activity = args[:economic_activity]
12
+ @date = args[:date]
13
+ @issuer = args[:issuer]
14
+ @receiver = args[:receiver]
15
+ @items = args[:items]
16
+ @number = args[:number]
17
+ @condition = args[:condition]
18
+ @payment_type = args[:payment_type] || ["01"]
19
+ @document_type = DOCUMENT_TYPE
20
+ @credit_term = args[:credit_term]
21
+ @summary = args[:summary]
22
+ @security_code = args[:security_code]
23
+ @document_situation = args[:document_situation]
24
+ @other_charges = args[:other_charges]
25
+ @namespaces = {
26
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
27
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
28
+ "xmlns"=>"https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/facturaElectronicaExportacion"#,
29
+ }
30
+ @others = args[:others] || []
31
+ end
32
+
33
+ def document_tag
34
+ "FacturaElectronicaExportacion"
35
+ end
36
+
37
+ end
38
+ end
@@ -1,35 +1,47 @@
1
- require 'facturacr/document'
1
+ require_relative 'document'
2
2
 
3
3
  module FE
4
-
4
+
5
5
  class Invoice < Document
6
-
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/facturaElectronica"#,
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/facturaElectronica"#,
16
+ }}
17
+ validates :receiver, presence: true, if: -> { version.eql?("4.3") }
18
+
19
+ DOCUMENT_TYPE = "01"
7
20
  def initialize(args={})
21
+ @version = args[:version]
22
+ @economic_activity = args[:economic_activity]
8
23
  @date = args[:date]
9
24
  @issuer = args[:issuer]
10
25
  @receiver = args[:receiver]
11
26
  @items = args[:items]
12
27
  @number = args[:number]
13
28
  @condition = args[:condition]
14
- @payment_type = args[:payment_type] || "01"
15
- @document_type = "01"
29
+ @payment_type = args[:payment_type] || ["01"]
30
+ @document_type = DOCUMENT_TYPE
16
31
  @credit_term = args[:credit_term]
17
32
  @summary = args[:summary]
18
33
  @regulation = args[:regulation] ||= FE::Document::Regulation.new
19
34
  @security_code = args[:security_code]
20
35
  @document_situation = args[:document_situation]
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/facturaElectronica"#,
25
- #"xsi:schemaLocation"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/facturaElectronica https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/facturaElectronica.xsd"
26
- }
36
+ @other_charges = args[:other_charges]
37
+ @namespaces = NAMESPACES[@version]
27
38
  @others = args[:others] || []
39
+ @references = args[:references] || []
28
40
  end
29
-
41
+
30
42
  def document_tag
31
43
  "FacturaElectronica"
32
44
  end
33
-
34
- end
35
- end
45
+
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ require 'facturacr/document'
2
+
3
+ module FE
4
+
5
+ class PurchaseInvoice < Document
6
+ DOCUMENT_TYPE = "08"
7
+
8
+ validates :receiver, presence: true, if: -> { version.eql?("4.3") }
9
+
10
+ def initialize(args={})
11
+ @version = args[:version]
12
+ @economic_activity = args[:economic_activity]
13
+ @date = args[:date]
14
+ @issuer = args[:issuer]
15
+ @receiver = args[:receiver]
16
+ @items = args[:items]
17
+ @number = args[:number]
18
+ @condition = args[:condition]
19
+ @payment_type = args[:payment_type] || ["01"]
20
+ @document_type = DOCUMENT_TYPE
21
+ @credit_term = args[:credit_term]
22
+ @summary = args[:summary]
23
+ @security_code = args[:security_code]
24
+ @document_situation = args[:document_situation]
25
+ @other_charges = args[:other_charges]
26
+ @namespaces = {
27
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
28
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
29
+ "xmlns"=>"https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/facturaElectronicaCompra"#,
30
+ }
31
+ @others = args[:others] || []
32
+ end
33
+
34
+ def document_tag
35
+ "FacturaElectronicaCompra"
36
+ end
37
+
38
+ end
39
+ end
@@ -3,27 +3,58 @@ require 'active_model'
3
3
  module FE
4
4
  class ReceptionMessage
5
5
  include ActiveModel::Validations
6
-
6
+
7
7
  MESSAGE_TYPES = {
8
8
  "1" => "Aceptado",
9
9
  "2" => "Aceptacion Parcial",
10
10
  "3" => "Rechazado"
11
11
  }.freeze
12
-
13
- attr_accessor :key, :date, :issuer_id_number, :receiver_id_number, :message, :details, :tax, :total, :number, :receiver_id_type, :security_code, :document_situation, :issuer_id_type
14
-
12
+
13
+ TAX_CONDITION={
14
+ "01" => "Genera crédito IVA",
15
+ "02" => "Genera Crédito parcial del IVA",
16
+ "03" => "Bienes de Capital",
17
+ "04" => "Gasto corriente",
18
+ "05" => "Proporcionalidad"
19
+ }.freeze
20
+
21
+ NAMESPACES = {
22
+ "4.2" => {
23
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
24
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
25
+ "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/mensajeReceptor"
26
+ },
27
+ "4.3" => {
28
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
29
+ "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
30
+ "xmlns"=>"https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/mensajeReceptor"
31
+ }
32
+ }
33
+ attr_writer :headquarters, :terminal, :key
34
+ attr_accessor :key, :date, :issuer_id_number, :receiver_id_number, :message, :details, :economic_activity,
35
+ :tax_condition,:creditable_tax, :applicable_expense,:tax, :total, :number, :receiver_id_type, :security_code,
36
+ :document_situation, :issuer_id_type, :original_version, :version
37
+
38
+ validates :version, presence: true
39
+ validates :original_version, presence: true, if: -> { version_43? }
15
40
  validates :date, presence: true
16
41
  validates :issuer_id_number, presence: true, length: {is: 12}
17
42
  validates :receiver_id_number, presence: true, length: {is: 12}
18
43
  validates :message, presence: true, inclusion: MESSAGE_TYPES.keys
44
+ validates :tax_condition, inclusion: TAX_CONDITION.keys, presence:true, if: ->{ version_43? && original_version.eql?("4.3") && !tax_condition.blank?}
19
45
  validates :tax, numericality: true, if: -> { tax.present? }
20
46
  validates :total, presence: true, numericality: true
21
47
  validates :number, presence: true
22
48
  validates :security_code, presence: true, length: {is: 8}
23
49
  validates :issuer_id_type, presence: true
24
50
  validates :receiver_id_type, presence: true
25
-
51
+ validates :economic_activity, presence: true, if: ->{ version_43? && tax_condition != "05"}
52
+ validates :creditable_tax, presence: true, if: -> { version_43? && tax_condition != "05" && (creditable_tax.present? && tax != creditable_tax) }
53
+ validates :applicable_expense, presence: true, if: -> { version_43? && tax_condition != "05" && (applicable_expense.present? && tax != applicable_expense) }
54
+
26
55
  def initialize(args = {})
56
+ @version = args[:version]
57
+ @original_version = args[:original_version]
27
58
  @key = args[:key]
28
59
  @date = args[:date]
29
60
  @issuer_id_type = args[:issuer_id_type]
@@ -37,22 +68,22 @@ module FE
37
68
  @number = args[:number].to_i
38
69
  @security_code = args[:security_code]
39
70
  @document_situation = args[:document_situation]
40
- @namespaces = {
41
- "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
42
- "xmlns:xsd"=>"http://www.w3.org/2001/XMLSchema",
43
- "xmlns"=>"https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/mensajeReceptor"
44
- }
71
+ @namespaces = NAMESPACES[@version]
72
+ @tax_condition = args[:tax_condition]
73
+ @economic_activity = args[:economic_activity]
74
+ @creditable_tax = args[:creditable_tax]
75
+ @applicable_expense = args[:applicable_expense]
45
76
  end
46
-
47
-
77
+
78
+
48
79
  def headquarters
49
80
  @headquarters ||= "001"
50
81
  end
51
-
82
+
52
83
  def terminal
53
84
  @terminal ||= "00001"
54
- end
55
-
85
+ end
86
+
56
87
  def sequence
57
88
  if @message.eql?("1")
58
89
  @document_type = "05"
@@ -64,13 +95,20 @@ module FE
64
95
  cons = ("%010d" % @number)
65
96
  "#{headquarters}#{terminal}#{@document_type}#{cons}"
66
97
  end
67
-
68
-
69
-
98
+
99
+ def version_42?
100
+ @version.eql?("4.2")
101
+ end
102
+
103
+ def version_43?
104
+ @version.eql?("4.3")
105
+ end
106
+
107
+
70
108
  def build_xml
71
- raise "Documento inválido: #{errors.messages}" unless valid?
109
+ raise FE::Error.new "Documento inválido #{errors.messages}", class: self.class, messages: errors.messages unless valid?
72
110
  builder = Nokogiri::XML::Builder.new
73
-
111
+
74
112
  builder.MensajeReceptor(@namespaces) do |xml|
75
113
  xml.Clave @key
76
114
  xml.NumeroCedulaEmisor @issuer_id_number
@@ -78,20 +116,27 @@ module FE
78
116
  xml.Mensaje @message
79
117
  xml.DetalleMensaje @details if @details
80
118
  xml.MontoTotalImpuesto @tax.to_f if @tax
119
+ if version_43? && @original_version.eql?("4.3")
120
+ xml.CodigoActividad @economic_activity if @economic_activity.present?
121
+ xml.CondicionImpuesto @tax_condition if @tax_condition.present?
122
+ xml.MontoImpuestoAcreditar @creditable_tax.to_f if @creditable_tax.present?
123
+ xml.MontoTotalDeGastoAplicable @applicable_expense.to_f if @applicable_expense.present?
124
+ end
125
+
81
126
  xml.TotalFactura @total
82
127
  xml.NumeroCedulaReceptor @receiver_id_number
83
128
  xml.NumeroConsecutivoReceptor sequence
84
129
  end
85
-
130
+
86
131
  builder
87
132
  end
88
-
133
+
89
134
  def generate
90
135
  build_xml.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)
91
136
  end
92
-
137
+
93
138
  def api_payload
94
-
139
+
95
140
  payload = {}
96
141
  payload[:clave] = @key
97
142
  payload[:fecha] = @date.xmlschema
@@ -106,7 +151,7 @@ module FE
106
151
  payload[:consecutivoReceptor] = sequence
107
152
  payload
108
153
  end
109
-
154
+
110
155
  def infer_id_type(id_number)
111
156
  if id_number.to_i.to_s.size == 9
112
157
  "01"
@@ -116,7 +161,7 @@ module FE
116
161
  "03"
117
162
  end
118
163
  end
119
-
164
+
120
165
  end
121
-
122
- end
166
+
167
+ end
@@ -3,19 +3,19 @@ require 'base64'
3
3
  module FE
4
4
  class SignedDocument
5
5
  attr_accessor :document, :base64, :payload
6
-
6
+
7
7
  def initialize(document, xml_provider)
8
8
  # Backwards compatibility with v0.1.4
9
9
  if xml_provider.is_a?(String)
10
- raise ArgumentError, "File: #{xml_provider} does not exist" unless File.exists?(xml_provider)
10
+ raise ArgumentError, "File: #{xml_provider} does not exist" unless File.exist?(xml_provider)
11
11
  xml_provider = FE::DataProvider.new(:file, xml_provider)
12
12
  end
13
13
  raise ArgumentError, "Invalid Argument" unless xml_provider.is_a?(FE::DataProvider)
14
-
14
+
15
15
  @document = document
16
16
  @base64 = Base64.encode64(xml_provider.contents).gsub("\n","");
17
17
  @payload = document.api_payload
18
18
  @payload[:comprobanteXml] = @base64
19
19
  end
20
20
  end
21
- end
21
+ end