Class: Cfdi40::Comprobante
Overview
root node
Instance Attribute Summary collapse
-
#cadena_original ⇒ Object
readonly
Returns the value of attribute cadena_original.
-
#cfdi_relacionados ⇒ Object
readonly
Returns the value of attribute cfdi_relacionados.
-
#conceptos ⇒ Object
readonly
Returns the value of attribute conceptos.
-
#emisor ⇒ Object
readonly
Returns the value of attribute emisor.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#key_data ⇒ Object
writeonly
Sets the attribute key_data.
-
#key_pass ⇒ Object
writeonly
Sets the attribute key_pass.
-
#loaded_xml ⇒ Object
Returns the value of attribute loaded_xml.
-
#namespace_pagos_on_root ⇒ Object
writeonly
Sets the attribute namespace_pagos_on_root.
-
#private_key ⇒ Object
readonly
Returns the value of attribute private_key.
-
#receptor ⇒ Object
readonly
Returns the value of attribute receptor.
-
#sat_csd ⇒ Object
readonly
Returns the value of attribute sat_csd.
Attributes inherited from Node
#children_nodes, #element_name, #parent_node, #readonly, #xml_document, #xml_parent
Instance Method Summary collapse
- #add_cfdi_relacionado(tipo_relacion, uuid) ⇒ Object
-
#add_concepto(attributes = {}) ⇒ Object
## Required attributes.
-
#add_namespace_pagos_to_root ⇒ Object
Some PACs require that the namespace pago20 be placed in root node.
-
#add_pago(attributes = {}) ⇒ Object
TODO: Doc params add_pago monto uuid folio serie num_parcialidad fecha_pago forma_pago importe_saldo_anterior objeto_impuestos.
-
#add_splitted_pago(attributes = {}) ⇒ Object
See test_adding_pago_with_n_docto_relacionados in file test/test_cfdi40_rep.rb.
- #calculate! ⇒ Object
- #cert_der=(cert_data) ⇒ Object
-
#cert_path=(path) ⇒ Object
Accept a path to read the certificate.
- #cfdi_relacionados_nodes ⇒ Object
- #concepto_nodes ⇒ Object
-
#initialize ⇒ Comprobante
constructor
A new instance of Comprobante.
- #key_path=(path) ⇒ Object
-
#load_cert ⇒ Object
Load from attribute ‘Certificado’ when the CFDi is loaded from a string.
-
#load_concepto(ng_node) ⇒ Object
Load node ‘Concepto’ from a Nokogiri::XML::Element.
-
#load_concepto_rep(ng_node) ⇒ Object
Load node ‘Concepto’ (rep) from a Nokogiri::XML::Element.
-
#load_impuestos(ng_node) ⇒ Object
Load node cfdi:Comprobante/cfdi:Impuestos.
- #load_pagos(pagos_node) ⇒ Object
- #load_tfd(tfd_node) ⇒ Object
- #original_content ⇒ Object
- #pago_nodes ⇒ Object
- #remove_cfdi_relacionado(index) ⇒ Object
- #remove_pago(index) ⇒ Object
- #sign ⇒ Object
- #signed? ⇒ Boolean
- #timbre ⇒ Object
- #to_s ⇒ Object
- #to_xml ⇒ Object
-
#total_impuestos_trasladados ⇒ Object
Shortcut to attribute TotalImpuestosTrasladados of impuestos node.
- #total_iva ⇒ Object
- #total_iva_node ⇒ Object
- #valid? ⇒ Boolean
- #valid_signature? ⇒ Boolean
Methods inherited from Node
#add_attributes_to, #add_child_node, #add_children_to, #add_namespaces_to, #attibute_is_null?, attributes, #clean_cached_xml, #create_xml_node, #current_namespace, default_values, define_attribute, define_element_name, define_namespace, define_reader, define_writer, #delete_child, element_name, #expanded_element_name, formats, #formatted_value, #load_from_ng_node, #lock, namespaces, #set_defaults, verify_class_variables
Constructor Details
#initialize ⇒ Comprobante
Returns a new instance of Comprobante.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/cfdi40/comprobante.rb', line 38 def initialize super @errors = [] @conceptos = Conceptos.new @conceptos.parent_node = self @emisor = Emisor.new @emisor.parent_node = self @receptor = Receptor.new @receptor.parent_node = self @sat_csd = SatCsd.new @fecha ||= Time.now @children_nodes = [@emisor, @receptor, @conceptos] @cfdi_relacionados = [] @namespace_pagos_on_root = false set_defaults end |
Instance Attribute Details
#cadena_original ⇒ Object (readonly)
Returns the value of attribute cadena_original.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def cadena_original @cadena_original end |
#cfdi_relacionados ⇒ Object (readonly)
Returns the value of attribute cfdi_relacionados.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def cfdi_relacionados @cfdi_relacionados end |
#conceptos ⇒ Object (readonly)
Returns the value of attribute conceptos.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def conceptos @conceptos end |
#emisor ⇒ Object (readonly)
Returns the value of attribute emisor.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def emisor @emisor end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def errors @errors end |
#key_data=(value) ⇒ Object (writeonly)
Sets the attribute key_data
35 36 37 |
# File 'lib/cfdi40/comprobante.rb', line 35 def key_data=(value) @key_data = value end |
#key_pass=(value) ⇒ Object (writeonly)
Sets the attribute key_pass
35 36 37 |
# File 'lib/cfdi40/comprobante.rb', line 35 def key_pass=(value) @key_pass = value end |
#loaded_xml ⇒ Object
Returns the value of attribute loaded_xml.
36 37 38 |
# File 'lib/cfdi40/comprobante.rb', line 36 def loaded_xml @loaded_xml end |
#namespace_pagos_on_root=(value) ⇒ Object (writeonly)
Sets the attribute namespace_pagos_on_root
35 36 37 |
# File 'lib/cfdi40/comprobante.rb', line 35 def namespace_pagos_on_root=(value) @namespace_pagos_on_root = value end |
#private_key ⇒ Object (readonly)
Returns the value of attribute private_key.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def private_key @private_key end |
#receptor ⇒ Object (readonly)
Returns the value of attribute receptor.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def receptor @receptor end |
#sat_csd ⇒ Object (readonly)
Returns the value of attribute sat_csd.
34 35 36 |
# File 'lib/cfdi40/comprobante.rb', line 34 def sat_csd @sat_csd end |
Instance Method Details
#add_cfdi_relacionado(tipo_relacion, uuid) ⇒ Object
221 222 223 224 225 226 227 228 229 230 |
# File 'lib/cfdi40/comprobante.rb', line 221 def add_cfdi_relacionado(tipo_relacion, uuid) cfdi_relacionados_node = CfdiRelacionados.new cfdi_relacionados_node.tipo_relacion = tipo_relacion cfdi_relacionados_node.parent_node = self cfdi_relacionados_node.add_cfdi(uuid) @children_nodes << cfdi_relacionados_node @cfdi_relacionados ||= [] @cfdi_relacionados << cfdi_relacionados_node cfdi_relacionados_node end |
#add_concepto(attributes = {}) ⇒ Object
## Required attributes
clave_prod_serv-
From SAT catalogue
clave_unidad-
From SAT catalogue
cantidad-
Must be greather than 0
descripcion-
Product or service description
### Price and Taxes attributes
tasa_iva-
Decimal between 0 and 1. Nil means exempt. Default value is 0.16
tasa_ieps-
Decimal between 0 and 1. Nil means exempt. Default value is null
precio_bruto-
Price before apply taxes or gross price. All quantities are calculated based on this price and taxes rate.
precio_neto-
Precio after taxes or net price. All quantities are calculated from this prices. When both,
precio_netoandprecio_brutoexist,precio_netois used
The most common usage requires only the net price (precio_neto).
## Optional attributes:
no_identificacionunidaddescuento-
PENDING
## Special attributes
### IEDU attributes
IEDU node (path: cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto/cfdi:ComplementoConcepto/iedu:instEducativas) is generated when one of iedu_nombre_alumno, iedu_curp, iedu_nivel_educativo exist.
iedu_nombre_alumnoiedu_curpiedu_nivel_educativoiedu_aut_rvoeiedu_rfc_pago
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/cfdi40/comprobante.rb', line 139 def add_concepto(attributes = {}) raise Error, "CFDi tipo pago no acepta conceptos" if tipo_de_comprobante == "P" concepto = Concepto.new concepto.parent_node = @conceptos attributes.each do |key, value| method_name = "#{key}=".to_sym raise Error, ":#{key} no se puede asignar al concepto" unless concepto.respond_to?(method_name) concepto.public_send(method_name, value) end concepto.calculate! @conceptos.children_nodes << concepto calculate! concepto end |
#add_namespace_pagos_to_root ⇒ Object
Some PACs require that the namespace pago20 be placed in root node
343 344 345 346 347 348 |
# File 'lib/cfdi40/comprobante.rb', line 343 def add_namespace_pagos_to_root self.class.define_namespace "pago20", "http://www.sat.gob.mx/Pagos20" @schema_location += " http://www.sat.gob.mx/Pagos20 " \ "http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd" true end |
#add_pago(attributes = {}) ⇒ Object
TODO: Doc params add_pago monto uuid folio serie num_parcialidad fecha_pago forma_pago importe_saldo_anterior objeto_impuestos
199 200 201 202 203 204 |
# File 'lib/cfdi40/comprobante.rb', line 199 def add_pago(attributes = {}) raise Error, "CFDi debe ser tipo 'P'" unless tipo_de_comprobante == "P" add_node_concepto_actividad_pago complemento.add_pago(attributes) end |
#add_splitted_pago(attributes = {}) ⇒ Object
See test_adding_pago_with_n_docto_relacionados in file test/test_cfdi40_rep.rb
214 215 216 217 218 219 |
# File 'lib/cfdi40/comprobante.rb', line 214 def add_splitted_pago(attributes = {}) raise Error, "CFDi debe ser tipo 'P'" unless tipo_de_comprobante == "P" add_node_concepto_actividad_pago complemento.add_splitted_pago(attributes) end |
#calculate! ⇒ Object
291 292 293 294 295 296 297 298 299 |
# File 'lib/cfdi40/comprobante.rb', line 291 def calculate! return false if readonly @docxml = nil @subtotal = @conceptos.children_nodes.map(&:importe).map(&:to_f).sum @total = @conceptos.children_nodes.map(&:importe_neto).map(&:to_f).sum add_traslados_summary_node true end |
#cert_der=(cert_data) ⇒ Object
62 63 64 65 66 67 68 69 70 |
# File 'lib/cfdi40/comprobante.rb', line 62 def cert_der=(cert_data) @sat_csd ||= SatCsd.new @sat_csd.cert_der = cert_data emisor.rfc = @sat_csd.rfc emisor.nombre ||= @sat_csd.name @no_certificado = @sat_csd.no_certificado @certificado = @sat_csd.cert64 true end |
#cert_path=(path) ⇒ Object
Accept a path to read the certificate. Certificate is a X509 file. SAT generates those files in DER format.
58 59 60 |
# File 'lib/cfdi40/comprobante.rb', line 58 def cert_path=(path) self.cert_der = File.read(path) end |
#cfdi_relacionados_nodes ⇒ Object
232 233 234 |
# File 'lib/cfdi40/comprobante.rb', line 232 def cfdi_relacionados_nodes @cfdi_relacionados end |
#concepto_nodes ⇒ Object
301 302 303 |
# File 'lib/cfdi40/comprobante.rb', line 301 def concepto_nodes @conceptos.children_nodes end |
#key_path=(path) ⇒ Object
72 73 74 |
# File 'lib/cfdi40/comprobante.rb', line 72 def key_path=(path) @key_data = File.read(path) end |
#load_cert ⇒ Object
Load from attribute ‘Certificado’ when the CFDi is loaded from a string
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/cfdi40/comprobante.rb', line 78 def load_cert return if @sat_csd&.cert64 @sat_csd ||= SatCsd.new @sat_csd.cert_der = OpenSSL::X509::Certificate.new(Base64.decode64(certificado)) true rescue StandardError # puts "Waring; Unable to load certificate from XML string" false end |
#load_concepto(ng_node) ⇒ Object
Load node ‘Concepto’ from a Nokogiri::XML::Element
157 158 159 160 161 162 163 164 |
# File 'lib/cfdi40/comprobante.rb', line 157 def load_concepto(ng_node) concepto = Concepto.new concepto.parent_node = @conceptos concepto.load_from_ng_node(ng_node) concepto.precio_bruto = concepto.valor_unitario.to_f @conceptos.children_nodes << concepto concepto end |
#load_concepto_rep(ng_node) ⇒ Object
Load node ‘Concepto’ (rep) from a Nokogiri::XML::Element
167 168 169 170 171 172 173 174 |
# File 'lib/cfdi40/comprobante.rb', line 167 def load_concepto_rep(ng_node) concepto = ConceptoRep.new concepto.parent_node = @conceptos concepto.load_from_ng_node(ng_node) concepto.precio_bruto = concepto.valor_unitario.to_f @conceptos.children_nodes << concepto concepto end |
#load_impuestos(ng_node) ⇒ Object
Load node cfdi:Comprobante/cfdi:Impuestos
Normally this node is calculated but must be read from the XML when a CFDi is loaded
180 181 182 183 184 185 186 187 |
# File 'lib/cfdi40/comprobante.rb', line 180 def load_impuestos(ng_node) impuestos.load_from_ng_node(ng_node) ng_iva_node = ng_node.xpath("cfdi:Traslados/cfdi:Traslado[@Impuesto='002']").first return true if ng_iva_node.nil? impuestos.traslado_iva.load_from_ng_node(ng_iva_node) true end |
#load_pagos(pagos_node) ⇒ Object
332 333 334 |
# File 'lib/cfdi40/comprobante.rb', line 332 def load_pagos(pagos_node) complemento.load_pagos(pagos_node) end |
#load_tfd(tfd_node) ⇒ Object
324 325 326 327 328 329 330 |
# File 'lib/cfdi40/comprobante.rb', line 324 def load_tfd(tfd_node) timbre = Cfdi40::Timbre.new timbre.load_from_ng_node(tfd_node) timbre.parent_node = complemento complemento.children_nodes << timbre timbre end |
#original_content ⇒ Object
278 279 280 281 282 |
# File 'lib/cfdi40/comprobante.rb', line 278 def original_content xml_string = loaded_xml.nil? ? docxml.to_s : loaded_xml Cfdi40::OriginalContent.generate(xml_string) end |
#pago_nodes ⇒ Object
305 306 307 308 309 |
# File 'lib/cfdi40/comprobante.rb', line 305 def pago_nodes return [] unless defined?(@complemento) complemento.pago_nodes end |
#remove_cfdi_relacionado(index) ⇒ Object
236 237 238 239 240 241 242 243 244 |
# File 'lib/cfdi40/comprobante.rb', line 236 def remove_cfdi_relacionado(index) return if @cfdi_relacionados.empty? nodo = @cfdi_relacionados[index.to_i] return unless nodo delete_child(nodo) @cfdi_relacionados.delete_at(index.to_i) end |
#remove_pago(index) ⇒ Object
206 207 208 209 210 211 |
# File 'lib/cfdi40/comprobante.rb', line 206 def remove_pago(index) return unless defined?(@complemento) return if complemento.pagos.pago_nodes.empty? complemento.pagos.remove_pago(index.to_i) end |
#sign ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/cfdi40/comprobante.rb', line 89 def sign @sat_csd ||= SatCsd.new load_private_key if @sat_csd.private_key.nil? return unless @sat_csd.private_key raise Error, "Key and certificate not match" unless @sat_csd.valid_pair? @cadena_original = original_content digest = @sat_csd.private_key.sign(OpenSSL::Digest.new("SHA256"), @cadena_original) @sello = Base64.strict_encode64 digest lock @docxml = nil end |
#signed? ⇒ Boolean
274 275 276 |
# File 'lib/cfdi40/comprobante.rb', line 274 def signed? !docxml.root.attributes["Sello"].nil? end |
#timbre ⇒ Object
336 337 338 339 340 |
# File 'lib/cfdi40/comprobante.rb', line 336 def timbre return nil unless defined?(@complemento) complemento.timbre end |
#to_s ⇒ Object
246 247 248 |
# File 'lib/cfdi40/comprobante.rb', line 246 def to_s to_xml end |
#to_xml ⇒ Object
250 251 252 253 254 255 256 257 |
# File 'lib/cfdi40/comprobante.rb', line 250 def to_xml return loaded_xml if !loaded_xml.nil? && signed? sign unless signed? return xml_string_ns_pagos_on_root if @namespace_pagos_on_root && pago_nodes.count > 0 docxml.to_xml end |
#total_impuestos_trasladados ⇒ Object
Shortcut to attribute TotalImpuestosTrasladados of impuestos node
285 286 287 288 289 |
# File 'lib/cfdi40/comprobante.rb', line 285 def total_impuestos_trasladados return nil unless impuestos_node impuestos_node.total_impuestos_trasladados end |
#total_iva ⇒ Object
318 319 320 321 322 |
# File 'lib/cfdi40/comprobante.rb', line 318 def total_iva return 0 unless traslados traslados.traslados_iva.map(&:importe).map(&:to_f).sum.round(2) end |
#total_iva_node ⇒ Object
311 312 313 314 315 316 |
# File 'lib/cfdi40/comprobante.rb', line 311 def total_iva_node # TODO: Puede haber más de un nodo, cuando hay varias tasas de iva return nil unless impuestos_node impuestos_node.traslado_iva end |
#valid? ⇒ Boolean
259 260 261 262 263 264 265 |
# File 'lib/cfdi40/comprobante.rb', line 259 def valid? schema_validator = SchemaValidator.new(to_s) return true if schema_validator.valid? @errors = schema_validator.errors @errors.empty? end |
#valid_signature? ⇒ Boolean
267 268 269 270 271 272 |
# File 'lib/cfdi40/comprobante.rb', line 267 def valid_signature? return false unless signed? signature_validator = SignatureValidator.new(to_xml) signature_validator.valid? end |