class MCFDI::Invoice
Invoice
Class the most important class.
Attributes
addenda[RW]
canceled[RW]
certificate[RW]
certificate_number[RW]
complement[RW]
concepts[RW]
created_at[RW]
currency[RW]
exchange_rate[RW]
expedition_place[RW]
folio[RW]
payment_account_num[RW]
payment_conditions[RW]
payment_method[RW]
payment_way[RW]
proof_type[RW]
receptor[RW]
series[RW]
stamp[RW]
subtotal[RW]
taxes[RW]
total[RW]
transmitter[RW]
version[RW]
Public Class Methods
configure(options)
click to toggle source
to change default values.
# File lib/m_cfdi/invoice.rb, line 41 def self.configure(options) @@defaults = Invoice.rmerge(@@defaults, options) @@defaults end
new(args = {})
click to toggle source
# File lib/m_cfdi/invoice.rb, line 46 def initialize(args = {}) args = @@defaults.merge(args) args.each do |key, value| method = "#{key}=" next unless self.respond_to? method send(method, value) end end
Public Instance Methods
addenda=(addenda)
click to toggle source
# File lib/m_cfdi/invoice.rb, line 85 def addenda=(addenda) addenda = Addenda.new addenda unless addenda.is_a? Addenda @addenda = addenda end
attributes()
click to toggle source
return array of presented attributes. if any attribute does not have a value, will not be pushed to the array.
# File lib/m_cfdi/invoice.rb, line 115 def attributes a = [ @version, @created_at, @proof_type, @payment_way, @payment_conditions, @subtotal.to_f, @exchange_rate, @currency, @total.to_f, @payment_method, @expedition_place, @payment_account_num] c = [] a.each do |v| next unless v.present? c << v end c end
concepts=(data)
click to toggle source
# File lib/m_cfdi/invoice.rb, line 65 def concepts=(data) @concepts = [] if data.is_a? Array data.map do |c| c = Concept.new(c) unless c.is_a? Concept @concepts << c end elsif data.is_a? Hash @concepts << Concept.new(data) elsif data.is_a? Concept @concepts << data end @concepts end
created_at=(date)
click to toggle source
# File lib/m_cfdi/invoice.rb, line 80 def created_at=(date) date = date.strftime('%FT%R:%S') unless date.is_a? String @created_at = date end
original_string()
click to toggle source
Cadena Original
# File lib/m_cfdi/invoice.rb, line 129 def original_string params = [] attributes.each { |key| params << key } params += @transmitter.original_string params << @transmitter.fiscal_regime params += @receptor.original_string @concepts.each do |concept| params += concept.original_string end if @taxes.transferred.any? params += @taxes.transferred_original_string params += [@taxes.total_transferred] end if @taxes.detained.any? params += @taxes.detained_original_string params += [@taxes.total_detained] end params.select(&:present?) params.map! do |elem| if elem.is_a? Float elem = format('%.2f', elem) else elem = elem.to_s end elem end "||#{params.join('|')}||" end
original_string_from_xslt()
click to toggle source
save xml to file and generate original string from schema.
# File lib/m_cfdi/invoice.rb, line 91 def original_string_from_xslt # fail 'You have to specify schema!' unless # TODO: create configuration. xml = "#{Rails.root}/#{@transmitter.rfc}-#{@series}-#{@folio}.xml" save_xml(xml) sch = "#{Rails.root}/public/sat/schemas/cadenaoriginal_3_2.xslt" @original_string_from_xslt ||= `xsltproc #{sch} #{xml}` File.delete(xml) if File.exist?(xml) @original_string_from_xslt end
qr_code()
click to toggle source
return data of qr code image
# File lib/m_cfdi/invoice.rb, line 316 def qr_code t = "?re=#{@transmitter.rfc}" t += "&rr=#{@receptor.rfc}" t += "&tt=#{format('%6f', @total).rjust(17, '0')}" t += "&id=#{@complement.uuid}" img = RQRCode::QRCode.new(t).to_img img.resize(120, 120).to_data_url end
receptor=(data)
click to toggle source
# File lib/m_cfdi/invoice.rb, line 60 def receptor=(data) data = Entity.new(data) unless data.is_a? Entity @receptor = data end
save_xml(name)
click to toggle source
Save invoice xml to file
# File lib/m_cfdi/invoice.rb, line 102 def save_xml(name) file = File.new(name, 'w+') file.write(to_xml) file.close end
to_xml()
click to toggle source
return xml of the invoice.
# File lib/m_cfdi/invoice.rb, line 165 def to_xml ns = { 'xmlns:cfdi' => 'http://www.sat.gob.mx/cfd/3', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd', version: @version, folio: @folio, fecha: @created_at, formaDePago: @payment_way, subTotal: format('%.2f', @subtotal), Moneda: @currency, total: format('%.2f', @total), metodoDePago: @payment_method, tipoDeComprobante: @proof_type, LugarExpedicion: @expedition_place } ns[:condicionesDePago] = @payment_conditions if @payment_conditions.present? ns[:serie] = @series if @series ns[:TipoCambio] = @exchange_rate if @exchange_rate ns[:NumCtaPago] = @payment_account_num if @NumCtaPago.present? if @addenda ns["xmlns:#{@addenda.name}"] = @addenda.namespace ns['xsi:schemaLocation'] += ( ' ' + [@addenda.namespace, @addenda.xsd].join(' ')) end if @certificate_number ns[:noCertificado] = @certificate_number ns[:certificado] = @certificate end ns[:sello] = @stamp if @stamp @builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml| xml.Comprobante(ns) do ins = xml.doc.root.add_namespace_definition('cfdi', 'http://www.sat.gob.mx/cfd/3') xml.doc.root.namespace = ins xml.Emisor(@transmitter.to_x) do xml.DomicilioFiscal( @transmitter.address.to_x.reject { |_, v| !v.present? }) xml.ExpedidoEn( @transmitter.issued_in.to_x.reject { |_, v| !v.present? } ) if @transmitter.issued_in xml.RegimenFiscal(Regimen: @transmitter.fiscal_regime) end xml.Receptor(@receptor.to_x) do xml.Domicilio(@receptor.address.to_x.reject { |_, v| !v.present? }) end xml.Conceptos do @concepts.each do |concept| concept_complement = nil cc = concept.to_x.select { |_, v| v.present? } cc = cc.map do |k, v| v = format('%.2f', v) if v.is_a? Float [k, v] end.to_h xml.Concepto(cc) { xml.ComplementoConcepto if concept_complement } end end if @taxes.count > 0 tax_options = {} total_trans = format('%.2f', @taxes.total_transferred) total_detained = format('%.2f', @taxes.total_detained) tax_options[:totalImpuestosTrasladados] = total_trans if total_trans.to_i > 0 tax_options[:totalImpuestosRetenidos] = total_detained if total_detained.to_i > 0 xml.Impuestos(tax_options) do if @taxes.transferred.count > 0 xml.Traslados do @taxes.transferred.each do |trans| xml.Traslado(impuesto: trans.tax, tasa: format('%.2f', trans.rate), importe: format('%.2f', trans.import)) end end end if @taxes.detained.count > 0 xml.Retenciones do @taxes.detained.each do |det| xml.Retencion(impuesto: det.tax, tasa: format('%.2f', det.rate), importe: format('%.2f', det.import)) end end end end end xml.Complemento do if @complemento schema = 'http://www.sat.gob.mx/TimbreFiscalDigital '\ 'http://www.sat.gob.mx/TimbreFiscalDigital/'\ 'TimbreFiscalDigital.xsd' ns_tfd = { 'xsi:schemaLocation' => schema, 'xmlns:tfd' => 'http://www.sat.gob.mx/TimbreFiscalDigital', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' } xml['tfd'].TimbreFiscalDigital(@complemento.to_h.merge ns_tfd) end end if @addenda xml.Addenda do @addenda.data.each do |k, v| if v.is_a? Hash xml[@addenda.nombre].send(k, v) elsif v.is_a? Array xml[@addenda.nombre].send(k, v) else xml[@addenda.nombre].send(k, v) end end end end end end @builder.to_xml end
total_to_words()
click to toggle source
return string with total in words. Example: ( UN MIL CIENTO SESENTA PESOS 29/100 M.N. )
# File lib/m_cfdi/invoice.rb, line 310 def total_to_words decimal = format('%.2f', @total).split('.') "( #{@total.to_words.upcase} PESOS #{decimal[1]}/100 M.N. )" end
transmitter=(data)
click to toggle source
# File lib/m_cfdi/invoice.rb, line 55 def transmitter=(data) data = Entity.new(data) unless data.is_a? Entity @transmitter = data end
validate_original_string()
click to toggle source
Validate original string with the original string from schema
# File lib/m_cfdi/invoice.rb, line 109 def validate_original_string original_string == original_string_from_xslt end
validate_xml()
click to toggle source
validate generated xml with the schema
# File lib/m_cfdi/invoice.rb, line 294 def validate_xml errors = [] # schema_file = "#{Rails.root}/public/sat/schemas/cfdv32.xsd" # xsd = Nokogiri::XML::Schema(File.read(schema_file)) schema_file = 'http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd' xsd = Nokogiri::XML::Schema(open(schema_file)) doc = Nokogiri::XML(to_xml) xsd.validate(doc).each do |error| errors << error.message end errors end