Skip to content

Commit

Permalink
Merge pull request #5 from pdvend/feature/generate_cfe_pdf
Browse files Browse the repository at this point in the history
Feature/generate cfe pdf
  • Loading branch information
gabteles committed Aug 18, 2017
2 parents fbc1408 + 0ffb5c4 commit 462090a
Show file tree
Hide file tree
Showing 49 changed files with 1,777 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
*.pdf
Gemfile.lock
coverage
.byebug_history
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
## CHANGELOG

### Unreleased

### 0.2.0 [2017-05-29]
- [Modificado] Atualizando versões das dependências
- [Adicionado] Estrutura de Generator/Parser/Renderer
- [Adicionado] Gerador de Cfe
- [Adicionado] Cobertura de testes

### 0.1.5 [2017-05-29]
Expand Down
6 changes: 6 additions & 0 deletions br_invoices_pdf.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'br_invoices_pdf/version'

# rubocop:disable Metrics/BlockLength
Gem::Specification.new do |spec|
spec.name = 'br_invoices_pdf'
pre_release = ENV['SEMAPHORE'] && ENV['PRE_RELEASE']
Expand All @@ -23,7 +24,11 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'prawn', '~> 2.2.2'
spec.add_dependency 'prawn-table', '~> 0.2.1'
spec.add_dependency 'ox', '~> 2.5.0'
spec.add_dependency 'barby', '~> 0.5.1'
spec.add_dependency 'chunky_png', '~> 1.3.8'
spec.add_dependency 'rqrcode', '~> 0.10.1'

spec.add_development_dependency 'bundler', '~> 1.13'
spec.add_development_dependency 'rake', '~> 12.0'
Expand All @@ -32,4 +37,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'coveralls'
spec.add_development_dependency 'rubocop'
spec.add_development_dependency 'reek'
spec.add_development_dependency 'byebug'
end
2 changes: 2 additions & 0 deletions cfe.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<CFe><infCFe Id="CFe35170827192750000240590002257540001395955127" versao="0.07" versaoDadosEnt="0.07" versaoSB="010002"><ide><cUF>35</cUF><cNF>595512</cNF><mod>59</mod><nserieSAT>000225754</nserieSAT><nCFe>000139</nCFe><dEmi>20170808</dEmi><hEmi>151034</hEmi><cDV>7</cDV><tpAmb>1</tpAmb><CNPJ>22769530000131</CNPJ><signAC>FO7E++TPt/v6twwElpLfq8foIU4wE4FUW0fJLU1NTZywbOGZhijk8vH5BJf7/XiefaPP2uPFtS0333ZAlmzImBycKWe0ELM2bpu0k4shuSZrAXKJtY3VDEpWRD7mibQuF7EFzy/+kKHK9G2NIajlstMa90JpbzIx2aYFvzs2SfzuT2zeIeBBsnTMutHF7NFrsZQvgHkYY4Z7/PRzRvqikxb6o1LLt6yX+d+Z40vObqJeaGzWyfVs4KOdEdyv9mOKW7RRXR0/LM0RCcudjwVHlTCjyQ3HYBsjcmkSB1HQSMgydjkJOO9ICPWP7FtMjXcDZUny2gD5BOYkqL2Byple0Q==</signAC><assinaturaQRCODE>V0t3JUTvkC9hbQrfPtsnvQpDpvx950/Qj7lQZQ9Ee6sblwhlyZUitqy/EXyb65cdwz5inSNQhbjclH00bBmhzsIId/0DcJYyWLTZ8+jid6qoQqcZVN7+85nYQwe97k9t55to6/2KtMAdn1ooB4dNLhj+2+gy2s+X6We/2I/wuEcBPgDjUynX8KTnFMeFzCtESF3D1/ykVdxj+J3E5S82emBdBNEW4DL+3+kvHsFSyqbHjsseiuTp0oz2xJUuOoD9KE63hnLbIWuOC3z69gQofgGaKWuFjICFkirtTAHBuvBsMWcaSku3HCH68/fga4W/iIbv/RenbBeecG/Ffw4X9Q==</assinaturaQRCODE><numeroCaixa>001</numeroCaixa></ide><emit><CNPJ>27192750000240</CNPJ><xNome>NUTREM VALENS COMERCIO LTDA ME</xNome><xFant>NUTREM VALENS MARKET</xFant><enderEmit><xLgr>AVENIDA WASHINGTON LUIZ</xLgr><nro>970</nro><xCpl>C</xCpl><xBairro>METROPOLE</xBairro><xMun>DRACENA</xMun><CEP>17900000</CEP></enderEmit><IE>292065728117</IE><IM>197321</IM><cRegTrib>1</cRegTrib><cRegTribISSQN>1</cRegTribISSQN><indRatISSQN>N</indRatISSQN></emit><dest></dest><det nItem="1"><prod><cProd>SUCO4233</cProd><xProd>Suco de Tangerina Integral Mitto 1L</xProd><CFOP>5405</CFOP><uCom>UND</uCom><qCom>1.0000</qCom><vUnCom>16.500</vUnCom><vProd>16.50</vProd><indRegra>A</indRegra><vDesc>1.92</vDesc><vItem>14.58</vItem></prod><imposto><vItem12741>0.00</vItem12741><ICMS><ICMSSN102><Orig>0</Orig><CSOSN>500</CSOSN></ICMSSN102></ICMS><PIS><PISSN><CST>49</CST></PISSN></PIS><COFINS><COFINSSN><CST>49</CST></COFINSSN></COFINS></imposto></det><total><ICMSTot><vICMS>0.00</vICMS><vProd>16.50</vProd><vDesc>1.92</vDesc><vPIS>0.00</vPIS><vCOFINS>0.00</vCOFINS><vPISST>0.00</vPISST><vCOFINSST>0.00</vCOFINSST><vOutro>0.00</vOutro></ICMSTot><vCFe>14.58</vCFe><vCFeLei12741>0.00</vCFeLei12741></total><pgto><MP><cMP>04</cMP><vMP>14.58</vMP></MP><vTroco>0.00</vTroco></pgto><infAdic><obsFisco xCampo="02.03.04.03"><xTexto>Consulte o QRCode deste extrato atraves do App DeOlhoNaNota</xTexto></obsFisco></infAdic></infCFe><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod><Reference URI="#CFe35170827192750000240590002257540001395955127"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod><DigestValue>x7w+x/SD/didumxuYOXNGpg9o9amGeocwTKXaC2Wt18=</DigestValue></Reference></SignedInfo><SignatureValue>a7h3NWjAwkotebkrsQpVJj0I38ztE+GQagH24O8CeYLzXqvqilWg6ygp7mheNj3eNoZWwrOI2b43pAQogarnvkGHZJfXuG+Tt1rKIF9iTS/IeA2mEg5R5CgAGYtRuQRnGKe4Ws9Es5KjT7V3kWCaDjgHwhLj4+q6fx/ItyWYrAHy/BNTdeF2BKlKGJHmpxE2sZx1erFawgEcj7I3yXqljVAch1W3Q0i7IjvzlGRpDjO36DWm3zQJYRcK9mCo6apqIPTOvOCyWS4xlLE6YF6N9oPG1doBgkZ8Iai5bv94i5aBknKt7LWh5GRU+zTGmTFxoeYIV6WBgn+51dXe5Q2BdA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIGgjCCBGqgAwIBAgIJARZ+7S6IIx3NMA0GCSqGSIb3DQEBCwUAMFExNTAzBgNVBAoTLFNlY3JldGFyaWEgZGEgRmF6ZW5kYSBkbyBFc3RhZG8gZGUgU2FvIFBhdWxvMRgwFgYDVQQDEw9BQyBTQVQgU0VGQVogU1AwHhcNMTcwNjIzMDIyNDA2WhcNMjIwNjIzMDIyNDA2WjCBuzESMBAGA1UEBRMJMDAwMjI1NzU0MQswCQYDVQQGEwJCUjESMBAGA1UECBMJU2FvIFBhdWxvMREwDwYDVQQKEwhTRUZBWi1TUDEPMA0GA1UECxMGQUMtU0FUMSgwJgYDVQQLEx9BdXRlbnRpY2FkbyBwb3IgQVIgU0VGQVogU1AgU0FUMTYwNAYDVQQDEy1OVVRSRU0gVkFMRU5TIENPTUVSQ0lPIExUREEgTUU6MjcxOTI3NTAwMDAyNDAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfzf3N96ScvDnuqgTIhZPrqgnM4T1MrWg3RLT3dykFmt3NP1hazo3PMMeMrkL+wyX3kUa5xz1bf0RhqK1Njhb2Hmvy5lMjordsIYYQEmvsK1/MrFYDZ7I1ykQUn8qaDNAZ1O/2g7VMzfR1K/4UVufv1Qj0XTNSkhm1IwaEWSsUcVyqOulyoPHwkNZX2rHYKYlS0omR23cvhOvl6DmiukdcSCcK7aRMqNLCs4XfPiHvplhIrY7T4UjV7pXXjQuKJvwEDj89sbcq8wAuZtkpag0Sw4UWLxm/dA4FPtdMY0a6Gs8tuz7sevglkSqGA8auiLkbkLodyJ8WsTsGCVRrXpZHAgMBAAGjggHwMIIB7DAOBgNVHQ8BAf8EBAMCBeAwdQYDVR0gBG4wbDBqBgkrBgEEAYHsLQMwXTBbBggrBgEFBQcCARZPaHR0cDovL2Fjc2F0LmltcHJlbnNhb2ZpY2lhbC5jb20uYnIvcmVwb3NpdG9yaW8vZHBjL2Fjc2VmYXpzcC9kcGNfYWNzZWZhenNwLnBkZjBlBgNVHR8EXjBcMFqgWKBWhlRodHRwOi8vYWNzYXQuaW1wcmVuc2FvZmljaWFsLmNvbS5ici9yZXBvc2l0b3Jpby9sY3IvYWNzYXRzZWZhenNwL2Fjc2F0c2VmYXpzcGNybC5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbXByZW5zYW9maWNpYWwuY29tLmJyMFIGCCsGAQUFBzAChkZodHRwOi8vYWNzYXQuaW1wcmVuc2FvZmljaWFsLmNvbS5ici9yZXBvc2l0b3Jpby9jZXJ0aWZpY2Fkb3MvYWNzYXQucDdjMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAkGA1UdEwQCMAAwJAYDVR0RBB0wG6AZBgVgTAEDA6AQBA4yNzE5Mjc1MDAwMDI0MDAfBgNVHSMEGDAWgBSwhYGzKM12KikkS19YSm9o2aywKjANBgkqhkiG9w0BAQsFAAOCAgEAWk0aQYxxW+0v3FUcxbH7CDsrqH66s3e17q/TuGyoEbZL93ZyDlgDxzoILyWrRj2C5l5XgCjCaIbo2KQAecMfPSIKPRj9sYUzd5XBgcPcDx1QJ3nb3p2pIRqr5KQHc9WZZxNHqR8NoJaoa0BEUww+60gZLVHiWPTDesgB0YxxNYKUVlQonkq8dTuvT4OARbPE5W0PPldkFZnEMHl7f23DJ517voKh4dnIR788G6+FtXl4FlZq95Tn8VeqkT1mRvdafxB7kOQ9RNq2CRooxrG0ReIcXg1GrNR+AoGmHjsv7Bn7QsfOZv1hHLdXPqfF8OkOgXxYGJdVSjTiYjGkpUdzGdnQPJRV5U1qh2mYyl3/Cglj5ZySWxoeVDnqxc2ft/4ybvwECGSpx75ZGckHuMwz30K9uah5fQVHbzxzF95OxpvqeKEtHv5MmIV+m8IYS0NY2RamgKG6bTKttrUUb/kU9fqRyijFc35FStFYhfefSVALturuzG+Rwefe48ZEt8GgvAQCYE1ewmBnC2aJ0K9qv59eBg6p5Nt36s4ERN6MrGA/V8BSWR0ikiZRwDaHN9rioMtGfRR/ZDsBpNw3uNvikNHwPLvDEU7sfWohkLnGZekuLI+UmTra1RmWt1S7u3J1V6ceKS+7ZeDdHUu1JLbn63KSDIWsfNJIZMfUC4pP0nU=</X509Certificate></X509Data></KeyInfo></Signature></CFe>

7 changes: 7 additions & 0 deletions lib/br_invoices_pdf.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
require 'ox'
require 'prawn'
require 'prawn/table'
require 'chunky_png'
require 'rqrcode'
require 'barby'
require 'barby/barcode/code_128'
require 'barby/outputter/png_outputter'
require 'barby/outputter/prawn_outputter'

require 'br_invoices_pdf/version'
require 'br_invoices_pdf/generator'
Expand Down
2 changes: 2 additions & 0 deletions lib/br_invoices_pdf/cfe.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require 'br_invoices_pdf/cfe/parser/base_parser'
require 'br_invoices_pdf/cfe/parser'
require 'br_invoices_pdf/cfe/renderer/base_renderer'
require 'br_invoices_pdf/cfe/renderer'

module BrInvoicesPdf
Expand Down
41 changes: 25 additions & 16 deletions lib/br_invoices_pdf/cfe/parser.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
require 'br_invoices_pdf/cfe/parser/access_key'
require 'br_invoices_pdf/cfe/parser/company_attributes'
require 'br_invoices_pdf/cfe/parser/cpf'
require 'br_invoices_pdf/cfe/parser/document_number'
require 'br_invoices_pdf/cfe/parser/fisco_obs'
require 'br_invoices_pdf/cfe/parser/payment'
require 'br_invoices_pdf/cfe/parser/payments'
require 'br_invoices_pdf/cfe/parser/products_data'
require 'br_invoices_pdf/cfe/parser/sat'

module BrInvoicesPdf
module Cfe
module Parser
module_function

# atributos
# company_name -> emit,xNome
# trading_name -> emit,xFant
# company_address -> emit,enderEmit
# city -> emit,enderEmit
# company_state -> ?
# company_zipcode -> emit,enderEmit,CEP
# cnpj -> ide,Cnpj
# IE (numero do estado) -> emit,IE
# cfe_number -> ide,NFCE
# cpf -> ide,NFCE

def parse(_xml)
# Retornar uma hash com todos os atributos necessarios
{
PARSERERS = {
sat_params: Sat,
document_number: DocumentNumber,
payment: Payment,
payments: Payments,
products: ProductsData,
company_attributes: CompanyAttributes,
fisco_obs: FiscoObs,
access_key: AccessKey,
cpf: Cpf
}.freeze

}
def parse(xml)
PARSERERS.reduce({}) do |response, (param, parser)|
{ **response, param => parser.execute(xml) }
end
end
end
end
Expand Down
19 changes: 19 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/access_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module BrInvoicesPdf
module Cfe
module Parser
module AccessKey
extend BaseParser

module_function

def execute(xml)
element = xml.locate('Signature/SignedInfo').first
return unless element
element = element.nodes.last
return unless element
element.attributes[:URI]
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/base_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module BrInvoicesPdf
module Cfe
module Parser
module BaseParser
module_function

def locate_element(xml, path)
element = xml.locate(path).first
element.text if element
end

def node_locate(element, path)
value = element.nodes.first.locate(path).first
value.text if value
end
end
end
end
end
45 changes: 45 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/company_attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module BrInvoicesPdf
module Cfe
module Parser
module CompanyAttributes
extend BaseParser

module_function

SEFAZ_STATE_CODES = {
'11' => 'RO', '12' => 'AC', '13' => 'AM', '14' => 'RR', '15' => 'PA',
'16' => 'AP', '17' => 'TO', '21' => 'MA', '22' => 'PI', '23' => 'CE',
'24' => 'RN', '25' => 'PB', '26' => 'PE', '27' => 'AL', '28' => 'SE',
'29' => 'BA', '31' => 'MG', '32' => 'ES', '33' => 'RJ', '35' => 'SP',
'41' => 'PR', '42' => 'SC', '43' => 'RS', '50' => 'MS', '51' => 'MT',
'52' => 'GO', '53' => 'DF'
}.freeze

def execute(xml)
{
company_name: locate_element(xml, 'infCFe/emit/xNome'),
address: company_address_params(xml),
trading_name: locate_element(xml, 'infCFe/emit/xFant'),
zipcode: locate_element(xml, 'infCFe/emit/enderEmit/CEP'),
cnpj: locate_element(xml, 'infCFe/ide/CNPJ'),
ie: locate_element(xml, 'infCFe/emit/IE'),
im: locate_element(xml, 'infCFe/emit/IM')
}
end

def company_address_params(xml)
{
public_place: locate_element(xml, 'infCFe/emit/enderEmit/xLgr'),
number: locate_element(xml, 'infCFe/emit/enderEmit/nro'),
complement: locate_element(xml, 'infCFe/emit/enderEmit/xCpl'),
city: locate_element(xml, 'infCFe/emit/enderEmit/xMun'),
neighborhood: locate_element(xml, 'infCFe/emit/enderEmit/xBairro'),
cep: locate_element(xml, 'infCFe/emit/enderEmit/CEP'),
state: SEFAZ_STATE_CODES[locate_element(xml, 'infCFe/ide/cUF')]
}
end
private_class_method :company_address_params
end
end
end
end
15 changes: 15 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/cpf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module BrInvoicesPdf
module Cfe
module Parser
module Cpf
extend BaseParser

module_function

def execute(xml)
locate_element(xml, 'infCFe/dest/CPF')
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/document_number.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module BrInvoicesPdf
module Cfe
module Parser
module DocumentNumber
extend BaseParser

module_function

def execute(xml)
locate_element(xml, 'infCFe/ide/nCFe')
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/fisco_obs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module BrInvoicesPdf
module Cfe
module Parser
module FiscoObs
extend BaseParser

module_function

def execute(xml)
xml.locate('infCFe/infAdic/obsFisco').map do |element|
node = element.nodes.first
field = element.attributes[:xCampo]
next unless node && field
{
text: node.text,
field: field
}
end
end
end
end
end
end
22 changes: 22 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/payment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module BrInvoicesPdf
module Cfe
module Parser
module Payment
extend BaseParser

module_function

def execute(xml)
{
approximate_value_of_taxes: locate_element(xml, 'infCFe/total/vCFeLei12741'),
total: locate_element(xml, 'infCFe/total/vCFe'),
discount: locate_element(xml, 'infCFe/total/ICMSTot/vDesc'),
total_price: locate_element(xml, 'infCFe/total/ICMSTot/vProd'),
cashback: locate_element(xml, 'infCFe/pgto/vTroco'),
paid: locate_element(xml, 'infCFe/pgto/MP/vMP')
}
end
end
end
end
end
45 changes: 45 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/payments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module BrInvoicesPdf
module Cfe
module Parser
module Payments
extend BaseParser

module_function

PAYMENT_TYPES = {
'01' => 'Dinheiro',
'02' => 'Cheque',
'03' => 'Cartão de Crédito',
'04' => 'Cartão de Débito',
'05' => 'Crédito Loja',
'10' => 'Vale Alimentação',
'11' => 'Vale Refeição',
'12' => 'Vale Presente',
'13' => 'Vale Combustível',
'99' => 'Outros'
}.freeze

def execute(xml)
node_payments = xml.locate('infCFe/pgto')

payments_by_nodes(node_payments) if node_payments && node_payments.any?
end

def payments_by_nodes(node_payments)
node_payments.first.nodes
.select { |element| element.value == 'MP' }
.map { |element| payment_by(element) }
end
private_class_method :payments_by_nodes

def payment_by(element)
{
type: PAYMENT_TYPES[locate_element(element, 'cMP')],
amount: locate_element(element, 'vMP')
}
end
private_class_method :payment_by
end
end
end
end
37 changes: 37 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/products_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module BrInvoicesPdf
module Cfe
module Parser
module ProductsData
extend BaseParser

module_function

FIELDS = { code: 'cProd',
description: 'xProd',
cfop: 'CFOP',
quantity: 'qCom',
unit_label: 'uCom',
total_value: 'vProd',
unit_value: 'vUnCom',
item_value: 'vItem' }.freeze

def execute(xml)
node_products = xml.locate('infCFe/det')
products_params(node_products) if node_products
end

def products_params(node_products)
node_products.map(&method(:product_by))
end
private_class_method :products_params

def product_by(element)
FIELDS
.map { |(key, field)| [key, node_locate(element, field)] }
.to_h
end
private_class_method :product_by
end
end
end
end
25 changes: 25 additions & 0 deletions lib/br_invoices_pdf/cfe/parser/sat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module BrInvoicesPdf
module Cfe
module Parser
module Sat
extend BaseParser

module_function

AVAILABLE_UF = { '35' => 'São Paulo' }.freeze

def execute(xml)
{
pos_number: locate_element(xml, 'infCFe/ide/numeroCaixa'),
ncfe_number: locate_element(xml, 'infCFe/ide/nCFe'),
uf: AVAILABLE_UF[locate_element(xml, 'infCFe/ide/cUF')],
sat_number: locate_element(xml, 'infCFe/ide/nserieSAT'),
emission_date: locate_element(xml, 'infCFe/ide/dEmi'),
emission_hour: locate_element(xml, 'infCFe/ide/hEmi'),
document_qr_code_signature: locate_element(xml, 'infCFe/ide/assinaturaQRCODE')
}
end
end
end
end
end
Loading

0 comments on commit 462090a

Please sign in to comment.