facturacr 0.1.2

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +9 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +7 -0
  7. data/README.md +181 -0
  8. data/Rakefile +10 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/bin/signer/signer.jar +0 -0
  12. data/config/config.yml +10 -0
  13. data/exe/facturacr +3 -0
  14. data/facturacr.gemspec +31 -0
  15. data/lib/facturacr.rb +37 -0
  16. data/lib/facturacr/api.rb +68 -0
  17. data/lib/facturacr/api/document_status.rb +36 -0
  18. data/lib/facturacr/builder.rb +223 -0
  19. data/lib/facturacr/cli.rb +116 -0
  20. data/lib/facturacr/cli/generate.rb +133 -0
  21. data/lib/facturacr/configuration.rb +51 -0
  22. data/lib/facturacr/credit_note.rb +33 -0
  23. data/lib/facturacr/debit_note.rb +33 -0
  24. data/lib/facturacr/document.rb +171 -0
  25. data/lib/facturacr/document/code.rb +4 -0
  26. data/lib/facturacr/document/exoneration.rb +49 -0
  27. data/lib/facturacr/document/fax.rb +24 -0
  28. data/lib/facturacr/document/identification_document.rb +41 -0
  29. data/lib/facturacr/document/issuer.rb +56 -0
  30. data/lib/facturacr/document/item.rb +67 -0
  31. data/lib/facturacr/document/location.rb +49 -0
  32. data/lib/facturacr/document/phone.rb +22 -0
  33. data/lib/facturacr/document/phone_type.rb +37 -0
  34. data/lib/facturacr/document/receiver.rb +59 -0
  35. data/lib/facturacr/document/reference.rb +46 -0
  36. data/lib/facturacr/document/regulation.rb +26 -0
  37. data/lib/facturacr/document/summary.rb +64 -0
  38. data/lib/facturacr/document/tax.rb +44 -0
  39. data/lib/facturacr/invoice.rb +34 -0
  40. data/lib/facturacr/signed_document.rb +19 -0
  41. data/lib/facturacr/signer/signer.rb +245 -0
  42. data/lib/facturacr/ticket.rb +34 -0
  43. data/lib/facturacr/version.rb +3 -0
  44. data/lib/facturacr/xml_document.rb +161 -0
  45. data/resources/FacturaElectronica_V.4.2.xsd +1571 -0
  46. data/resources/NotaCreditoElectronica_V4.2.xsd +1657 -0
  47. data/resources/credit_note.xml +86 -0
  48. data/resources/data.yml +73 -0
  49. data/resources/debit_note.xml +86 -0
  50. data/resources/invoice.xml +94 -0
  51. data/resources/pruebas.xml +37 -0
  52. data/resources/test.p12 +0 -0
  53. metadata +235 -0
@@ -0,0 +1,67 @@
1
+ module FE
2
+ class Document
3
+ class Item
4
+ include ActiveModel::Validations
5
+
6
+ attr_accessor :line_number, :code, :quantity, :unit, :description, :unit_price, :total,
7
+ :discount, :discount_reason, :subtotal, :taxes, :exoneration, :net_total
8
+
9
+ validates :line_number, presence: true
10
+ validates :quantity, presence: true, numericality: {greater_than: 0}
11
+ validates :unit, presence: true
12
+ validates :description, presence: true, length: {maximum: 160 }
13
+ validates :unit_price, presence: true
14
+ validates :total, presence: true
15
+ validates :discount, numericality: { grater_than: 0}, if: ->{ discount.present? }
16
+ validates :discount_reason, presence: true, if: ->{ discount.present? }
17
+ validates :subtotal, presence: true
18
+ validates :net_total, presence: true
19
+
20
+
21
+ def initialize(args={})
22
+ @line_number = args[:line_number]
23
+ @code = args[:code]
24
+ @quantity = args[:quantity]
25
+ @unit = args[:unit]
26
+ @description = args[:description]
27
+ @unit_price = args[:unit_price]
28
+ @total = args[:total]
29
+ @discount = args[:discount]
30
+ @discount_reason = args[:discount_reason]
31
+ @subtotal = args[:subtotal]
32
+ @taxes = args[:taxes] || []
33
+ @net_total = args[:net_total]
34
+ end
35
+
36
+ def build_xml(node)
37
+ raise "Item invalid: #{errors.messages}" unless valid?
38
+ node = Nokogiri::XML::Builder.new if node.nil?
39
+ node.LineaDetalle do |x|
40
+ x.NumeroLinea @line_number
41
+ if @code.present?
42
+ x.Codigo do |x2|
43
+ x2.Tipo "01"
44
+ x2.Codigo @code
45
+ end
46
+ end
47
+ x.Cantidad @quantity
48
+ x.UnidadMedida @unit
49
+ x.Detalle @description
50
+ x.PrecioUnitario @unit_price
51
+ x.MontoTotal @total
52
+ x.Discount @discount if @discount.present?
53
+ x.NaturalezaDescuento @discount_reason if @discount_reason.present?
54
+ x.SubTotal @subtotal
55
+ @taxes.each do |tax|
56
+ tax.build_xml(x)
57
+ end
58
+ if @exoneration.present?
59
+ @exoneration.build_xml(x)
60
+ end
61
+ x.MontoTotalLinea @net_total
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,49 @@
1
+ require "facturacr/document"
2
+ require 'active_model'
3
+ require 'nokogiri'
4
+
5
+ module FE
6
+ class Document
7
+
8
+
9
+ class Location
10
+ include ActiveModel::Validations
11
+
12
+ attr_accessor :province, :county,:district,:neighborhood, :others
13
+
14
+ validates :province, presence: true, length: { is: 1 }
15
+ validates :county, presence: true, length: { is: 2 }
16
+ validates :district, presence: true, length: { is: 2 }
17
+ validates :neighborhood, length: { is: 2 }, allow_blank: true
18
+ validates :others, presence: true, length: { maximum: 160 }
19
+
20
+ def initialize(args={})
21
+
22
+ @province = args[:province]
23
+ @county = args[:county]
24
+ @district = args[:district]
25
+ @neighborhood = args[:neighborhood]
26
+ @others = args[:others]
27
+
28
+
29
+ end
30
+
31
+ def build_xml(node)
32
+ raise "Location invalid: #{errors.messages}" unless valid?
33
+ node = Nokogiri::XML::Builder.new if node.nil?
34
+ node.Ubicacion do |x|
35
+ x.Provincia @province
36
+ x.Canton @county
37
+ x.Distrito @district
38
+ x.Barrio @neighborhood unless @neighborhood.nil?
39
+ x.OtrasSenas @others
40
+ end
41
+ end
42
+
43
+ def to_xml(builder)
44
+ build_xml(builder).to_xml
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ require "facturacr/document"
2
+ require 'facturacr/document/phone_type'
3
+ require 'active_model'
4
+
5
+ module FE
6
+ class Document
7
+ class Phone < PhoneType
8
+ include ActiveModel::Validations
9
+
10
+ attr_accessor :country_code, :number
11
+
12
+ validates :country_code, presence: true, length: { maximum: 3 }
13
+ validates :number, presence: true, length: {maximum: 20}
14
+
15
+
16
+ def initialize(args={})
17
+ super('Telefono', args[:country_code],args[:number])
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ require "facturacr/document"
2
+ require 'active_model'
3
+ require 'nokogiri'
4
+
5
+ module FE
6
+ class Document
7
+ class PhoneType
8
+ include ActiveModel::Validations
9
+
10
+ attr_accessor :tag_name, :country_code, :number
11
+
12
+ validates :tag_name, presence: true, inclusion: ['Telefono','Fax']
13
+ validates :country_code, presence: true, length: { maximum: 3 }
14
+ validates :number, presence: true, length: {maximum: 20}, format: {with: /\d+/}
15
+
16
+
17
+ def initialize(tag_name, country_code, number)
18
+ @tag_name = tag_name
19
+ @country_code = country_code
20
+ @number = number
21
+ end
22
+
23
+ def build_xml(node)
24
+ raise "Invalid Record: #{errors.messages}" unless valid?
25
+ node = Nokogiri::XML::Builder.new if node.nil?
26
+ node.send(tag_name) do |xml|
27
+ xml.CodigoPais country_code
28
+ xml.NumTelefono number
29
+ end
30
+ end
31
+
32
+ def to_xml(builder)
33
+ build_xml(builder).to_xml
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,59 @@
1
+ require "facturacr/document"
2
+ require 'active_model'
3
+ require 'nokogiri'
4
+
5
+ module FE
6
+ class Document
7
+
8
+
9
+ class Receiver
10
+ include ActiveModel::Validations
11
+
12
+ attr_accessor :name, :identification_document,:foreign_id_number, :comercial_name, :location, :phone, :fax, :email
13
+
14
+ validates :name, presence: true, length: { maximum: 80 }
15
+ validates :comercial_name, length: { maximum: 80 }
16
+ validates :foreign_id_number, length: { maximum: 20 }
17
+
18
+
19
+
20
+ def initialize(args={})
21
+
22
+ @name = args[:name]
23
+ @identification_document = args[:identification_document]
24
+ @comercial_name = args[:comercial_name]
25
+ @location = args[:location]
26
+ @phone = args[:phone]
27
+ @fax = args[:fax]
28
+ @email = args[:email]
29
+ @foreign_id_number = args[:foreign_id_number]
30
+
31
+ end
32
+
33
+ def build_xml(node)
34
+ raise "IdentificationDocument is invalid" if !@identification_document.nil? && !@identification_document.is_a?(IdentificationDocument)
35
+ raise "Location is invalid" if !@location.nil? && !@location.is_a?(Location)
36
+ raise "Phone is invalid" if !@phone.nil? && !@phone.is_a?(Phone)
37
+ raise "Fax is invalid" if !@fax.nil? && !@fax.is_a?(Fax)
38
+ raise "Reciever is invalid: #{errors.messages}" unless valid?
39
+
40
+ node = Nokogiri::XML::Builder.new if node.nil?
41
+ node.Receptor do |xml|
42
+ xml.Nombre @name
43
+ @identification_document.build_xml(xml) if @identification_document.present?
44
+ xml.IdentificacionExtranjer foreign_id_number if @foreign_id_number.present?
45
+ xml.NombreComercial @comercial_name if @comercial_name.present?
46
+ @location.build_xml(xml) if @location.present?
47
+ @phone.build_xml(xml) if @phone.present?
48
+ @fax.build_xml(xml) if @fax.present?
49
+ xml.CorreElectronico @email if @email.present?
50
+ end
51
+ end
52
+
53
+ def to_xml(builder)
54
+ build_xml(builder).to_xml
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,46 @@
1
+ require 'facturacr/document'
2
+
3
+ module FE
4
+ class Document
5
+ class Reference
6
+ include ActiveModel::Validations
7
+
8
+ attr_accessor :document_type, :number, :date, :code, :reason
9
+
10
+ REFERENCE_CODES = {
11
+ "01" => "Anula Documento de referencia",
12
+ "02" => "Corrige texto documento de referencia",
13
+ "03" => "Corrige monto",
14
+ "04" => "Referencia a otro documento",
15
+ "05" => "Sustituye comprobante provisional por contingencia",
16
+ "99" => "Otros"
17
+ }
18
+
19
+ validates :document_type, presence: true, inclusion: FE::Document::DOCUMENT_TYPES.keys
20
+ validates :number, presence: true, length: {is: 50}
21
+ validates :date, presence: true
22
+ validates :code, presence: true, length: {is: 2}, inclusion: REFERENCE_CODES.keys
23
+ validates :reason, presence: true, length: {maximum: 180}
24
+
25
+ def initialize(args={})
26
+ @document_type = args[:document_type]
27
+ @number = args[:number]
28
+ @date = args[:date]
29
+ @code = args[:code]
30
+ @reason = args[:reason]
31
+ end
32
+
33
+ def build_xml(node)
34
+ raise "Reference Invalid: #{errors.messages}" unless valid?
35
+ node = Nokogiri::XML::Builder.new if node.nil?
36
+ node.InformacionReferencia do |xml|
37
+ xml.TipoDoc @document_type
38
+ xml.Numero @number
39
+ xml.FechaEmision @date.xmlschema
40
+ xml.Codigo @code
41
+ xml.Razon @reason
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ module FE
2
+ class Document
3
+ class Regulation
4
+ include ActiveModel::Validations
5
+
6
+ attr_accessor :number, :date
7
+
8
+ validates :number, presence: true
9
+ validates :date, presence: true
10
+
11
+ def initialize(args={})
12
+ @number = args[:number] ||= "DGT-R-48-2016"
13
+ @date = args[:date] ||= "20-02-2017 13:22:22"
14
+ end
15
+
16
+ def build_xml(node)
17
+ raise "Regulation Invalid: #{errors.messages}" unless valid?
18
+ node = Nokogiri::XML::Builder.new if node.nil?
19
+ node.Normativa do |xml|
20
+ xml.NumeroResolucion @number
21
+ xml.FechaResolucion @date
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ module FE
2
+ class Document
3
+ class Summary
4
+ include ActiveModel::Validations
5
+
6
+ attr_accessor :currency, :exchange_rate, :services_taxable_total, :services_exent_total,
7
+ :goods_taxable_total,:goods_exent_total, :taxable_total, :exent_total,
8
+ :subtotal, :discount_total, :gross_total, :tax_total, :net_total
9
+
10
+ validates :exchange_rate, presence: true, if: -> { currency.present? }
11
+
12
+ validate :totals_ok?
13
+
14
+ def initialize(args={})
15
+ @currency = args[:currency]
16
+ @exchange_rate = args[:exchange_rate]
17
+ @services_taxable_total = args[:services_taxable_total].to_f
18
+ @services_exent_total = args[:services_exent_total].to_f
19
+ @goods_taxable_total = args[:goods_taxable_total].to_f
20
+ @goods_exent_total = args[:goods_exent_total].to_f
21
+ @taxable_total = args[:taxable_total].to_f
22
+ @exent_total = args[:exent_total].to_f
23
+ @subtotal = args[:subtotal].to_f
24
+ @discount_total = args[:discount_total].to_f
25
+ @gross_total = args[:gross_total].to_f
26
+ @tax_total = args[:tax_total].to_f
27
+ @net_total = args[:net_total].to_f
28
+ end
29
+
30
+ def build_xml(node)
31
+ unless valid?
32
+ raise "Summary invalid: #{errors.messages}"
33
+ end
34
+ node = Nokogiri::XML::Builder.new if node.nil?
35
+
36
+ node.ResumenFactura do |xml|
37
+ xml.CodigoMoneda @currency if @currency.present?
38
+ xml.TipoCambio @exchange_rate if @exchange_rate.present?
39
+ xml.TotalServGravados @services_taxable_total
40
+ xml.TotalServExentos @services_exent_total
41
+ xml.TotalMercanciasGravadas @goods_taxable_total
42
+ xml.TotalMercanciasExentas @goods_exent_total
43
+ xml.TotalGravado @taxable_total
44
+ xml.TotalExento @exent_total
45
+ xml.TotalVenta @subtotal
46
+ xml.TotalDescuentos @discount_total
47
+ xml.TotalVentaNeta @gross_total
48
+ xml.TotalImpuesto @tax_total
49
+ xml.TotalComprobante @net_total
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def totals_ok?
56
+ errors[:taxable_total] << "invalid amount" unless @taxable_total == @services_taxable_total + @goods_taxable_total
57
+ errors[:exent_total] << "invalid amount" unless @exent_total == @services_exent_total + @goods_exent_total
58
+ errors[:subtotal] << "invalid amount" unless @subtotal == @taxable_total + @exent_total
59
+ errors[:gross_total] << "invalid amount" unless @gross_total == @subtotal - @discount_total
60
+ errors[:net_total] << "invalid amount" unless @net_total == @gross_total + @tax_total
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ module FE
2
+ class Document
3
+ class Tax
4
+ include ActiveModel::Validations
5
+
6
+ TAX_CODES = {
7
+ "01"=>"Impuesto General sobre las Ventas",
8
+ "02"=>"Impuesto Selectivo de Consumo",
9
+ "03"=>"Impuesto Único a los combustibles",
10
+ "04"=>"Impuesto específico de bebidas alcohólicas",
11
+ "05"=>"Impuesto Específico sobre las bebidas envasadas sin contenido alcóholico y jabones de tocador",
12
+ "06"=>"Impuesto a los Productos de Tabaco",
13
+ "07"=>"Servicio",
14
+ "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
+ "99"=>"Otros"
21
+ }
22
+ attr_accessor :code, :rate, :total
23
+
24
+ validates :code, presence: true, inclusion: TAX_CODES.keys
25
+ def initialize(args={})
26
+ @code = args[:code]
27
+ @rate = args[:rate]
28
+ @total = args[:total]
29
+ end
30
+
31
+ def build_xml(node)
32
+ raise "Invalida Record: #{errors.messages}" unless valid?
33
+ node = Nokogiri::XML::Builder.new if node.nil?
34
+
35
+ node.Impuesto do |xml|
36
+ xml.Codigo @code
37
+ xml.Tarifa @rate
38
+ xml.Monto @total
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ require 'facturacr/document'
2
+
3
+ module FE
4
+
5
+ class Invoice < Document
6
+
7
+ def initialize(args={})
8
+ @date = args[:date]
9
+ @issuer = args[:issuer]
10
+ @receiver = args[:receiver]
11
+ @items = args[:items]
12
+ @number = args[:number]
13
+ @condition = args[:condition]
14
+ @payment_type = args[:payment_type] || "01"
15
+ @document_type = "01"
16
+ @credit_term = args[:credit_term]
17
+ @summary = args[:summary]
18
+ @regulation = args[:regulation] ||= FE::Document::Regulation.new
19
+ @security_code = args[:security_code]
20
+ @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
+ }
27
+ end
28
+
29
+ def document_tag
30
+ "FacturaElectronica"
31
+ end
32
+
33
+ end
34
+ end