orion_wholesale 1.0.0

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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0f912eba82356f534ac1e312792ac9e0b97f861f3703ef51c6ed6627259977c8
4
+ data.tar.gz: b227ce9f341ea67e794419c5fabfe3e447b76c582968a71cd241bc3195784805
5
+ SHA512:
6
+ metadata.gz: 8294e49274594cfc604879212042c2451cd5160562800c30eb3ffb1b05aa08369e04a1a31851266f4c7eb7db8d7cf4348c35bf6e258c00eeff3d5a148600856c
7
+ data.tar.gz: 9df02007435a308a6649755711d4e75492e370068e189b02bd657b7ffae472ec21cfc77305ca4b25ea6c97de0dc0fe6e5a2625cdbeff0554fbffce38c79c2a7d
@@ -0,0 +1,67 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /db/*.sqlite3
5
+ /db/*.sqlite3-journal
6
+ /db/*.sqlite3-[0-9]*
7
+ /public/system
8
+ /coverage/
9
+ /spec/tmp
10
+ *.orig
11
+ rerun.txt
12
+ pickle-email-*.html
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ /tmp/*
17
+ !/log/.keep
18
+ !/tmp/.keep
19
+
20
+ # TODO Comment out this rule if you are OK with secrets being uploaded to the repo
21
+ config/initializers/secret_token.rb
22
+ config/master.key
23
+
24
+ # Only include if you have production secrets in this file, which is no longer a Rails default
25
+ # config/secrets.yml
26
+
27
+ # dotenv
28
+ # TODO Comment out this rule if environment variables can be committed
29
+ .env
30
+
31
+ ## Environment normalization:
32
+ /.bundle
33
+ /vendor/bundle
34
+
35
+ # these should all be checked in to normalize the environment:
36
+ # Gemfile.lock, .ruby-version, .ruby-gemset
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
40
+
41
+ # if using bower-rails ignore default bower_components path bower.json files
42
+ /vendor/assets/bower_components
43
+ *.bowerrc
44
+ bower.json
45
+
46
+ # Ignore pow environment settings
47
+ .powenv
48
+
49
+ # Ignore Byebug command history file.
50
+ .byebug_history
51
+
52
+ # Ignore node_modules
53
+ node_modules/
54
+
55
+ # Ignore precompiled javascript packs
56
+ /public/packs
57
+ /public/packs-test
58
+ /public/assets
59
+
60
+ # Ignore yarn files
61
+ /yarn-error.log
62
+ yarn-debug.log*
63
+ .yarn-integrity
64
+
65
+ # Ignore uploaded files in development
66
+ /storage/*
67
+ !/storage/.keep
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in orion.gemspec
4
+ gemspec
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ orion_wholesale (1.0.0)
5
+ activesupport (>= 5, < 7)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (5.2.4.3)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
15
+ concurrent-ruby (1.1.6)
16
+ diff-lcs (1.3)
17
+ i18n (1.8.5)
18
+ concurrent-ruby (~> 1.0)
19
+ minitest (5.14.1)
20
+ net-sftp (2.1.2)
21
+ net-ssh (>= 2.6.5)
22
+ net-ssh (5.2.0)
23
+ rake (12.3.3)
24
+ rspec (3.9.0)
25
+ rspec-core (~> 3.9.0)
26
+ rspec-expectations (~> 3.9.0)
27
+ rspec-mocks (~> 3.9.0)
28
+ rspec-core (3.9.1)
29
+ rspec-support (~> 3.9.1)
30
+ rspec-expectations (3.9.0)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.9.0)
33
+ rspec-mocks (3.9.1)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.9.0)
36
+ rspec-support (3.9.2)
37
+ thread_safe (0.3.6)
38
+ tzinfo (1.2.7)
39
+ thread_safe (~> 0.1)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ bundler (>= 1.15)
46
+ net-sftp
47
+ orion_wholesale!
48
+ rake (~> 12.3.0)
49
+ rspec (~> 3.7)
50
+
51
+ BUNDLED WITH
52
+ 1.17.3
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Tony Beninate
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # OrionWholesale
2
+
3
+ Ruby library for OrionWholesale.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'orion_wholesale'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install orion_wholesale
20
+
21
+
22
+
23
+ #TODO: Update info below
24
+
25
+
26
+
27
+ ## Usage
28
+
29
+ **Note:** Nearly all methods require `:username` and `:password` keys in the options hash.
30
+
31
+ ```ruby
32
+ options = {
33
+ username: 'dealer@example.com',
34
+ password: 'sekret-passwd'
35
+ }
36
+ ```
37
+
38
+ ### OrionWholesale::Catalog
39
+
40
+ To get all items in the catalog:
41
+
42
+ ```ruby
43
+ catalog = []
44
+ OrionWholesale::Catalog.new(options).all do |i|
45
+ catalog << i
46
+ end
47
+ ```
48
+
49
+ See `OrionWholesale::Catalog` for the response structure.
50
+
51
+ ### OrionWholesale::Inventory
52
+
53
+ To get your inventory details (availability, price, etc.):
54
+
55
+ ```ruby
56
+ inventory = []
57
+ OrionWholesale::Inventory.new(options).all do |i|
58
+ inventory << i
59
+ end
60
+ ```
61
+
62
+ See `OrionWholesale::Inventory` for the response structure.
63
+
64
+ ### OrionWholesale::Category
65
+
66
+ Returns an array of category codes and descriptions.
67
+
68
+ ```ruby
69
+ categories = OrionWholesale::Category.all(options)
70
+
71
+ # [
72
+ # {:code=>"H648", :description=>"AIRGUNS"},
73
+ # {:code=>"H610", :description=>"AMMUNITION"},
74
+ # ...,
75
+ # ]
76
+ ```
77
+
78
+ ### OrionWholesale::Order
79
+
80
+ To build and submit an order, the basic steps are: 1) instantiate an Order object, 2) add header
81
+ information, 3) add item information (multiple items if needed), 4) submit the order.
82
+
83
+ ```ruby
84
+ # Instantiate the Order instance, passing in your :username and :password
85
+ order = OrionWholesale::Order.new(options)
86
+
87
+ # Add header information:
88
+ header_opts = {
89
+ customer: '...', # customer number
90
+ purchase_order: '...', # application specific purchase order
91
+ ffl: '...', # your FFL number
92
+ shipping: { # shipping information (all fields except :address_2 are required)
93
+ name: '...',
94
+ address_1: '...',
95
+ address_2: '...',
96
+ city: '...',
97
+ state: '...',
98
+ zip: '...',
99
+ },
100
+
101
+ # Optional fields:
102
+ shipping_method: '...',
103
+ notes: '...',
104
+ }
105
+ order.add_header(header_opts)
106
+
107
+ # Add item information:
108
+ item_opts = {
109
+ item_number: '...', # Orion item number
110
+ description: '...',
111
+ quantity: 1,
112
+ price: '123.45', # Decimal formatted price, without currency sign
113
+ }
114
+ order.add_item(item_opts) # Multiple items may be added, just call #add_item for each one.
115
+
116
+ # Submit the order (returns true on success, raises an exception on failure):
117
+ order.submit!
118
+ ```
119
+
120
+ See `OrionWholesale::Order` for details on required options.
121
+
122
+ ### OrionWholesale::Tracking
123
+
124
+ For fetching tracking data for individual order fulfillments.
125
+
126
+ ```ruby
127
+ tracking_details = OrionWholesale::Tracking.fetch_data(options)
128
+
129
+ # [
130
+ # {:po_number=>"12345", :carrier=>"UPS", :tracking_numbers=>['123456789']},
131
+ # {:po_number=>"54321", :carrier=>"UPS", :tracking_numbers=>['987654321']},
132
+ # ...,
133
+ # ]
134
+ ```
135
+
136
+ ## Development
137
+
138
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
139
+
140
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
141
+
142
+ ## Contributing
143
+
144
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ammoready/orion_wholesale.
145
+
146
+
147
+ ## License
148
+
149
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "orion_wholesale"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,47 @@
1
+ require 'orion_wholesale/version'
2
+
3
+ require 'csv'
4
+ require 'net/sftp'
5
+ require 'tempfile'
6
+
7
+ require 'active_support/all'
8
+
9
+ require 'orion_wholesale/base'
10
+ require 'orion_wholesale/catalog'
11
+ require 'orion_wholesale/category'
12
+ require 'orion_wholesale/inventory'
13
+ require 'orion_wholesale/order'
14
+ require 'orion_wholesale/tracking'
15
+ require 'orion_wholesale/user'
16
+
17
+ module OrionWholesale
18
+ class InvalidOrder < StandardError; end
19
+ class NotAuthenticated < StandardError; end
20
+ class FileOrDirectoryNotFound < StandardError; end
21
+
22
+ class << self
23
+ attr_accessor :config
24
+ end
25
+
26
+ def self.config
27
+ @config ||= Configuration.new
28
+ end
29
+
30
+ def self.configure
31
+ yield(config)
32
+ end
33
+
34
+ class Configuration
35
+ attr_accessor :debug_mode
36
+ attr_accessor :ftp_host
37
+ attr_accessor :ftp_port
38
+ attr_accessor :top_level_dir
39
+
40
+ def initialize
41
+ @debug_mode ||= false
42
+ @ftp_host ||= "74.143.53.110"
43
+ @ftp_port ||= "10022"
44
+ @top_level_dir ||= "ffldealer"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ module OrionWholesale
2
+ class Base
3
+
4
+ def self.connect(options = {})
5
+ requires!(options, :username, :password)
6
+
7
+ Net::SFTP.start(OrionWholesale.config.ftp_host, options[:username], password: options[:password], port: OrionWholesale.config.ftp_port) do |sftp|
8
+ yield(sftp)
9
+ end
10
+ end
11
+
12
+ protected
13
+
14
+ # Wrapper to `self.requires!` that can be used as an instance method.
15
+ def requires!(*args)
16
+ self.class.requires!(*args)
17
+ end
18
+
19
+ def self.requires!(hash, *params)
20
+ params.each do |param|
21
+ if param.is_a?(Array)
22
+ raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
23
+
24
+ valid_options = param[1..-1]
25
+ raise ArgumentError.new("Parameter: #{param.first} must be one of: #{valid_options.join(', ')}") unless valid_options.include?(hash[param.first])
26
+ else
27
+ raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
28
+ end
29
+ end
30
+ end
31
+
32
+ # Instance methods become class methods through inheritance
33
+ def connect(options)
34
+ self.class.connect(options) do |sftp|
35
+ begin
36
+ yield(sftp)
37
+ end
38
+ end
39
+ end
40
+
41
+ def content_for(xml_doc, field)
42
+ node = xml_doc.css(field).first
43
+
44
+ if node.nil?
45
+ nil
46
+ else
47
+ node.content.try(:strip)
48
+ end
49
+ end
50
+
51
+ def get_file(filename, file_directory=nil)
52
+ connect(@options) do |sftp|
53
+ begin
54
+ tempfile = Tempfile.new
55
+
56
+ sftp.download!(File.join(file_directory, filename), tempfile.path)
57
+
58
+ return tempfile
59
+ end
60
+ end
61
+ end
62
+
63
+ def get_most_recent_file(file_prefix, file_directory=nil)
64
+ filenames = []
65
+
66
+ connect(@options) do |sftp|
67
+ sftp.dir.foreach(file_directory) { |entry| filenames << entry.name }
68
+ filename = filenames.select{ |n| n.include?(file_prefix) }.sort.last
69
+
70
+ tempfile = self.get_file(filename, file_directory)
71
+ return tempfile
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,66 @@
1
+ module OrionWholesale
2
+ class Catalog < Base
3
+
4
+ CATALOG_DIR = '/ammoready'
5
+ CATALOG_FILENAME_PREFIX = 'orion_inv_arweb'
6
+
7
+ def initialize(options = {})
8
+ requires!(options, :username, :password)
9
+ @options = options
10
+ end
11
+
12
+ def self.all(options = {})
13
+ requires!(options, :username, :password)
14
+ new(options).all
15
+ end
16
+
17
+ def all
18
+ tempfile = get_most_recent_file(CATALOG_FILENAME_PREFIX, CATALOG_DIR)
19
+ items = []
20
+
21
+ File.open(tempfile).each_with_index do |row, i|
22
+ row = row.split("\t")
23
+
24
+ if i==0
25
+ @headers = row
26
+ next
27
+ end
28
+
29
+ if row[@headers.index('Category1')].strip == 'Guns'
30
+ # Guns
31
+ case row[@headers.index('Category2')].strip
32
+ when 'Long Guns'
33
+ @category = row[@headers.index('Category3')].strip
34
+ @subcategory = row[@headers.index("Category4\n")].strip
35
+ when 'Handguns'
36
+ @category = row[@headers.index('Category2')].strip
37
+ @subcategory = row[@headers.index("Category3")].strip
38
+ end
39
+ else
40
+ # Everything else
41
+ @category = row[@headers.index('Category1')].strip
42
+ @subcategory = row[@headers.index('Category2')].strip
43
+ end
44
+
45
+ item = {
46
+ mfg_number: row[@headers.index('Item ID')].strip,
47
+ upc: row[@headers.index('Bar Code')].strip,
48
+ name: row[@headers.index('Description')].strip,
49
+ quantity: row[@headers.index('Qty available')].to_i,
50
+ price: row[@headers.index('Price')].strip,
51
+ brand: row[@headers.index('Brand')].strip,
52
+ category: @category,
53
+ subcategory: @subcategory,
54
+ }
55
+
56
+ items << item
57
+ end
58
+
59
+ tempfile.close
60
+ tempfile.unlink
61
+
62
+ items
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,42 @@
1
+ module OrionWholesale
2
+ # Category item response structure:
3
+ #
4
+ # {
5
+ # code: "...", # ':category_code' in Catalog response.
6
+ # description: "..." # ':category_description' in Catalog response.
7
+ # }
8
+ class Category < Base
9
+
10
+ def initialize(options = {})
11
+ requires!(options, :username, :password)
12
+ @options = options
13
+ end
14
+
15
+ def self.all(options = {}, &block)
16
+ requires!(options, :username, :password)
17
+ new(options).all(&block)
18
+ end
19
+
20
+ # Returns an array of hashes with category details.
21
+ def all(&block)
22
+ categories = []
23
+
24
+ # Categories are listed in catalog xml, so fetch that.
25
+ catalog = Catalog.new(@options)
26
+ catalog.all do |item|
27
+ categories << {
28
+ code: item[:subcategory].gsub("&amp;", "").gsub(/[^A-Za-z0-9]/, "_").squeeze("_").downcase,
29
+ description: item[:subcategory].gsub("&amp;", "&")
30
+ }
31
+ end
32
+
33
+ categories.uniq! { |c| c[:code] }
34
+ categories.sort_by! { |c| c[:code] }
35
+
36
+ categories.each do |category|
37
+ yield category
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,76 @@
1
+ module OrionWholesale
2
+ class Inventory < Base
3
+
4
+ CATALOG_DIR = '/ammoready'.freeze
5
+ CATALOG_FILENAME_PREFIX = 'orion_inv_web'.freeze
6
+
7
+ def initialize(options = {})
8
+ requires!(options, :username, :password)
9
+ @options = options
10
+ end
11
+
12
+ def self.all(options = {})
13
+ requires!(options, :username, :password)
14
+ new(options).all
15
+ end
16
+
17
+ def self.quantity(options = {})
18
+ requires!(options, :username, :password)
19
+ new(options).quantity
20
+ end
21
+
22
+ def all
23
+ tempfile = get_most_recent_file(CATALOG_FILENAME_PREFIX, CATALOG_DIR)
24
+ items = []
25
+
26
+ File.open(tempfile).each_with_index do |row, i|
27
+ row = row.split("\t")
28
+
29
+ if i==0
30
+ @headers = row
31
+ next
32
+ end
33
+
34
+ item = {
35
+ item_identifier: row[@headers.index('Item ID')].strip,
36
+ quantity: row[@headers.index('Qty available')].to_i,
37
+ price: row[@headers.index('Price')].strip,
38
+ }
39
+
40
+ items << item
41
+ end
42
+
43
+ tempfile.close
44
+ tempfile.unlink
45
+
46
+ items
47
+ end
48
+
49
+ def quantity
50
+ tempfile = get_most_recent_file(CATALOG_FILENAME_PREFIX, CATALOG_DIR)
51
+ items = []
52
+
53
+ File.open(tempfile).each_with_index do |row, i|
54
+ row = row.split("\t")
55
+
56
+ if i==0
57
+ @headers = row
58
+ next
59
+ end
60
+
61
+ item = {
62
+ item_identifier: row[@headers.index('Item ID')].strip,
63
+ quantity: row[@headers.index('Qty available')].to_i,
64
+ }
65
+
66
+ items << item
67
+ end
68
+
69
+ tempfile.close
70
+ tempfile.unlink
71
+
72
+ items
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,102 @@
1
+ module OrionWholesale
2
+ # To submit an order:
3
+ #
4
+ # * Instantiate a new Order, passing in `:username`
5
+ # * Call {#add_recipient}
6
+ # * Call {#add_item} for each item on the order
7
+ #
8
+ # See each method for a list of required options.
9
+ class Order < Base
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
+
30
+ # @option options [String] :username *required*
31
+ def initialize(options = {})
32
+ requires!(options, :username, :po_number)
33
+
34
+ @dealer_number = options[:username]
35
+ @po_number = options[:po_number]
36
+ @items = []
37
+ end
38
+
39
+ # @param header [Hash]
40
+ # * :dealer_name [String] *required*
41
+ # * :ffl [String]
42
+ # * :shipping [Hash] *required*
43
+ # * :name [String] *required*
44
+ # * :address [String] *required*
45
+ # * :city [String] *required*
46
+ # * :state [String] *required*
47
+ # * :zip [String] *required*
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
55
+ end
56
+
57
+ # @param item [Hash]
58
+ # * :identifier [String] *required*
59
+ # * :description [String]
60
+ # * :upc [String] *required*
61
+ # * :qty [Integer] *required*
62
+ # * :price [String]
63
+ def add_item(item = {})
64
+ requires!(item, :identifier, :upc, :qty)
65
+ @items << item
66
+ end
67
+
68
+ def filename
69
+ return @filename if defined?(@filename)
70
+ timestamp = Time.now.strftime('%Y%m%d%T').gsub(':', '')
71
+ @filename = "ORION-#{@po_number}-#{timestamp}.csv"
72
+ end
73
+
74
+ def to_csv
75
+ CSV.generate(headers: true) do |csv|
76
+ csv << HEADERS
77
+
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
+ ]
97
+ end
98
+ end # CSV.generate
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,40 @@
1
+ module OrionWholesale
2
+ class Tracking < Base
3
+
4
+ BASE_TRACKING_FILE_DIRECTORY = "%s-TRACK".freeze
5
+ BASE_TRACKING_FILE_NAME = "%s_PackageTracking.csv".freeze
6
+ CARRIER = 'UPS'.freeze
7
+
8
+ def initialize(options = {})
9
+ requires!(options, :username, :password, :dealer_number)
10
+
11
+ @options = options
12
+ @dealer_number = options[:dealer_number]
13
+ end
14
+
15
+ def self.fetch_data(options = {})
16
+ requires!(options, :username, :password, :dealer_number)
17
+
18
+ new(options).fetch_data
19
+ end
20
+
21
+ def fetch_data
22
+ tracking_file_name = BASE_TRACKING_FILE_NAME % @dealer_number
23
+ tracking_file_directory = BASE_TRACKING_FILE_DIRECTORY % @dealer_number
24
+ tracking_file = get_file(tracking_file_name, tracking_file_directory)
25
+
26
+ tracking_details = CSV.foreach(tracking_file, { headers: :first_row }).map do |row|
27
+ {
28
+ po_number: row['USR_VEND_ORD_NO'],
29
+ carrier: CARRIER,
30
+ tracking_numbers: [row['TRK_NO_1'], row['TRK_NO_2'], row['TRK_NO_3'], row['TRK_NO_4'], row['TRK_NO_5']].reject(&:blank?),
31
+ }
32
+ end
33
+
34
+ tracking_file.unlink
35
+
36
+ tracking_details
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module OrionWholesale
2
+ class User < Base
3
+
4
+ def initialize(options = {})
5
+ requires!(options, :username, :password)
6
+ @options = options
7
+ end
8
+
9
+ def authenticated?
10
+ connect(@options) { |ftp| ftp.pwd }
11
+ true
12
+ rescue OrionWholesale::NotAuthenticated
13
+ false
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module OrionWholesale
2
+ VERSION = '1.0.0'.freeze
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'orion_wholesale/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "orion_wholesale"
8
+ spec.version = OrionWholesale::VERSION
9
+ spec.authors = ["Tony Beninate"]
10
+ spec.email = ["tonybeninate@icloud.com"]
11
+
12
+ spec.summary = %q{Ruby library for OrionWholesale}
13
+ spec.description = %q{}
14
+ spec.homepage = ""
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency "activesupport", ">= 5", "< 7"
25
+
26
+ spec.add_development_dependency "bundler", ">= 1.15"
27
+ spec.add_development_dependency "rake", "~> 12.3.0"
28
+ spec.add_development_dependency "rspec", "~> 3.7"
29
+ spec.add_development_dependency "net-sftp"
30
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: orion_wholesale
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tony Beninate
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.15'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.15'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 12.3.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 12.3.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.7'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.7'
75
+ - !ruby/object:Gem::Dependency
76
+ name: net-sftp
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: ''
90
+ email:
91
+ - tonybeninate@icloud.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - Gemfile
98
+ - Gemfile.lock
99
+ - LICENSE.txt
100
+ - README.md
101
+ - Rakefile
102
+ - bin/console
103
+ - bin/setup
104
+ - lib/orion_wholesale.rb
105
+ - lib/orion_wholesale/base.rb
106
+ - lib/orion_wholesale/catalog.rb
107
+ - lib/orion_wholesale/category.rb
108
+ - lib/orion_wholesale/inventory.rb
109
+ - lib/orion_wholesale/order.rb
110
+ - lib/orion_wholesale/tracking.rb
111
+ - lib/orion_wholesale/user.rb
112
+ - lib/orion_wholesale/version.rb
113
+ - orion_wholesale.gemspec
114
+ homepage: ''
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 3.0.8
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Ruby library for OrionWholesale
137
+ test_files: []