mge_wholesale 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 886679bf2b3c05ded7fc01419e2e8ac27bdf66ce7d7901e2a4e2b30873545854
4
- data.tar.gz: f4f51d632d13087d1e9a37875f566a70be0004a766e0b630ee5f04483d528cb6
3
+ metadata.gz: f5b7852fe6985745da0f7bc4967e1fb8c64e336f226b9ac25ad2bacced35db70
4
+ data.tar.gz: 131296929cca3c7070a66909e110189f2efe5b4907599b6040cb1a79ac03f893
5
5
  SHA512:
6
- metadata.gz: f0f804a38963dfdd52c082a862d013ea582af0686709de1c5962933b5a7a7935a6338133446881f11b04c850d91b1a0baf7550d30376ade28f50d80b3bb5e34c
7
- data.tar.gz: 824e81b8deb279ebb36a99333aef106dd228da1e89d1ed4feaf37b6f7b671bb40868fc302f8180b72def4835edff9e8d09fe2cc87e37e796fe151644b12e4e92
6
+ metadata.gz: 56e138bae3c58c1f5a6952d79aa9e36dc9169297bc5727579ea3d1df1c81316abb251d4a8943203521d3141ee6bd5da0b3d1ff94c0e0934a6ef8898edabe6963
7
+ data.tar.gz: 48884f2aa13689fc317ed111f72ada10b4b3653555c690fbc4c48508407d48aea05c61a80997ea5a3574f11f683f23ec5c23c949a543912c2a8fcbc076ac2066
@@ -1,126 +1,101 @@
1
1
  module MgeWholesale
2
2
  # To submit an order:
3
3
  #
4
- # * Instantiate a new Order, passing in `:username` and `:password`
5
- # * Call {#add_header}
4
+ # * Instantiate a new Order, passing in `:username`
5
+ # * Call {#add_recipient}
6
6
  # * Call {#add_item} for each item on the order
7
- # * Call {#submit!} to send the order
8
7
  #
9
8
  # See each method for a list of required options.
10
9
  class Order < Base
11
10
 
11
+ HEADERS = [
12
+ 'Ordering Dealer Number',
13
+ 'Ordering Dealer Name',
14
+ 'Ship to Name',
15
+ 'Ship To Address',
16
+ 'Ship to City',
17
+ 'Ship to State',
18
+ 'Ship to Zip',
19
+ 'Ship to email',
20
+ 'Ship to phone',
21
+ 'Ship to FFL',
22
+ 'Item',
23
+ 'Item Desc',
24
+ 'Item UPC',
25
+ 'Item Qty',
26
+ 'Item Price',
27
+ 'Special Instructions'
28
+ ]
29
+
12
30
  # @option options [String] :username *required*
13
- # @option options [String] :password *required*
14
31
  def initialize(options = {})
15
- requires!(options, :username, :password)
16
- @options = options
17
- @items = []
32
+ requires!(options, :username, :po_number)
33
+
34
+ @dealer_number = options[:username]
35
+ @po_number = options[:po_number]
36
+ @items = []
18
37
  end
19
38
 
20
- # @param [Hash] header
21
- # * :customer [String] *required*
22
- # * :purchase_order [String] *required*
23
- # * :ffl [String] *required*
39
+ # @param header [Hash]
40
+ # * :dealer_name [String] *required*
41
+ # * :ffl [String]
24
42
  # * :shipping [Hash] *required*
25
43
  # * :name [String] *required*
26
- # * :address_1 [String] *required*
27
- # * :address_2 [String] optional
44
+ # * :address [String] *required*
28
45
  # * :city [String] *required*
29
46
  # * :state [String] *required*
30
47
  # * :zip [String] *required*
31
- def add_header(header = {})
32
- requires!(header, :customer, :purchase_order, :ffl, :shipping)
33
- requires!(header[:shipping], :name, :address_1, :city, :state, :zip)
34
- @header = header
35
- # Ensure that address_2 is not an empty string
36
- if @header[:shipping][:address_2] && @header[:shipping][:address_2].empty?
37
- @header[:shipping][:address_2] = nil
38
- end
48
+ # * :email [String] *required*
49
+ # * :phone [String] *required*
50
+ # * :special_instructions [String] optional
51
+ def add_recipient(hash = {})
52
+ requires!(hash, :dealer_name, :shipping)
53
+ requires!(hash[:shipping], :name, :address, :city, :state, :zip, :email, :phone)
54
+ @headers = hash
39
55
  end
40
56
 
41
- # @option item [String] :item_number *required*
42
- # @option item [Integer] :quantity *required*
43
- # @option item [String] :price *required* - Decimal formatted price, without currency sign
44
- # @option item [String] :description optional
57
+ # @param item [Hash]
58
+ # * :identifier [String] *required*
59
+ # * :description [String] *required*
60
+ # * :upc [String] *required*
61
+ # * :qty [Integer] *required*
62
+ # * :price [String] *required*
45
63
  def add_item(item = {})
46
- requires!(item, :item_number, :quantity, :price)
64
+ requires!(item, :identifier, :description, :upc, :qty, :price,)
47
65
  @items << item
48
66
  end
49
67
 
50
68
  def filename
51
- "#{@header[:purchase_order]}-order.txt"
69
+ return @filename if defined?(@filename)
70
+ timestamp = Time.now.strftime('%Y%m%d%T').gsub(':', '')
71
+ @filename = "MGE-#{@po_number.gsub('-', '')}-#{timestamp}.csv"
52
72
  end
53
73
 
54
- def submit!
55
- raise MgeWholesale::InvalidOrder.new("Must call #add_header before submitting") if @header.nil?
56
- raise MgeWholesale::InvalidOrder.new("Must add items with #add_item before submitting") if @items.empty?
74
+ def to_csv
75
+ CSV.generate(headers: true) do |csv|
76
+ csv << HEADERS
57
77
 
58
- @order_file = Tempfile.new(filename)
59
- begin
60
- CSV.open(@order_file.path, 'w+', col_sep: "\t") do |csv|
61
- csv << header_names
62
- csv << header_fields
63
- csv << items_header
64
- @items.each do |item|
65
- csv << item_fields(item)
66
- end
78
+ @items.each do |item|
79
+ csv << [
80
+ @dealer_number,
81
+ @headers[:dealer_name],
82
+ @headers[:shipping][:name],
83
+ @headers[:shipping][:address],
84
+ @headers[:shipping][:city],
85
+ @headers[:shipping][:state],
86
+ @headers[:shipping][:zip],
87
+ @headers[:shipping][:email],
88
+ @headers[:shipping][:phone],
89
+ @headers[:ffl],
90
+ item[:identifier],
91
+ item[:description],
92
+ item[:upc],
93
+ item[:qty],
94
+ item[:price],
95
+ @headers[:special_instructions]
96
+ ]
67
97
  end
68
-
69
- upload!
70
- ensure
71
- # Close and delete (unlink) file.
72
- @order_file.close
73
- @order_file.unlink
74
- end
75
-
76
- # TODO: Find some way of returning a meaningful true/false. Currently, if there's a problem, an exception is raised.
77
- true
78
- end
79
-
80
- private
81
-
82
- def header_names
83
- ['HL', 'Customer#', 'Ship to#', 'Ship to Name1', 'Address 1', 'Address 2', 'city', 'state', 'zip', 'cust po', 'ship method', 'notes', 'FFL#']
84
- end
85
-
86
- def header_fields
87
- [
88
- 'H',
89
- @header[:customer],
90
- (@header[:shipping][:ship_to_number] || '0000'),
91
- @header[:shipping][:name],
92
- @header[:shipping][:address_1],
93
- @header[:shipping][:address_2],
94
- @header[:shipping][:city],
95
- @header[:shipping][:state],
96
- @header[:shipping][:zip],
97
- @header[:purchase_order],
98
- @header[:shipping_method],
99
- @header[:notes],
100
- @header[:ffl],
101
- 'END',
102
- ]
103
- end
104
-
105
- def items_header
106
- ['LL', 'Item', 'Description', 'Qty', 'Price']
107
- end
108
-
109
- def item_fields(item)
110
- [
111
- 'L',
112
- item[:item_number],
113
- item[:description],
114
- item[:quantity],
115
- item[:price],
116
- ]
117
- end
118
-
119
- def upload!
120
- connect(@options) do |ftp|
121
- ftp.chdir(MgeWholesale.config.full_submission_dir)
122
- ftp.puttextfile(@order_file.path, filename)
123
- end
98
+ end # CSV.generate
124
99
  end
125
100
 
126
101
  end
@@ -1,3 +1,3 @@
1
1
  module MgeWholesale
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.0.1'.freeze
3
3
  end
data/lib/mge_wholesale.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'mge_wholesale/version'
2
2
 
3
+ require 'csv'
3
4
  require 'net/ftp'
4
5
  require 'tempfile'
5
6
 
@@ -8,17 +9,13 @@ require 'nokogiri'
8
9
  require 'active_support/all'
9
10
 
10
11
  require 'mge_wholesale/base'
11
- require 'mge_wholesale/ftp'
12
12
  require 'mge_wholesale/catalog'
13
13
  require 'mge_wholesale/category'
14
14
  require 'mge_wholesale/inventory'
15
- #require 'mge_wholesale/order'
16
- #require 'mge_wholesale/response_file'
15
+ require 'mge_wholesale/order'
17
16
  require 'mge_wholesale/user'
18
- #require 'mge_wholesale/brand_converter'
19
17
 
20
18
  module MgeWholesale
21
- #TODO: handle orders
22
19
  class InvalidOrder < StandardError; end
23
20
  class NotAuthenticated < StandardError; end
24
21
 
@@ -37,24 +34,12 @@ module MgeWholesale
37
34
  class Configuration
38
35
  attr_accessor :debug_mode
39
36
  attr_accessor :ftp_host
40
- attr_accessor :response_dir
41
- attr_accessor :submission_dir
42
37
  attr_accessor :top_level_dir
43
38
 
44
39
  def initialize
45
- @debug_mode ||= false
46
- @ftp_host ||= "ftp.mgegroup.com"
47
- @top_level_dir ||= "ffldealer"
48
- @submission_dir ||= ""
49
- @response_dir ||= ""
50
- end
51
-
52
- def full_submission_dir
53
- File.join(@top_level_dir, @submission_dir)
54
- end
55
-
56
- def full_response_dir
57
- File.join(@top_level_dir, @response_dir)
40
+ @debug_mode ||= false
41
+ @ftp_host ||= "ftp.mgegroup.com"
42
+ @top_level_dir ||= "ffldealer"
58
43
  end
59
44
  end
60
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mge_wholesale
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Ebling
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-21 00:00:00.000000000 Z
11
+ date: 2018-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -103,10 +103,8 @@ files:
103
103
  - lib/mge_wholesale/base.rb
104
104
  - lib/mge_wholesale/catalog.rb
105
105
  - lib/mge_wholesale/category.rb
106
- - lib/mge_wholesale/ftp.rb
107
106
  - lib/mge_wholesale/inventory.rb
108
107
  - lib/mge_wholesale/order.rb
109
- - lib/mge_wholesale/response_file.rb
110
108
  - lib/mge_wholesale/user.rb
111
109
  - lib/mge_wholesale/version.rb
112
110
  - mge_wholesale.gemspec
@@ -1,23 +0,0 @@
1
- module MgeWholesale
2
- class FTP
3
-
4
- attr_reader :connection
5
-
6
- def initialize(credentials)
7
- @connection ||= Net::FTP.new(MgeWholesale.config.ftp_host)
8
- @connection.passive = true
9
- self.login(credentials[:username], credentials[:password])
10
- end
11
-
12
- def login(username, password)
13
- @connection.login(username, password)
14
- rescue Net::FTPPermError
15
- raise MgeWholesale::NotAuthenticated
16
- end
17
-
18
- def close
19
- @connection.close
20
- end
21
-
22
- end
23
- end
@@ -1,71 +0,0 @@
1
- module MgeWholesale
2
- class ResponseFile < Base
3
-
4
- attr_reader :credentials
5
- attr_reader :filename
6
-
7
- # @option options [String] :username *required*
8
- # @option options [String] :password *required*
9
- # @option options [String] :filename *required*
10
- def initialize(ftp, options = {})
11
- requires!(options, :username, :password, :filename)
12
-
13
- @credentials = options.select { |k, v| [:username, :password].include?(k) }
14
- @filename = options[:filename]
15
- @ftp = ftp
16
- end
17
-
18
- # Return list of '855 Purchase Order Acknowledgement' files
19
- # @option options [String] :username *required*
20
- # @option options [String] :password *required*
21
- def self.all(ftp)
22
- ftp.nlst("*.txt")
23
- end
24
-
25
- # Is the file a '855 Purchase Order Acknowledgement'?
26
- def ack?
27
- filename.downcase.start_with?("ack")
28
- end
29
-
30
- # Is the file a '856 Advance Shipping Notice'?
31
- def asn?
32
- filename.downcase.start_with?("asn")
33
- end
34
-
35
- # Use '#gettextfile' to read file contents as a string
36
- def content
37
- return @content if @content
38
-
39
- @content = @ftp.gettextfile(@filename, nil)
40
-
41
- @content
42
- end
43
-
44
- # Convert to easily readable key-value pairs
45
- def to_json
46
- if corrupt_asn?
47
- CSV.parse(content.gsub("Price|", ""), headers: true, col_sep: "|").
48
- map { |x| x.to_h }.
49
- group_by { |x| x["PO Number"] }
50
- else
51
- CSV.parse(content, headers: true, col_sep: "|").
52
- map { |x| x.to_h }.
53
- group_by { |x| x["PO Number"] }
54
- end
55
- end
56
-
57
- private
58
-
59
- def corrupt_asn?
60
- return false if ack?
61
- lines = content.lines.map(&:chomp)
62
- if lines[0].split("|").length != lines[1].split("|").length
63
- puts "Notice: ASN file is malformed! (#{filename})"
64
- true
65
- else
66
- false
67
- end
68
- end
69
-
70
- end
71
- end