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.
- checksums.yaml +5 -5
- data/facturacr.gemspec +2 -2
- data/lib/facturacr.rb +25 -19
- data/lib/facturacr/api.rb +5 -4
- data/lib/facturacr/builder.rb +4 -4
- data/lib/facturacr/configuration.rb +18 -9
- data/lib/facturacr/credit_note.rb +24 -13
- data/lib/facturacr/data.rb +41 -0
- data/lib/facturacr/data_provider.rb +1 -1
- data/lib/facturacr/debit_note.rb +25 -13
- data/lib/facturacr/document.rb +120 -69
- data/lib/facturacr/document/exoneration.rb +17 -12
- data/lib/facturacr/document/fax.rb +1 -4
- data/lib/facturacr/document/identification_document.rb +7 -10
- data/lib/facturacr/document/issuer.rb +31 -32
- data/lib/facturacr/document/item.rb +77 -20
- data/lib/facturacr/document/location.rb +19 -23
- data/lib/facturacr/document/other_charges.rb +56 -0
- data/lib/facturacr/document/other_content.rb +30 -0
- data/lib/facturacr/document/other_content/price_smart.rb +58 -0
- data/lib/facturacr/document/other_text.rb +4 -8
- data/lib/facturacr/document/phone.rb +1 -3
- data/lib/facturacr/document/phone_type.rb +6 -9
- data/lib/facturacr/document/receiver.rb +28 -19
- data/lib/facturacr/document/reference.rb +36 -18
- data/lib/facturacr/document/regulation.rb +3 -3
- data/lib/facturacr/document/summary.rb +54 -22
- data/lib/facturacr/document/tax.rb +43 -21
- data/lib/facturacr/element.rb +9 -0
- data/lib/facturacr/error.rb +11 -0
- data/lib/facturacr/export_invoice.rb +38 -0
- data/lib/facturacr/invoice.rb +27 -15
- data/lib/facturacr/purchase_invoice.rb +39 -0
- data/lib/facturacr/reception_message.rb +73 -28
- data/lib/facturacr/signed_document.rb +4 -4
- data/lib/facturacr/signer/signer.rb +20 -8
- data/lib/facturacr/ticket.rb +27 -14
- data/lib/facturacr/version.rb +1 -1
- data/lib/facturacr/xml_document.rb +46 -29
- metadata +15 -9
@@ -1,25 +1,28 @@
|
|
1
1
|
module FE
|
2
2
|
class Document
|
3
|
-
class Exoneration
|
3
|
+
class Exoneration < Element
|
4
4
|
include ActiveModel::Validations
|
5
5
|
|
6
6
|
|
7
7
|
DOCUMENT_TYPES = {
|
8
8
|
"01" => "Compras Autorizadas",
|
9
9
|
"02" => "Ventas exentas a diplomáticos",
|
10
|
-
"03" => "
|
10
|
+
"03" => "Autorizado por Ley especial",
|
11
11
|
"04" => "Exenciones Dirección General de Hacienda",
|
12
|
-
"05" => "
|
12
|
+
"05" => "Transitorio V",
|
13
|
+
"06" => "Transitorio IX",
|
14
|
+
"07" => "Transitorio XVII",
|
13
15
|
"99" => "Otros"
|
14
16
|
}.freeze
|
15
17
|
attr_accessor :document_type, :document_number, :institution, :date, :total_tax, :percentage, :net_total
|
16
18
|
|
17
19
|
validates :document_type, presence: true, inclusion: DOCUMENT_TYPES.keys
|
18
|
-
validates :document_number, presence: true
|
19
|
-
validates :institution, presence: true
|
20
|
+
validates :document_number, presence: true, length: { maximum: 40 }
|
21
|
+
validates :institution, presence: true, length: { maximum: 160 }
|
20
22
|
validates :date, presence: true
|
21
23
|
validates :total_tax,presence: true
|
22
|
-
validates :percentage, presence: true
|
24
|
+
validates :percentage, presence: true, numericality: {greater_than_or_equal_to: 0, less_than_or_equal_to: 100, only_integer: true}
|
25
|
+
|
23
26
|
|
24
27
|
def initialize(args={})
|
25
28
|
@document_type = args[:document_type]
|
@@ -27,11 +30,12 @@ module FE
|
|
27
30
|
@institution = args[:institution]
|
28
31
|
@date = args[:date]
|
29
32
|
@total_tax = args[:total_tax]
|
30
|
-
@percentage =
|
33
|
+
@percentage = args[:percentage]
|
31
34
|
end
|
32
35
|
|
33
|
-
def build_xml(node)
|
34
|
-
|
36
|
+
def build_xml(node, document)
|
37
|
+
@document = document
|
38
|
+
raise FE::Error.new("invalid exoneration",class: self.class, messages: errors.messages) unless valid?
|
35
39
|
node = Nokogiri::XML::Builder.new if node.nil?
|
36
40
|
|
37
41
|
node.Exoneracion do |xml|
|
@@ -39,11 +43,12 @@ module FE
|
|
39
43
|
xml.NumeroDocumento @document_number
|
40
44
|
xml.NombreInstitucion @institution
|
41
45
|
xml.FechaEmision @date.xmlschema
|
42
|
-
xml.
|
43
|
-
xml.
|
46
|
+
xml.PorcentajeExoneracion @percentage
|
47
|
+
xml.MontoExoneracion @total_tax
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
51
|
+
|
47
52
|
end
|
48
53
|
end
|
49
|
-
end
|
54
|
+
end
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require "facturacr/document"
|
2
|
-
require 'active_model'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
1
|
module FE
|
6
2
|
class Document
|
7
3
|
|
@@ -20,20 +16,21 @@ module FE
|
|
20
16
|
|
21
17
|
@document_type = args[:type]
|
22
18
|
@raw_id_number = args[:number]
|
23
|
-
@
|
24
|
-
|
19
|
+
if @raw_id_number
|
20
|
+
@id_number = "%012d" % args[:number]
|
21
|
+
end
|
25
22
|
end
|
26
23
|
|
27
|
-
def build_xml(node)
|
28
|
-
raise "
|
24
|
+
def build_xml(node, document)
|
25
|
+
raise FE::Error.new("invalid identification document", class: self.class, messages: errors.messages) unless valid?
|
29
26
|
node = Nokogiri::XML::Builder.new if node.nil?
|
30
27
|
node.Identificacion do |x|
|
31
28
|
x.Tipo document_type
|
32
29
|
x.Numero raw_id_number
|
33
30
|
end
|
34
31
|
end
|
35
|
-
def to_xml(builder)
|
36
|
-
build_xml(builder).to_xml
|
32
|
+
def to_xml(builder, document)
|
33
|
+
build_xml(builder,document).to_xml
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
@@ -1,22 +1,25 @@
|
|
1
|
-
require "facturacr/document"
|
2
|
-
require 'active_model'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
1
|
module FE
|
6
2
|
class Document
|
7
|
-
|
8
|
-
|
9
|
-
class Issuer
|
3
|
+
|
4
|
+
|
5
|
+
class Issuer < Element
|
10
6
|
include ActiveModel::Validations
|
11
|
-
|
7
|
+
|
12
8
|
attr_accessor :name, :identification_document, :comercial_name, :location, :phone, :fax, :email
|
13
|
-
|
14
|
-
validates :name, presence: true
|
9
|
+
|
10
|
+
validates :name, presence: true
|
15
11
|
validates :identification_document, presence: true
|
16
|
-
validates :comercial_name, length: {maximum: 80}
|
17
12
|
validates :location, presence: true
|
18
|
-
validates :email, presence: true, format:
|
13
|
+
validates :email, presence: true,length: {maximum: 160}, format:{with: /\s*\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\s*/}
|
19
14
|
|
15
|
+
validates :name, length: { maximum: 80}, if: ->{ document.version_42? }
|
16
|
+
validates :comercial_name, length: { maximum: 80 }, if: ->{ document.version_42? }
|
17
|
+
|
18
|
+
validates :name, length: { maximum: 100}, if: ->{ document.version_43? }
|
19
|
+
validates :comercial_name, length: { maximum: 100 }, if: ->{ document.version_43? }
|
20
|
+
|
21
|
+
|
22
|
+
|
20
23
|
def initialize(args={})
|
21
24
|
@name = args[:name]
|
22
25
|
@identification_document = args[:identification_document]
|
@@ -24,33 +27,29 @@ module FE
|
|
24
27
|
@location = args[:location]
|
25
28
|
@phone = args[:phone]
|
26
29
|
@fax = args[:fax]
|
27
|
-
@email = args[:email]
|
30
|
+
@email = args[:email]
|
28
31
|
end
|
29
|
-
|
30
|
-
def build_xml(node)
|
31
|
-
|
32
|
-
raise "
|
33
|
-
|
34
|
-
raise "Fax is invalid" if !@fax.nil? && !@fax.is_a?(Fax)
|
35
|
-
|
36
|
-
raise "Issuer is invalid: #{errors.messages}" unless valid?
|
37
|
-
|
32
|
+
|
33
|
+
def build_xml(node, document)
|
34
|
+
@document = document
|
35
|
+
raise FE::Error.new("issuer invalid",class: self.class, messages: errors.messages) unless valid?
|
36
|
+
|
38
37
|
node = Nokogiri::XML::Builder.new if node.nil?
|
39
38
|
node.Emisor do |xml|
|
40
39
|
xml.Nombre @name
|
41
|
-
identification_document.build_xml(xml)
|
40
|
+
identification_document.build_xml(xml,document)
|
42
41
|
xml.NombreComercial @comercial_name if @comercial_name
|
43
|
-
location.build_xml(xml)
|
44
|
-
phone.build_xml(xml) if phone.present?
|
45
|
-
fax.build_xml(xml) if fax.present?
|
42
|
+
location.build_xml(xml, document)
|
43
|
+
phone.build_xml(xml, document) if phone.present?
|
44
|
+
fax.build_xml(xml, document) if fax.present?
|
46
45
|
xml.CorreoElectronico @email
|
47
|
-
end
|
46
|
+
end
|
48
47
|
end
|
49
|
-
|
50
|
-
def to_xml(builder)
|
51
|
-
build_xml(builder).to_xml
|
48
|
+
|
49
|
+
def to_xml(builder,document)
|
50
|
+
build_xml(builder,document).to_xml
|
52
51
|
end
|
53
52
|
end
|
54
|
-
|
53
|
+
|
55
54
|
end
|
56
|
-
end
|
55
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module FE
|
2
2
|
class Document
|
3
|
-
class Item
|
3
|
+
class Item < Element
|
4
4
|
include ActiveModel::Validations
|
5
5
|
|
6
|
-
UNITS = %w[Sp m kg s A K mol cd m² m³ m/s m/s² 1/m kg/m³ A/m² A/m mol/m³ cd/m² 1 rad sr Hz N Pa J W C V F Ω S Wb T H °C lm
|
6
|
+
UNITS = %w[ Al Alc Cm I Os Spe St Sp m kg s A K mol cd m² m³ m/s m/s² 1/m kg/m³ A/m² A/m mol/m³ cd/m² 1 rad sr Hz N Pa J W C V F Ω S Wb T H °C lm
|
7
7
|
lx Bq Gy Sv kat Pa·s N·m N/m rad/s rad/s² W/m² J/K J/(kg·K) J/kg W/(m·K) J/m³ V/m C/m³ C/m² F/m H/m J/mol J/(mol·K)
|
8
8
|
C/kg Gy/s W/sr W/(m²·sr) kat/m³ min h d º ´ ´´ L t Np B eV u ua Unid Gal g Km ln cm mL mm Oz Otros].freeze
|
9
|
-
|
9
|
+
SERVICE_UNITS = %w[Al Alc Os Spe Sp St min h I Cm]
|
10
10
|
CODE_TYPES = {
|
11
11
|
'01' => 'Código del producto del vendedor',
|
12
12
|
'02' => 'Código del producto del comprador',
|
@@ -15,26 +15,35 @@ module FE
|
|
15
15
|
'99' => 'Otros'
|
16
16
|
}.freeze
|
17
17
|
|
18
|
-
attr_accessor :line_number, :
|
19
|
-
:discount, :discount_reason, :subtotal, :
|
18
|
+
attr_accessor :line_number,:tariff_item,:code, :comercial_code_type, :comercial_code, :quantity, :unit, :description, :unit_price, :total,
|
19
|
+
:discount, :discount_reason, :subtotal,:taxable_base ,:taxes,:net_tax ,:net_total, :exoneration, :document_type
|
20
20
|
|
21
|
+
validates :document_type, presence: true, inclusion: FE::Document::DOCUMENT_TYPES.keys
|
21
22
|
validates :line_number, presence: true
|
22
|
-
validates :
|
23
|
+
validates :tariff_item, presence: true, length: {is: 12}, if:->{document_type.eql?(FE::ExportInvoice::DOCUMENT_TYPE) && !SERVICE_UNITS.include?(unit) && document.version_43? }
|
23
24
|
validates :quantity, presence: true, numericality: { greater_than: 0 }
|
24
25
|
validates :unit, presence: true, inclusion: UNITS
|
25
|
-
validates :description, presence: true, length: { maximum:
|
26
|
+
validates :description, presence: true, length: { maximum: 200 }
|
26
27
|
validates :unit_price, presence: true
|
27
28
|
validates :total, presence: true
|
28
29
|
validates :discount, numericality: { grater_than: 0 }, if: -> { discount.present? }
|
29
30
|
validates :discount_reason, presence: true, if: -> { discount.present? }
|
30
31
|
validates :subtotal, presence: true
|
32
|
+
validates :taxable_base, presence: true, if: ->{ taxes.map{ |t| t.code.eql?("07")}.include?(true) && document.version_43? }
|
33
|
+
validates :net_tax,presence:true, if: ->{ taxes.map{ |t| t.exoneration.present? }.include?(true) }
|
31
34
|
validates :net_total, presence: true
|
32
|
-
validates :
|
35
|
+
validates :comercial_code_type, inclusion: CODE_TYPES.keys, if: -> { comercial_code.present? }
|
36
|
+
validates :comercial_code, presence: true, length: {maximum: 20}
|
37
|
+
validates :code, presence: true, length: {maximum: 13}, if: :code_is_mandatory?
|
38
|
+
|
39
|
+
validate :calculations_ok?
|
33
40
|
|
34
41
|
def initialize(args = {})
|
42
|
+
@document_type = args[:document_type]
|
35
43
|
@line_number = args[:line_number]
|
36
|
-
@code_type = args[:code_type].presence || '01'
|
37
44
|
@code = args[:code]
|
45
|
+
@comercial_code_type = args[:comercial_code_type].presence || '01'
|
46
|
+
@comercial_code = args[:comercial_code]
|
38
47
|
@quantity = args[:quantity]
|
39
48
|
@unit = args[:unit]
|
40
49
|
@description = args[:description]
|
@@ -45,17 +54,39 @@ module FE
|
|
45
54
|
@subtotal = args[:subtotal]
|
46
55
|
@taxes = args[:taxes] || []
|
47
56
|
@net_total = args[:net_total]
|
57
|
+
@exoneration = args[:exoneration]
|
58
|
+
@net_tax = args[:net_tax]
|
59
|
+
@tariff_item = args[:tariff_item]
|
60
|
+
@taxable_base = args[:taxable_base]
|
61
|
+
|
48
62
|
end
|
49
63
|
|
50
|
-
def build_xml(node)
|
51
|
-
|
64
|
+
def build_xml(node, document)
|
65
|
+
@document = document
|
66
|
+
@document_type = document.document_type
|
67
|
+
raise FE::Error.new("item invalid: #{ errors.messages.map{|k,v| "#{k}=#{v.join(". ")}"}.join("; ")}",class: self.class, messages: errors.messages) unless valid?
|
68
|
+
|
52
69
|
node = Nokogiri::XML::Builder.new if node.nil?
|
53
70
|
node.LineaDetalle do |x|
|
54
71
|
x.NumeroLinea @line_number
|
55
|
-
|
72
|
+
|
73
|
+
x.PartidaArancelaria @tariff_item if @tariff_item.present? && document.version_43?
|
74
|
+
|
75
|
+
if document.version_43?
|
76
|
+
x.Codigo @code if @code.present?
|
77
|
+
end
|
78
|
+
|
79
|
+
if @comercial_code.present? && document.version_43?
|
80
|
+
x.CodigoComercial do |x2|
|
81
|
+
x2.Tipo @comercial_code_type
|
82
|
+
x2.Codigo @comercial_code
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if @comercial_code.present? && document.version_42?
|
56
87
|
x.Codigo do |x2|
|
57
|
-
x2.Tipo @
|
58
|
-
x2.Codigo @
|
88
|
+
x2.Tipo @comercial_code_type
|
89
|
+
x2.Codigo @comercial_code
|
59
90
|
end
|
60
91
|
end
|
61
92
|
x.Cantidad @quantity
|
@@ -63,17 +94,43 @@ module FE
|
|
63
94
|
x.Detalle @description
|
64
95
|
x.PrecioUnitario @unit_price
|
65
96
|
x.MontoTotal @total
|
66
|
-
|
67
|
-
|
97
|
+
|
98
|
+
if document.version_42?
|
99
|
+
x.MontoDescuento @discount if @discount.present?
|
100
|
+
x.NaturalezaDescuento @discount_reason if @discount_reason.present?
|
101
|
+
end
|
102
|
+
if @discount.present? && document.version_43?
|
103
|
+
x.Descuento do |x2|
|
104
|
+
x2.MontoDescuento @discount
|
105
|
+
x2.NaturalezaDescuento @discount_reason
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
68
109
|
x.SubTotal @subtotal
|
110
|
+
|
111
|
+
x.BaseImponible @taxable_base if @taxable_base.present? && document.version_43?
|
69
112
|
@taxes.each do |tax|
|
70
|
-
tax.build_xml(x)
|
71
|
-
end
|
72
|
-
if @exoneration.present?
|
73
|
-
@exoneration.build_xml(x)
|
113
|
+
tax.build_xml(x,document)
|
74
114
|
end
|
115
|
+
|
116
|
+
x.ImpuestoNeto @net_tax if @net_tax.present?
|
75
117
|
x.MontoTotalLinea @net_total
|
76
118
|
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
def calculations_ok?
|
125
|
+
errors.add :total, :invalid_amount, message: 'invalid amount' if (@total - (@quantity * @unit_price).round(5)).abs > 1
|
126
|
+
end
|
127
|
+
|
128
|
+
def code_is_mandatory?
|
129
|
+
if Time.zone.now >= Time.zone.parse("2020-12-01").beginning_of_day
|
130
|
+
true
|
131
|
+
else
|
132
|
+
false
|
133
|
+
end
|
77
134
|
end
|
78
135
|
end
|
79
136
|
end
|
@@ -1,36 +1,32 @@
|
|
1
|
-
require "facturacr/document"
|
2
|
-
require 'active_model'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
1
|
module FE
|
6
2
|
class Document
|
7
|
-
|
8
|
-
|
9
|
-
class Location
|
3
|
+
|
4
|
+
|
5
|
+
class Location < Element
|
10
6
|
include ActiveModel::Validations
|
11
|
-
|
7
|
+
|
12
8
|
attr_accessor :province, :county,:district,:neighborhood, :others
|
13
|
-
|
9
|
+
|
14
10
|
validates :province, presence: true, length: { is: 1 }
|
15
11
|
validates :county, presence: true, length: { is: 2 }
|
16
12
|
validates :district, presence: true, length: { is: 2 }
|
17
13
|
validates :neighborhood, length: { is: 2 }, allow_blank: true
|
18
|
-
validates :others, presence: true, length: { maximum:
|
19
|
-
|
14
|
+
validates :others, presence: true, length: { maximum: 250 }
|
15
|
+
|
20
16
|
def initialize(args={})
|
21
|
-
|
17
|
+
|
22
18
|
@province = args[:province]
|
23
19
|
@county = args[:county]
|
24
20
|
@district = args[:district]
|
25
21
|
@neighborhood = args[:neighborhood]
|
26
22
|
@others = args[:others]
|
27
|
-
|
28
|
-
|
23
|
+
|
24
|
+
|
29
25
|
end
|
30
|
-
|
31
|
-
def build_xml(node)
|
32
|
-
raise "
|
33
|
-
node = Nokogiri::XML::Builder.new if node.nil?
|
26
|
+
|
27
|
+
def build_xml(node, document)
|
28
|
+
raise FE::Error.new("location invalid",class: self.class, messages: errors.messages) unless valid?
|
29
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
34
30
|
node.Ubicacion do |x|
|
35
31
|
x.Provincia @province
|
36
32
|
x.Canton @county
|
@@ -39,11 +35,11 @@ module FE
|
|
39
35
|
x.OtrasSenas @others
|
40
36
|
end
|
41
37
|
end
|
42
|
-
|
43
|
-
def to_xml(builder)
|
44
|
-
build_xml(builder).to_xml
|
38
|
+
|
39
|
+
def to_xml(builder,document)
|
40
|
+
build_xml(builder,document).to_xml
|
45
41
|
end
|
46
|
-
|
42
|
+
|
47
43
|
end
|
48
44
|
end
|
49
|
-
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module FE
|
2
|
+
class Document
|
3
|
+
class OtherCharges < Element
|
4
|
+
|
5
|
+
include ActiveModel::Validations
|
6
|
+
|
7
|
+
OTHER_DOCUMENT_TYPES = {
|
8
|
+
|
9
|
+
"01" => "Contribución parafiscal",
|
10
|
+
"02" => "Timbre de la Cruz Roja",
|
11
|
+
"03" => "Timbre de Benemérito Cuerpo de Bomberos de Costa Rica",
|
12
|
+
"04" => "Cobro de un tercero",
|
13
|
+
"05" => "Costos de Exportación",
|
14
|
+
"06" => "Impuesto de servicio 10%",
|
15
|
+
"07" => "Timbre de Colegios Profesionales",
|
16
|
+
"99" => "Otros Cargos"
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
attr_accessor :document_type, :collector_id_number, :collector_name, :detail, :percentage, :total_charge
|
20
|
+
|
21
|
+
validates :document_type, presence: true, inclusion: OTHER_DOCUMENT_TYPES.keys
|
22
|
+
validates :collector_id_number, presence: false, if: ->{ document_type.eql?("04") }
|
23
|
+
validates :detail, presence: true
|
24
|
+
validates :total_charge, presence: true
|
25
|
+
validates :collector_name, presence: false, if: ->{ document_type.eql?("04") }
|
26
|
+
|
27
|
+
def initialize(args={})
|
28
|
+
@document_type = args[:document_type]
|
29
|
+
@collector_id_number=args[:collector_id_number]
|
30
|
+
@collector_name = args[:collector_name]
|
31
|
+
@detail = args[:detail]
|
32
|
+
@percentage =args[:percentage]
|
33
|
+
@total_charge = args[:total_charge]
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_xml(node, document)
|
37
|
+
raise FE::Error.new("other charges invalid",class: self.class, messages: errors.messages) unless valid?
|
38
|
+
|
39
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
40
|
+
|
41
|
+
node.OtrosCargos do |xml|
|
42
|
+
xml.TipoDocumento @document_type
|
43
|
+
xml.NumeroIdentidadTercero @third_id_number if @third_id_number.present?
|
44
|
+
xml.NombreTercero @third_name if @third_name.present?
|
45
|
+
xml.Detalle @detail
|
46
|
+
xml.Porcentaje @percentage if @percentage.present?
|
47
|
+
xml.MontoCargo @total_charge
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|