Class: SunatInvoice::Invoice

Inherits:
Model
  • Object
show all
Includes:
Utils
Defined in:
lib/sunat_invoice/invoice.rb

Constant Summary collapse

UBL_VERSION =
'2.0'
CUSTOMIZATION =
'1.0'

Constants included from Utils

Utils::UBL_NAMESPACES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#amount_xml, #ubl_ext

Constructor Details

#initialize(*args) ⇒ Invoice

Returns a new instance of Invoice.



18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/sunat_invoice/invoice.rb', line 18

def initialize(*args)
  super(*args)
  opts = args[0] || {}
  @provider = opts[:provider] || SunatInvoice::Provider.new
  @customer = opts[:customer] || SunatInvoice::Customer.new
  @date = opts[:date] || DateTime.now.strftime('%Y-%m-%d')
  @document_type = opts[:document_type] || '01'
  @document_number = opts[:document_number] || 'F001-1'
  @currency = opts[:currency] || 'PEN'
  @items ||= []
  @signature = SunatInvoice::Signature.new(provider: @provider)
end

Instance Attribute Details

#document_numberObject

Returns the value of attribute document_number.



13
14
15
# File 'lib/sunat_invoice/invoice.rb', line 13

def document_number
  @document_number
end

#document_typeObject

Returns the value of attribute document_type.



13
14
15
# File 'lib/sunat_invoice/invoice.rb', line 13

def document_type
  @document_type
end

#itemsObject

Returns the value of attribute items.



13
14
15
# File 'lib/sunat_invoice/invoice.rb', line 13

def items
  @items
end

Instance Method Details

#add_item(item) ⇒ Object



132
133
134
# File 'lib/sunat_invoice/invoice.rb', line 132

def add_item(item)
  items << item if item.is_a?(SunatInvoice::Item)
end

#attributesObject



158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/sunat_invoice/invoice.rb', line 158

def attributes
  {
    attributes!: {
      'cbc:InvoicedQuantity' => { '@unitCode' => :unit },
      'cbc:PriceAmount' => { '@currencyID' => :currency }, # PEN
      'cbc:TaxAmount' => { '@currencyID' => :currency }, # PEN
      'cbc:PayableAmount' => { '@currencyID' => :currency }, # PEN
      'cbc:LineExtensionAmount' => { '@currencyID' => :currency }, # PEN
      'cbc:ChargeTotalAmount' => { '@currencyID' => :currency }, # PEN
    }
  }
end

#build_items(xml) ⇒ Object



97
98
99
100
101
# File 'lib/sunat_invoice/invoice.rb', line 97

def build_items(xml)
  items.each_with_index do |item, index|
    item.xml(xml, index, @currency)
  end
end

#build_sale_totals(xml) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/sunat_invoice/invoice.rb', line 113

def build_sale_totals(xml)
  ubl_ext(xml) do
    xml['sac'].AdditionalInformation do
      @sale_totals.each do |code, amount|
        xml['sac'].AdditionalMonetaryTotal do
          xml['cbc'].ID code
          amount_xml(xml['cbc'], 'PayableAmount', amount, @currency)
        end
      end
    end
  end
end

#build_tax_totals(xml) ⇒ Object



107
108
109
110
111
# File 'lib/sunat_invoice/invoice.rb', line 107

def build_tax_totals(xml)
  @tax_totals.each do |key, value|
    SunatInvoice::Tax.new(tax_type: key, amount: value).xml(xml, @currency)
  end
end

#build_total(xml) ⇒ Object



126
127
128
129
130
# File 'lib/sunat_invoice/invoice.rb', line 126

def build_total(xml)
  xml['cac'].LegalMonetaryTotal do
    amount_xml(xml['cbc'], 'PayableAmount', @total, @currency)
  end
end

#calculate_sale_totalsObject



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/sunat_invoice/invoice.rb', line 73

def calculate_sale_totals
  @sale_totals = {}
  # get bi totals according kind of sale (gravado, inafecto, exonerado ..)
  items.each do |item|
    # TODO: I think in most cases only be one tax for item, but should
    #       handle more cases
    total_code = get_total_code(item.taxes.first)
    if total_code
      @sale_totals[total_code] = 0 unless @sale_totals[total_code]
      @sale_totals[total_code] += item.bi_value
    end
  end
end

#calculate_tax_totalsObject



87
88
89
90
91
92
93
94
95
# File 'lib/sunat_invoice/invoice.rb', line 87

def calculate_tax_totals
  # concat item's sale_taxes
  @tax_totals = {}
  taxes = items.map(&:sale_taxes).flatten
  taxes.each do |tax|
    @tax_totals[tax.keys.first] ||= 0
    @tax_totals[tax.keys.first] += tax.values.sum
  end
end

#calculate_totalObject



66
67
68
69
70
71
# File 'lib/sunat_invoice/invoice.rb', line 66

def calculate_total
  # calculate invoice total
  @total = 0
  @total += @tax_totals.values.sum
  @total += @sale_totals.values.sum
end

#document_nameObject



103
104
105
# File 'lib/sunat_invoice/invoice.rb', line 103

def document_name
  "#{@provider.ruc}-#{document_type}-#{document_number}"
end

#get_total_code(tax) ⇒ Object



136
137
138
139
140
141
142
143
144
# File 'lib/sunat_invoice/invoice.rb', line 136

def get_total_code(tax)
  return unless tax
  case tax.tax_type
  when :igv
    get_total_igv_code(tax.tax_exemption_reason)
  # when :isc
  #   tax.tier_range
  end
end

#get_total_igv_code(exemption_reason) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/sunat_invoice/invoice.rb', line 146

def get_total_igv_code(exemption_reason)
  if Catalogs::CATALOG_07.first == exemption_reason
    Catalogs::CATALOG_14.first
  elsif Catalogs::CATALOG_07[1..6].include?(exemption_reason)
    Catalogs::CATALOG_14[3]
  elsif Catalogs::CATALOG_07[7] == exemption_reason
    Catalogs::CATALOG_14[2]
  elsif Catalogs::CATALOG_07[8..14].include?(exemption_reason)
    Catalogs::CATALOG_14[1]
  end
end

#prepare_totalsObject



60
61
62
63
64
# File 'lib/sunat_invoice/invoice.rb', line 60

def prepare_totals
  calculate_tax_totals
  calculate_sale_totals
  calculate_total
end

#xmlObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/sunat_invoice/invoice.rb', line 31

def xml
  prepare_totals

  build = Nokogiri::XML::Builder.new do |xml|
    xml.Invoice(UBL_NAMESPACES) do
      xml['ext'].UBLExtensions do
        build_sale_totals(xml)
        @signature.signature_ext(xml)
      end

      xml['cbc'].UBLVersionID UBL_VERSION
      xml['cbc'].CustomizationID CUSTOMIZATION
      xml['cbc'].ID @document_number
      xml['cbc'].IssueDate @date
      xml['cbc'].InvoiceTypeCode @document_type
      xml['cbc'].DocumentCurrencyCode @currency

      @signature.signer_data(xml)
      @provider.info(xml)
      @customer.info(xml)
      build_tax_totals(xml)
      build_total(xml)
      build_items(xml)
    end
  end
  invoice_xml = build.to_xml
  @signature.sign(invoice_xml)
end