correios-frete-alternative 1.10.2

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.
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList["spec/**/*_spec.rb"]
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
10
+ spec.pattern = "spec/**/*_spec.rb"
11
+ spec.rcov = true
12
+ end
13
+
14
+ task :default => :spec
15
+
16
+ require 'rdoc/task'
17
+ Rake::RDocTask.new do |rdoc|
18
+ version = File.exist?("VERSION") ? File.read("VERSION") : ""
19
+
20
+ rdoc.rdoc_dir = "rdoc"
21
+ rdoc.title = "correios-frete #{version}"
22
+ rdoc.rdoc_files.include("README*")
23
+ rdoc.rdoc_files.include("CHANGELOG*")
24
+ rdoc.rdoc_files.include("lib/**/*.rb")
25
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'correios-frete'
5
+
6
+ # pry
7
+ require 'pry'
8
+ Pry.start
9
+
10
+ # irb
11
+ require 'irb'
12
+ IRB.start
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'correios/frete/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "correios-frete-alternative"
7
+ gem.version = Correios::Frete::VERSION
8
+ gem.authors = ["Fernando Hamasaki de Amorim"]
9
+ gem.email = ["prodis@gmail.com"]
10
+ gem.summary = "Calculo de frete dos Correios."
11
+ gem.description = "Calculo de frete utilizando o Web Service dos Correios (http://www.correios.com.br/webservices).\nOs servicos de frete suportados sao PAC, SEDEX, SEDEX a Cobrar, SEDEX 10, SEDEX Hoje e e-SEDEX."
12
+ gem.homepage = "http://prodis.blog.br/correios-frete-gem-para-calculo-de-frete-dos-correios"
13
+ gem.licenses = ["MIT"]
14
+
15
+ gem.files = `git ls-files`.split($\)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.platform = Gem::Platform::RUBY
21
+ gem.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
22
+
23
+ gem.add_dependency "log-me", "~> 0.0.10"
24
+ gem.add_dependency "nokogiri"
25
+ gem.add_dependency "sax-machine", "~> 1.3"
26
+
27
+ gem.add_development_dependency "rake", "~> 12.0"
28
+ gem.add_development_dependency "pry", "~> 0.10"
29
+ gem.add_development_dependency "rspec", "~> 3.5"
30
+ gem.add_development_dependency "webmock", "~> 3.0"
31
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'correios/frete'
4
+ require 'correios/frete/calculador'
5
+ require 'correios/frete/pacote'
6
+ require 'correios/frete/pacote_item'
7
+ require 'correios/frete/parser'
8
+ require 'correios/frete/servico'
9
+ require 'correios/frete/web_service'
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+ require 'log-me'
3
+
4
+ module Correios
5
+ module Frete
6
+ extend LogMe
7
+
8
+ module Timeout
9
+ DEFAULT_REQUEST_TIMEOUT = 10 #seconds
10
+ attr_writer :request_timeout
11
+
12
+ def request_timeout
13
+ (@request_timeout ||= DEFAULT_REQUEST_TIMEOUT).to_i
14
+ end
15
+ end
16
+
17
+ extend Timeout
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: UTF-8
2
+ module Correios
3
+ module Frete
4
+ class Calculador
5
+ attr_accessor :cep_origem, :cep_destino
6
+ attr_accessor :diametro, :mao_propria, :aviso_recebimento, :valor_declarado
7
+ attr_accessor :codigo_empresa, :senha, :encomenda
8
+ attr_writer :peso, :comprimento, :largura, :altura, :formato
9
+
10
+ DEFAULT_OPTIONS = {
11
+ :peso => 0.0,
12
+ :comprimento => 0.0,
13
+ :largura => 0.0,
14
+ :altura => 0.0,
15
+ :diametro => 0.0,
16
+ :formato => :caixa_pacote,
17
+ :mao_propria => false,
18
+ :aviso_recebimento => false,
19
+ :valor_declarado => 0.0
20
+ }
21
+
22
+ def initialize(options = {})
23
+ DEFAULT_OPTIONS.merge(options).each do |attr, value|
24
+ self.send("#{attr}=", value)
25
+ end
26
+
27
+ yield self if block_given?
28
+ end
29
+
30
+ [:peso, :comprimento, :largura, :altura, :formato].each do |method|
31
+ define_method method do
32
+ @encomenda ? @encomenda.send(method) : instance_variable_get("@#{method}")
33
+ end
34
+ end
35
+
36
+ def calcular(*service_types)
37
+ response = web_service(service_types).request!
38
+ services = parser.servicos(response)
39
+
40
+ if service_types.size == 1
41
+ services.values.first
42
+ else
43
+ services
44
+ end
45
+ end
46
+ alias calculate calcular
47
+
48
+ def method_missing(method_name, *args)
49
+ return calcular($2.to_sym) if method_name.to_s =~ /^(calcular|calculate)_(.*)/ && Correios::Frete::Servico.code_from_type($2.to_sym)
50
+ super
51
+ end
52
+
53
+ def respond_to?(method_name)
54
+ return true if method_name.to_s =~ /^(calcular|calculate)_(.*)/ && Correios::Frete::Servico.code_from_type($2.to_sym)
55
+ super
56
+ end
57
+
58
+ private
59
+
60
+ def web_service(service_types)
61
+ Correios::Frete::WebService.new(self, service_types)
62
+ end
63
+
64
+ def parser
65
+ @parser ||= Correios::Frete::Parser.new
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: UTF-8
2
+ module Correios
3
+ module Frete
4
+ class Pacote
5
+ attr_reader :peso, :comprimento, :altura, :largura, :volume
6
+
7
+ MIN_DIMENSIONS = {
8
+ :comprimento => 16.0,
9
+ :largura => 11.0,
10
+ :altura => 2.0
11
+ }
12
+
13
+ def initialize(itens = nil)
14
+ @peso = @comprimento = @largura = @altura = @volume = 0.0
15
+ @itens = []
16
+
17
+ itens.each { |item| adicionar_item(item) } if itens
18
+ end
19
+
20
+ def formato
21
+ :caixa_pacote
22
+ end
23
+
24
+ def itens
25
+ @itens
26
+ end
27
+ alias items itens
28
+
29
+ def adicionar_item(item)
30
+ return unless item
31
+
32
+ item = Correios::Frete::PacoteItem.new(item) if item.is_a?(Hash)
33
+ @itens << item
34
+
35
+ calcular_medidas(item)
36
+ item
37
+ end
38
+ alias add_item adicionar_item
39
+
40
+ private
41
+
42
+ def calcular_medidas(item)
43
+ @peso += item.peso
44
+ @volume += item.volume
45
+
46
+ if @itens.size == 1
47
+ @comprimento = item.comprimento
48
+ @largura = item.largura
49
+ @altura = item.altura
50
+ else
51
+ dimensao = @volume.to_f**(1.0/3)
52
+ @comprimento = @largura = @altura = dimensao
53
+ end
54
+
55
+ min_dimension_values
56
+ end
57
+
58
+ def min_dimension_values()
59
+ @comprimento = min(@comprimento, MIN_DIMENSIONS[:comprimento])
60
+ @largura = min(@largura, MIN_DIMENSIONS[:largura])
61
+ @altura = min(@altura, MIN_DIMENSIONS[:altura])
62
+ end
63
+
64
+ def min(value, minimum)
65
+ (value < minimum) ? minimum : value
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+ module Correios
3
+ module Frete
4
+ class PacoteItem
5
+ attr_accessor :peso, :comprimento, :largura, :altura
6
+
7
+ DEFAULT_OPTIONS = {
8
+ :peso => 0.0,
9
+ :comprimento => 0.0,
10
+ :largura => 0.0,
11
+ :altura => 0.0
12
+ }
13
+
14
+ def initialize(options = {})
15
+ DEFAULT_OPTIONS.merge(options).each do |attr, value|
16
+ self.send("#{attr}=", value)
17
+ end
18
+
19
+ yield self if block_given?
20
+ end
21
+
22
+ def volume
23
+ @comprimento * @largura * @altura
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+ require 'nokogiri'
3
+
4
+ module Correios
5
+ module Frete
6
+ class Parser
7
+ def servicos(xml)
8
+ servicos = {}
9
+ xml = xml.encode("UTF-8", "ISO-8859-1")
10
+
11
+ Nokogiri::XML(xml).root.elements.each do |element|
12
+ servico = Correios::Frete::Servico.new.parse(element.to_xml)
13
+ servicos[servico.tipo] = servico
14
+ end
15
+
16
+ servicos
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: UTF-8
2
+ require 'sax-machine'
3
+
4
+ module Correios
5
+ module Frete
6
+ class Servico
7
+ include SAXMachine
8
+
9
+ AVAILABLE_SERVICES = {
10
+ "04510" => { :type => :pac, :name => "PAC", :description => "PAC sem contrato" },
11
+ "41068" => { :type => :pac_com_contrato, :name => "PAC", :description => "PAC com contrato" },
12
+ "41300" => { :type => :pac_gf, :name => "PAC GF", :description => "PAC para grandes formatos" },
13
+ "04014" => { :type => :sedex, :name => "SEDEX", :description => "SEDEX sem contrato" },
14
+ "40045" => { :type => :sedex_a_cobrar, :name => "SEDEX a Cobrar", :description => "SEDEX a Cobrar, sem contrato" },
15
+ "40126" => { :type => :sedex_a_cobrar_com_contrato, :name => "SEDEX a Cobrar", :description => "SEDEX a Cobrar, com contrato" },
16
+ "40215" => { :type => :sedex_10, :name => "SEDEX 10", :description => "SEDEX 10, sem contrato" },
17
+ "40290" => { :type => :sedex_hoje, :name => "SEDEX Hoje", :description => "SEDEX Hoje, sem contrato" },
18
+ "40096" => { :type => :sedex_com_contrato_1, :name => "SEDEX", :description => "SEDEX com contrato" },
19
+ "40436" => { :type => :sedex_com_contrato_2, :name => "SEDEX", :description => "SEDEX com contrato" },
20
+ "40444" => { :type => :sedex_com_contrato_3, :name => "SEDEX", :description => "SEDEX com contrato" },
21
+ "40568" => { :type => :sedex_com_contrato_4, :name => "SEDEX", :description => "SEDEX com contrato" },
22
+ "40606" => { :type => :sedex_com_contrato_5, :name => "SEDEX", :description => "SEDEX com contrato" },
23
+ "81019" => { :type => :e_sedex, :name => "e-SEDEX", :description => "e-SEDEX, com contrato" },
24
+ "81027" => { :type => :e_sedex_prioritario, :name => "e-SEDEX", :description => "e-SEDEX Prioritário, com contrato" },
25
+ "81035" => { :type => :e_sedex_express, :name => "e-SEDEX", :description => "e-SEDEX Express, com contrato" },
26
+ "81868" => { :type => :e_sedex_grupo_1, :name => "e-SEDEX", :description => "(Grupo 1) e-SEDEX, com contrato" },
27
+ "81833" => { :type => :e_sedex_grupo_2, :name => "e-SEDEX", :description => "(Grupo 2) e-SEDEX, com contrato" },
28
+ "81850" => { :type => :e_sedex_grupo_3, :name => "e-SEDEX", :description => "(Grupo 3) e-SEDEX, com contrato" }
29
+ }.freeze
30
+
31
+ element :Codigo, :as => :codigo
32
+ element :Valor, :as => :valor
33
+ element :PrazoEntrega, :as => :prazo_entrega
34
+ element :ValorMaoPropria, :as => :valor_mao_propria
35
+ element :ValorAvisoRecebimento, :as => :valor_aviso_recebimento
36
+ element :ValorValorDeclarado, :as => :valor_valor_declarado
37
+ element :EntregaDomiciliar, :as => :entrega_domiciliar
38
+ element :EntregaSabado, :as => :entrega_sabado
39
+ element :Erro, :as => :erro
40
+ element :MsgErro, :as => :msg_erro
41
+ attr_reader :tipo, :nome, :descricao
42
+
43
+ alias_method :original_parse, :parse
44
+
45
+ def parse(xml_text)
46
+ original_parse xml_text
47
+
48
+ if AVAILABLE_SERVICES[codigo]
49
+ @tipo = AVAILABLE_SERVICES[codigo][:type]
50
+ @nome = AVAILABLE_SERVICES[codigo][:name]
51
+ @descricao = AVAILABLE_SERVICES[codigo][:description]
52
+ end
53
+
54
+ cast_to_float! :valor, :valor_mao_propria, :valor_aviso_recebimento, :valor_valor_declarado
55
+ cast_to_int! :prazo_entrega
56
+ cast_to_boolean! :entrega_domiciliar, :entrega_sabado
57
+ self
58
+ end
59
+
60
+ def success?
61
+ valor > 0.0
62
+ end
63
+ alias sucesso? success?
64
+
65
+ def error?
66
+ !success?
67
+ end
68
+ alias erro? error?
69
+
70
+ def self.code_from_type(type)
71
+ # I don't use select method for Ruby 1.8.7 compatibility.
72
+ AVAILABLE_SERVICES.map { |key, value| key if value[:type] == type }.compact.first
73
+ end
74
+
75
+ private
76
+
77
+ def cast_to_float!(*attributes)
78
+ attributes.each do |attr|
79
+ value = send(attr).to_s.gsub(".", "").gsub("," ,".")
80
+ instance_variable_set("@#{attr}", value.to_f)
81
+ end
82
+ end
83
+
84
+ def cast_to_int!(*attributes)
85
+ attributes.each do |attr|
86
+ instance_variable_set("@#{attr}", send(attr).to_i)
87
+ end
88
+ end
89
+
90
+ def cast_to_boolean!(*attributes)
91
+ attributes.each do |attr|
92
+ instance_variable_set("@#{attr}", send(attr) == "S")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+ module Correios
3
+ module Frete
4
+ VERSION = "1.10.2"
5
+ end
6
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: UTF-8
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ module Correios
6
+ module Frete
7
+ class WebService
8
+ URL = "http://ws.correios.com.br/calculador/CalcPrecoPrazo.aspx"
9
+ FORMATS = { :caixa_pacote => 1, :rolo_prisma => 2, :envelope => 3 }
10
+ CONDITIONS = { true => "S", false => "N" }
11
+
12
+ def initialize(frete, service_types)
13
+ @url = "#{URL}?#{params_for(frete, service_types)}"
14
+ end
15
+
16
+ def request!
17
+ response = with_log { http_request(@url) }
18
+ response.body
19
+ end
20
+
21
+ private
22
+
23
+ def http_request(url)
24
+ uri = URI.parse(url)
25
+ request = Net::HTTP::Get.new(uri.request_uri)
26
+
27
+ http = Net::HTTP.new(uri.host, uri.port)
28
+ http.open_timeout = Correios::Frete.request_timeout
29
+ http.request(request)
30
+ end
31
+
32
+ def params_for(frete, service_types)
33
+ "sCepOrigem=#{frete.cep_origem}&" +
34
+ "sCepDestino=#{frete.cep_destino}&" +
35
+ "nVlPeso=#{frete.peso}&" +
36
+ "nVlComprimento=#{format_decimal(frete.comprimento)}&" +
37
+ "nVlLargura=#{format_decimal(frete.largura)}&" +
38
+ "nVlAltura=#{format_decimal(frete.altura)}&" +
39
+ "nVlDiametro=#{format_decimal(frete.diametro)}&" +
40
+ "nCdFormato=#{FORMATS[frete.formato]}&" +
41
+ "sCdMaoPropria=#{CONDITIONS[frete.mao_propria]}&" +
42
+ "sCdAvisoRecebimento=#{CONDITIONS[frete.aviso_recebimento]}&" +
43
+ "nVlValorDeclarado=#{format_decimal(format("%.2f" % frete.valor_declarado))}&" +
44
+ "nCdServico=#{service_codes_for(service_types)}&" +
45
+ "nCdEmpresa=#{frete.codigo_empresa}&" +
46
+ "sDsSenha=#{frete.senha}&" +
47
+ "StrRetorno=xml"
48
+ end
49
+
50
+ def format_decimal(value)
51
+ value.to_s.gsub(".", ",")
52
+ end
53
+
54
+ def service_codes_for(service_types)
55
+ service_types.map { |type| Correios::Frete::Servico.code_from_type(type) }.join(",")
56
+ end
57
+
58
+ def with_log
59
+ Correios::Frete.log format_request_message
60
+ response = yield
61
+ Correios::Frete.log format_response_message(response)
62
+ response
63
+ end
64
+
65
+ def format_request_message
66
+ message = with_line_break { "Correios-Frete Request:" }
67
+ message << with_line_break { "GET #{@url}" }
68
+ end
69
+
70
+ def format_response_message(response)
71
+ message = with_line_break { "Correios-Frete Response:" }
72
+ message << with_line_break { "HTTP/#{response.http_version} #{response.code} #{response.message}" }
73
+ message << with_line_break { format_headers_for(response) } if Correios::Frete.log_level == :debug
74
+ message << with_line_break { response.body }
75
+ end
76
+
77
+ def format_headers_for(http)
78
+ # I'm using an empty block in each_header method for Ruby 1.8.7 compatibility.
79
+ http.each_header{}.map { |name, values| "#{name}: #{values.first}" }.join("\n")
80
+ end
81
+
82
+ def with_line_break
83
+ "#{yield}\n"
84
+ end
85
+ end
86
+ end
87
+ end