correios-frete-alternative 1.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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