Skip to content

Commit f43cdac

Browse files
committed
Merge branch 'split-files' of https://github.com/cesarjr/ruby_danfe
2 parents 6c59bd9 + ad0da0e commit f43cdac

File tree

7 files changed

+862
-852
lines changed

7 files changed

+862
-852
lines changed

lib/ruby_danfe.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1+
require "prawn"
2+
require "prawn/measurement_extensions"
3+
require "barby"
4+
require "barby/barcode/code_128"
5+
require "barby/outputter/prawn_outputter"
6+
require "nokogiri"
7+
18
Dir[File.dirname(__FILE__) + "/**/*.rb"].each { |f| require f }

lib/ruby_danfe/dacte_generator.rb

Lines changed: 459 additions & 0 deletions
Large diffs are not rendered by default.

lib/ruby_danfe/danfe_generator.rb

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# coding: utf-8
2+
module RubyDanfe
3+
class DanfeGenerator
4+
def self.generatePDF(xml)
5+
pdf = Document.new
6+
7+
pdf.repeat :all do
8+
9+
# CANHOTO
10+
11+
pdf.ibox 0.85, 16.10, 0.25, 0.42, "RECEBEMOS DE " + xml['emit/xNome'] + " OS PRODUTOS CONSTANTES DA NOTA FISCAL INDICADA ABAIXO"
12+
pdf.ibox 0.85, 4.10, 0.25, 1.27, "DATA DE RECEBIMENTO"
13+
pdf.ibox 0.85, 12.00, 4.35, 1.27, "IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR"
14+
15+
pdf.ibox 1.70, 4.50, 16.35, 0.42, '',
16+
"NF-e\n" +
17+
"N°. " + xml['ide/nNF'] + "\n" +
18+
"SÉRIE " + xml['ide/serie'], {:align => :center, :valign => :center}
19+
20+
# EMITENTE
21+
22+
pdf.ibox 3.92, 7.46, 0.25, 2.54, '',
23+
xml['emit/xNome'] + "\n" +
24+
xml['enderEmit/xLgr'] + ", " + xml['enderEmit/nro'] + "\n" +
25+
xml['enderEmit/xBairro'] + " - " + xml['enderEmit/CEP'] + "\n" +
26+
xml['enderEmit/xMun'] + "/" + xml['enderEmit/UF'] + "\n" +
27+
xml['enderEmit/fone'] + " " + xml['enderEmit/email'], {:align => :center, :valign => :center}
28+
29+
pdf.ibox 3.92, 3.08, 7.71, 2.54
30+
31+
pdf.ibox 0.60, 3.08, 7.71, 2.54, '', "DANFE", {:size => 12, :align => :center, :border => 0, :style => :bold}
32+
pdf.ibox 1.20, 3.08, 7.71, 3.14, '', "DOCUMENTO AUXILIAR DA NOTA FISCAL ELETRÔNICA", {:size => 8, :align => :center, :border => 0}
33+
pdf.ibox 0.60, 3.08, 7.71, 4.34, '', "#{xml['ide/tpNF']} - " + (xml['ide/tpNF'] == '0' ? 'ENTRADA' : 'SAÍDA'), {:size => 8, :align => :center, :border => 0}
34+
35+
pdf.ibox 1.00, 3.08, 7.71, 4.94, '',
36+
"N°. " + xml['ide/nNF'] + "\n" +
37+
"SÉRIE " + xml['ide/serie'], {:size => 8, :align => :center, :valign => :center, :border => 0, :style => :bold}
38+
39+
pdf.ibox 2.20, 10.02, 10.79, 2.54
40+
pdf.ibarcode 1.50, 8.00, 10.9010, 4.44, xml['chNFe']
41+
pdf.ibox 0.85, 10.02, 10.79, 4.74, "CHAVE DE ACESSO", xml['chNFe'].gsub(/(\d)(?=(\d\d\d\d)+(?!\d))/, "\\1 "), {:style => :bold, :align => :center}
42+
pdf.ibox 0.85, 10.02, 10.79, 5.60 , '', "Consulta de autenticidade no portal nacional da NF-e www.nfe.fazenda.gov.br/portal ou no site da Sefaz Autorizadora", {:align => :center, :size => 8}
43+
pdf.ibox 0.85, 10.54, 0.25, 6.46, "NATUREZA DA OPERAÇÃO", xml['ide/natOp']
44+
pdf.ibox 0.85, 10.02, 10.79, 6.46, "PROTOCOLO DE AUTORIZAÇÃO DE USO", xml['infProt/nProt'] + ' ' + xml['infProt/dhRecbto'], {:align => :center}
45+
46+
pdf.ibox 0.85, 6.86, 0.25, 7.31, "INSCRIÇÃO ESTADUAL", xml['emit/IE']
47+
pdf.ibox 0.85, 6.86, 7.11, 7.31, "INSC.ESTADUAL DO SUBST. TRIBUTÁRIO", xml['emit/IE_ST']
48+
pdf.ibox 0.85, 6.84, 13.97, 7.31, "CNPJ", xml['emit/CNPJ']
49+
50+
# TITULO
51+
52+
pdf.ititle 0.42, 10.00, 0.25, 8.16, "DESTINATÁRIO / REMETENTE"
53+
54+
pdf.ibox 0.85, 12.32, 0.25, 8.58, "NOME/RAZÃO SOCIAL", xml['dest/xNome']
55+
pdf.ibox 0.85, 5.33, 12.57, 8.58, "CNPJ/CPF", xml['dest/CNPJ'] if xml['dest/CNPJ'] != ''
56+
pdf.ibox 0.85, 5.33, 12.57, 8.58, "CNPJ/CPF", xml['dest/CPF'] if xml['dest/CPF'] != ''
57+
pdf.idate 0.85, 2.92, 17.90, 8.58, "DATA DA EMISSÃO", xml['ide/dEmi'], {:align => :right}
58+
pdf.ibox 0.85, 10.16, 0.25, 9.43, "ENDEREÇO", xml['enderDest/xLgr'] + " " + xml['enderDest/nro']
59+
pdf.ibox 0.85, 4.83, 10.41, 9.43, "BAIRRO", xml['enderDest/xBairro']
60+
pdf.ibox 0.85, 2.67, 15.24, 9.43, "CEP", xml['enderDest/CEP']
61+
pdf.idate 0.85, 2.92, 17.90, 9.43, "DATA DA SAÍDA/ENTRADA", xml['ide/dSaiEnt'], {:align => :right}
62+
pdf.ibox 0.85, 7.11, 0.25, 10.28, "MUNICÍPIO", xml['enderDest/xMun']
63+
pdf.ibox 0.85, 4.06, 7.36, 10.28, "FONE/FAX", xml['enderDest/fone']
64+
pdf.ibox 0.85, 1.14, 11.42, 10.28, "UF", xml['enderDest/UF']
65+
pdf.ibox 0.85, 5.33, 12.56, 10.28, "INSCRIÇÃO ESTADUAL", xml['dest/IE']
66+
pdf.idate 0.85, 2.92, 17.90, 10.28, "HORA DE SAÍDA", xml['ide/dSaiEnt'], {:align => :right}
67+
68+
# FATURAS
69+
70+
pdf.ititle 0.42, 10.00, 0.25, 11.12, "FATURA / DUPLICATAS"
71+
pdf.ibox 0.85, 20.57, 0.25, 11.51
72+
73+
x = 0.25
74+
y = 11.51
75+
xml.collect('xmlns', 'dup') { |det|
76+
pdf.ibox 0.85, 2.12, x, y, '', 'Núm.:', { :size => 6, :border => 0, :style => :italic }
77+
pdf.ibox 0.85, 2.12, x + 0.70, y, '', det.css('nDup').text, { :size => 6, :border => 0 }
78+
pdf.ibox 0.85, 2.12, x, y + 0.20, '', 'Venc.:', { :size => 6, :border => 0, :style => :italic }
79+
dtduplicata = det.css('dVenc').text
80+
dtduplicata = dtduplicata[8,2] + '/' + dtduplicata[5, 2] + '/' + dtduplicata[0, 4]
81+
pdf.ibox 0.85, 2.12, x + 0.70, y + 0.20, '', dtduplicata, { :size => 6, :border => 0 }
82+
pdf.ibox 0.85, 2.12, x, y + 0.40, '', 'Valor: R$', { :size => 6, :border => 0, :style => :italic }
83+
pdf.inumeric 0.85, 1.25, x + 0.70, y + 0.40, '', det.css('vDup').text, { :size => 6, :border => 0 }
84+
x = x + 2.30
85+
}
86+
87+
pdf.ititle 0.42, 5.60, 0.25, 12.36, "CÁLCULO DO IMPOSTO"
88+
89+
pdf.inumeric 0.85, 4.06, 0.25, 12.78, "BASE DE CÁLCULO DO ICMS", xml['ICMSTot/vBC']
90+
pdf.inumeric 0.85, 4.06, 4.31, 12.78, "VALOR DO ICMS", xml['ICMSTot/vICMS']
91+
pdf.inumeric 0.85, 4.06, 8.37, 12.78, "BASE DE CÁLCULO DO ICMS ST", xml['ICMSTot/vBCST']
92+
pdf.inumeric 0.85, 4.06, 12.43, 12.78, "VALOR DO ICMS ST", xml['ICMSTot/vST']
93+
pdf.inumeric 0.85, 4.32, 16.49, 12.78, "VALOR TOTAL DOS PRODUTOS", xml['ICMSTot/vProd']
94+
pdf.inumeric 0.85, 3.46, 0.25, 13.63, "VALOR DO FRETE", xml['ICMSTot/vFrete']
95+
pdf.inumeric 0.85, 3.46, 3.71, 13.63, "VALOR DO SEGURO", xml['ICMSTot/vSeg']
96+
pdf.inumeric 0.85, 3.46, 7.17, 13.63, "DESCONTO", xml['ICMSTot/vDesc']
97+
pdf.inumeric 0.85, 3.46, 10.63, 13.63, "OUTRAS DESPESAS ACESSORIAS", xml['ICMSTot/vOutro']
98+
pdf.inumeric 0.85, 3.46, 14.09, 13.63, "VALOR DO IPI", xml['ICMSTot/vIPI']
99+
pdf.inumeric 0.85, 3.27, 17.55, 13.63, "VALOR TOTAL DA NOTA", xml['ICMSTot/vNF'], :style => :bold
100+
101+
pdf.ititle 0.42, 10.00, 0.25, 14.48, "TRANSPORTADOR / VOLUMES TRANSPORTADOS"
102+
103+
pdf.ibox 0.85, 9.02, 0.25, 14.90, "RAZÃO SOCIAL", xml['transporta/xNome']
104+
pdf.ibox 0.85, 2.79, 9.27, 14.90, "FRETE POR CONTA", xml['transp/modFrete'] == '0' ? ' 0 - EMITENTE' : '1 - DEST.'
105+
pdf.ibox 0.85, 1.78, 12.06, 14.90, "CODIGO ANTT", xml['veicTransp/RNTC']
106+
pdf.ibox 0.85, 2.29, 13.84, 14.90, "PLACA DO VEÍCULO", xml['veicTransp/placa']
107+
pdf.ibox 0.85, 0.76, 16.13, 14.90, "UF", xml['veicTransp/UF']
108+
pdf.ibox 0.85, 3.94, 16.89, 14.90, "CNPJ/CPF", xml['transporta/CNPJ']
109+
pdf.ibox 0.85, 9.02, 0.25, 15.75, "ENDEREÇO", xml['transporta/xEnder']
110+
pdf.ibox 0.85, 6.86, 9.27, 15.75, "MUNICÍPIO", xml['transporta/xMun']
111+
pdf.ibox 0.85, 0.76, 16.13, 15.75, "UF", xml['transporta/UF']
112+
pdf.ibox 0.85, 3.94, 16.89, 15.75, "INSCRIÇÂO ESTADUAL", xml['transporta/IE']
113+
114+
vol = 0
115+
xml.collect('xmlns', 'vol') { |det|
116+
vol += 1
117+
if vol < 2
118+
pdf.ibox 0.85, 2.92, 0.25, 16.60, "QUANTIDADE", det.css('qVol').text
119+
pdf.ibox 0.85, 3.05, 3.17, 16.60, "ESPÉCIE", det.css('esp').text
120+
pdf.ibox 0.85, 3.05, 6.22, 16.60, "MARCA", det.css('marca').text
121+
pdf.ibox 0.85, 4.83, 9.27, 16.60, "NUMERAÇÃO"
122+
pdf.inumeric 0.85, 3.43, 14.10, 16.60, "PESO BRUTO", det.css('pesoB').text, {:decimals => 3}
123+
pdf.inumeric 0.85, 3.30, 17.53, 16.60, "PESO LÍQUIDO", det.css('pesoL').text, {:decimals => 3}
124+
else
125+
break
126+
end
127+
}
128+
129+
pdf.ititle 0.42, 10.00, 0.25, 17.45, "DADOS DO PRODUTO / SERVIÇO"
130+
131+
pdf.ibox 6.70, 2.00, 0.25, 17.87, "CÓDIGO"
132+
pdf.ibox 6.70, 4.90, 2.25, 17.87, "DESCRIÇÃO"
133+
pdf.ibox 6.70, 1.30, 7.15, 17.87, "NCM"
134+
pdf.ibox 6.70, 0.80, 8.45, 17.87, "CST"
135+
pdf.ibox 6.70, 1.00, 9.25, 17.87, "CFOP"
136+
pdf.ibox 6.70, 1.00, 10.25, 17.87, "UNID"
137+
pdf.ibox 6.70, 1.30, 11.25, 17.87, "QUANT"
138+
pdf.ibox 6.70, 1.50, 12.55, 17.87, "VALOR UNIT"
139+
pdf.ibox 6.70, 1.50, 14.05, 17.87, "VALOR TOT"
140+
pdf.ibox 6.70, 1.50, 15.55, 17.87, "BASE CÁLC"
141+
pdf.ibox 6.70, 1.00, 17.05, 17.87, "VL ICMS"
142+
pdf.ibox 6.70, 1.00, 18.05, 17.87, "VL IPI"
143+
pdf.ibox 6.70, 0.90, 19.05, 17.87, "% ICMS"
144+
pdf.ibox 6.70, 0.86, 19.95, 17.87, "% IPI"
145+
146+
pdf.horizontal_line 0.25.cm, 20.83.cm, :at => Helper.invert(18.17.cm)
147+
148+
pdf.ititle 0.42, 10.00, 0.25, 24.64, "CÁLCULO DO ISSQN"
149+
150+
pdf.ibox 0.85, 5.08, 0.25, 25.06, "INSCRIÇÃO MUNICIPAL", xml['emit/IM']
151+
pdf.ibox 0.85, 5.08, 5.33, 25.06, "VALOR TOTAL DOS SERVIÇOS", xml['total/vServ']
152+
pdf.ibox 0.85, 5.08, 10.41, 25.06, "BASE DE CÁLCULO DO ISSQN", xml['total/vBCISS']
153+
pdf.ibox 0.85, 5.28, 15.49, 25.06, "VALOR DO ISSQN", xml['total/ISSTot']
154+
155+
pdf.ititle 0.42, 10.00, 0.25, 25.91, "DADOS ADICIONAIS"
156+
157+
if vol > 1
158+
pdf.ibox 3.07, 12.93, 0.25, 26.33, "INFORMAÇÕES COMPLEMENTARES", '', {:size => 8, :valign => :top}
159+
pdf.ibox 3.07, 12.93, 0.25, 26.60, '', 'CONTINUAÇÃO TRANSPORTADOR/VOLUMES TRANSPORTADOS', {:size => 5, :valign => :top, :border => 0}
160+
v = 0
161+
y = 26.67
162+
xml.collect('xmlns', 'vol') { |det|
163+
v += 1
164+
if v > 1
165+
pdf.ibox 0.35, 0.70, 0.25, y + 0.10, '', 'QUANT.:', { :size => 4, :border => 0 }
166+
pdf.ibox 0.35, 0.70, 0.90, y + 0.10, '', det.css('qVol').text, { :size => 4, :border => 0, :style => :italic }
167+
pdf.ibox 0.35, 0.50, 1.35, y + 0.10, '', 'ESP.:', { :size => 4, :border => 0 }
168+
pdf.ibox 0.35, 3.00, 1.75, y + 0.10, '', det.css('esp').text, { :size => 4, :border => 0, :style => :italic }
169+
pdf.ibox 0.35, 0.70, 4.15, y + 0.10, '', 'MARCA:', { :size => 4, :border => 0 }
170+
pdf.ibox 0.35, 2.00, 4.75, y + 0.10, '', det.css('marca').text, { :size => 4, :border => 0, :style => :italic }
171+
pdf.ibox 0.35, 1.00, 6.10, y + 0.10, '', 'NUM.:', { :size => 4, :border => 0 }
172+
pdf.ibox 0.35, 1.30, 7.00, y + 0.10, '', 'PESO B.:', { :size => 4, :border => 0 }
173+
pdf.inumeric 0.35, 1.30, 7.00, y + 0.10, '', det.css('pesoB').text, {:decimals => 3, :size => 4, :border => 0, :style => :italic }
174+
pdf.ibox 0.35, 0.90, 8.50, y + 0.10, '', 'PESO LÍQ.:', { :size => 4, :border => 0 }
175+
pdf.inumeric 0.35, 1.50, 8.50, y + 0.10, '', det.css('pesoL').text, {:decimals => 3, :size => 4, :border => 0, :style => :italic }
176+
y = y + 0.15
177+
end
178+
}
179+
pdf.ibox 2.07, 12.93, 0.25, y + 0.30, '', 'OUTRAS INFORMAÇÕES', {:size => 6, :valign => :top, :border => 0}
180+
pdf.ibox 2.07, 12.93, 0.25, y + 0.50, '', xml['infAdic/infCpl'], {:size => 5, :valign => :top, :border => 0}
181+
else
182+
pdf.ibox 3.07, 12.93, 0.25, 26.33, "INFORMAÇÕES COMPLEMENTARES", xml['infAdic/infCpl'], {:size => 6, :valign => :top}
183+
end
184+
185+
pdf.ibox 3.07, 7.62, 13.17, 26.33, "RESERVADO AO FISCO"
186+
187+
end
188+
189+
pdf.font_size(6) do
190+
pdf.itable 6.37, 21.50, 0.25, 18.17,
191+
xml.collect('xmlns', 'det') { |det|
192+
if !det.css('prod/nFCI').text.empty? then
193+
descricao = <<-string
194+
#{det.css('prod/xProd').text}
195+
196+
FCI: #{det.css('prod/nFCI').text}
197+
string
198+
else
199+
descricao = det.css('prod/xProd').text
200+
end
201+
202+
[
203+
det.css('prod/cProd').text, #I02
204+
descricao, #I04
205+
det.css('prod/NCM').text, #I05
206+
Cst.to_danfe(det), #N11
207+
det.css('prod/CFOP').text, #I08
208+
det.css('prod/uCom').text, #I09
209+
Helper.numerify(det.css('prod/qCom').text), #I10
210+
Helper.numerify(det.css('prod/vUnCom').text), #I10a
211+
Helper.numerify(det.css('prod/vProd').text), #I11
212+
Helper.numerify(det.css('ICMS/*/vBC').text), #N15
213+
Helper.numerify(det.css('ICMS/*/vICMS').text), #N17
214+
Helper.numerify(det.css('IPI/*/vIPI').text), #O14
215+
Helper.numerify(det.css('ICMS/*/pICMS').text), #N16
216+
Helper.numerify(det.css('IPI/*/pIPI').text) #O13
217+
]
218+
},
219+
:column_widths => {
220+
0 => 2.00.cm,
221+
1 => 4.90.cm,
222+
2 => 1.30.cm,
223+
3 => 0.80.cm,
224+
4 => 1.00.cm,
225+
5 => 1.00.cm,
226+
6 => 1.30.cm,
227+
7 => 1.50.cm,
228+
8 => 1.50.cm,
229+
9 => 1.50.cm,
230+
10 => 1.00.cm,
231+
11 => 1.00.cm,
232+
12 => 0.90.cm,
233+
13 => 0.86.cm
234+
},
235+
:cell_style => {:padding => 2, :border_width => 0} do |table|
236+
pdf.dash(5);
237+
table.column(6..13).style(:align => :right)
238+
table.column(0..13).border_width = 1
239+
table.column(0..13).borders = [:bottom]
240+
end
241+
end
242+
243+
pdf.page_count.times do |i|
244+
pdf.go_to_page(i + 1)
245+
pdf.ibox 1.00, 3.08, 7.71, 5.54, '',
246+
"FOLHA #{i + 1} de #{pdf.page_count}", {:size => 8, :align => :center, :valign => :center, :border => 0, :style => :bold}
247+
end
248+
249+
return pdf
250+
end
251+
end
252+
end

lib/ruby_danfe/document.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module RubyDanfe
2+
class Document
3+
def initialize
4+
@document = Prawn::Document.new(
5+
:page_size => 'A4',
6+
:page_layout => :portrait,
7+
:left_margin => 0,
8+
:right_margin => 0,
9+
:top_margin => 0,
10+
:botton_margin => 0
11+
)
12+
13+
@document.font "Times-Roman"
14+
end
15+
16+
def method_missing(method_name, *args, &block)
17+
@document.send(method_name, *args, &block)
18+
end
19+
20+
def respond_to_missing?(method_name, include_private = false)
21+
@document.respond_to?(method_name, include_private) || super
22+
end
23+
24+
def ititle(h, w, x, y, title)
25+
self.text_box title, :size => 10, :at => [x.cm, Helper.invert(y.cm) - 2], :width => w.cm, :height => h.cm, :style => :bold
26+
end
27+
28+
def ibarcode(h, w, x, y, info)
29+
Barby::Code128C.new(info).annotate_pdf(self, :x => x.cm, :y => Helper.invert(y.cm), :width => w.cm, :height => h.cm) if info != ''
30+
end
31+
32+
def irectangle(h, w, x, y)
33+
self.stroke_rectangle [x.cm, Helper.invert(y.cm)], w.cm, h.cm
34+
end
35+
36+
def ibox(h, w, x, y, title = '', info = '', options = {})
37+
box [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
38+
end
39+
40+
def idate(h, w, x, y, title = '', info = '', options = {})
41+
tt = info.split('-')
42+
ibox h, w, x, y, title, "#{tt[2]}/#{tt[1]}/#{tt[0]}", options
43+
end
44+
45+
def box(at, w, h, title = '', info = '', options = {})
46+
options = {
47+
:align => :left,
48+
:size => 10,
49+
:style => nil,
50+
:valign => :top,
51+
:border => 1
52+
}.merge(options)
53+
self.stroke_rectangle at, w, h if options[:border] == 1
54+
self.text_box title, :size => 6, :style => :italic, :at => [at[0] + 2, at[1] - 2], :width => w - 4, :height => 8 if title != ''
55+
self.text_box info, :size => options[:size], :at => [at[0] + 2, at[1] - (title != '' ? 14 : 4) ], :width => w - 4, :height => h - (title != '' ? 14 : 4), :align => options[:align], :style => options[:style], :valign => options[:valign]
56+
end
57+
58+
def inumeric(h, w, x, y, title = '', info = '', options = {})
59+
numeric [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
60+
end
61+
62+
def numeric(at, w, h, title = '', info = '', options = {})
63+
options = {:decimals => 2}.merge(options)
64+
info = Helper.numerify(info, options[:decimals]) if info != ''
65+
box at, w, h, title, info, options.merge({:align => :right})
66+
end
67+
68+
def itable(h, w, x, y, data, options = {}, &block)
69+
self.bounding_box [x.cm, Helper.invert(y.cm)], :width => w.cm, :height => h.cm do
70+
self.table data, options do |table|
71+
yield(table)
72+
end
73+
end
74+
end
75+
end
76+
end

lib/ruby_danfe/helper.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module RubyDanfe
2+
class Helper
3+
def self.numerify(number, decimals = 2)
4+
return "" if !number || number == ""
5+
int, frac = ("%.#{decimals}f" % number).split(".")
6+
int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1\.")
7+
int + "," + frac
8+
end
9+
10+
def self.invert(y)
11+
28.7.cm - y
12+
end
13+
end
14+
end

0 commit comments

Comments
 (0)