vng 0.1.0 → 0.1.14

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: 5db1598d18b48ee1c31a7e367c907bc8ba6c224623dc0590d573889812f27322
4
- data.tar.gz: 3384c1b42509e05a31c85b5335792c9796a9e20bb4985a0ecb0879d7c8f031c5
3
+ metadata.gz: de1ec03096db648933db7f9886eaa72b6c4af73e614e475bc0ad26126f35015e
4
+ data.tar.gz: 0adb9f129d030b78ec726f6a2a1cedd61ebc37c88a88df524aca23ff4d2583ca
5
5
  SHA512:
6
- metadata.gz: c9b6bdd93b8627c1512915dd353a08485047356a7cb7835c7a55dc587ce6eb27193fb4404dad08918e5e291cf22cd1380acf6544279a198eb593305232501f80
7
- data.tar.gz: 115ff16da1a5d29c4fb3a39404da2123332021df2d8bc1b809ce42126483010c4215c1030d1361120bea9943ee0c4e96eca8f0175f3b09e3b95f001d73bc8260
6
+ metadata.gz: ce83ddaea6c34121333fdaf15b5600b2486e51c2803b82a71826ca464b7c18f79f1c5d39e0837a0483ecda73512abae325fda7aea0b6ec7b4f8fac3ebcc201c4
7
+ data.tar.gz: 7efd6a2bc16eae79b5ecde44d85ec4ce4de4366a9c735bebc99350d1c743ccc4a6320d47a834e84a92205524761222d07e43a02b06878ff413ca5ff8219e7962
data/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.14] - 2024-11-17
4
+
5
+ - Adds Vng::Error
6
+
7
+ ## [0.1.13] - 2024-11-15
8
+
9
+ - Enable Vng.configuration.security_token
10
+
11
+ ## [0.1.12] - 2024-11-15
12
+
13
+ - Adds ServiceType.where(zip:)
14
+
15
+ ## [0.1.11] - 2024-11-15
16
+
17
+ - Adds Zip
18
+
19
+ ## [0.1.10] - 2024-11-14
20
+
21
+ - Adds Service Type
22
+
23
+ ## [0.1.9] - 2024-11-13
24
+
25
+ - Adds Franchise
26
+
27
+ ## [0.1.8] - 2024-11-13
28
+
29
+ - Adds Security Token
30
+
31
+ ## [0.1.7] - 2024-11-12
32
+
33
+ - Adds Case
34
+
35
+ ## [0.1.6] - 2024-11-12
36
+
37
+ - Adds WorkOrder
38
+
39
+ ## [0.1.5] - 2024-11-11
40
+
41
+ - Adds Availability
42
+
43
+ ## [0.1.4] - 2024-11-11
44
+
45
+ - Adds Price Item
46
+
47
+ ## [0.1.3] - 2024-11-10
48
+
49
+ - Adds Location
50
+
51
+ ## [0.1.2] - 2024-11-10
52
+
53
+ - Adds Breed
54
+
55
+ ## [0.1.1] - 2024-11-10
56
+
57
+ - Adds Lead.create, Lead#destroy, Contact.create, Contact#destroy
58
+
3
59
  ## [0.1.0] - 2024-11-10
4
60
 
5
61
  - Initial release
data/README.md CHANGED
@@ -1,3 +1,109 @@
1
- # Vng
1
+ Vng - a Ruby client for the Vonigo API
2
+ ======================================================
2
3
 
3
- A Ruby API Client for the Vonigo API
4
+ Vng helps you write apps that need to interact with Vonigo.
5
+
6
+ The **source code** is available on [GitHub](https://github.com/HouseAccountEng/vng) and the **documentation** on [RubyDoc](http://www.rubydoc.info/gems/vng/frames).
7
+
8
+ [![Code Climate](https://codeclimate.com/github/HouseAccountEng/vng.png)](https://codeclimate.com/github/HouseAccountEng/vng)
9
+
10
+ After [registering your app](#configuring-your-app), you can run commands like:
11
+
12
+
13
+ ```ruby
14
+ Vng::Zip.all
15
+ Vng::Franchise.all
16
+ Vng::ServiceType.where zip:
17
+ Vng::SecurityToken.create host:, usename:, password:
18
+ Vng::Lead.create email:, phone:, name: full_name
19
+ Vng::Contact.create first_name:, last_name:, email:, phone:, client_id:
20
+ Vng::Location.create address:, city:, zip:, state: state, client_id:
21
+ Vng::Breed.all
22
+ Vng::Asset.create name:, weight:, breed_option_id:, client_id:
23
+ Vng::PriceItem.where location_id:, asset_id:
24
+ Vng::Availability.where(location_id:, duration:, from_time:, to_time:
25
+ Vng::Lock.create date:, duration: location_id:
26
+ Vng::WorkOrder.create lock_id:, client_id:, contact_id:, location_id:, duration:, summary:, line_items:
27
+ Vng::Case.create client_id:, summary:, comments:
28
+ ```
29
+
30
+ The **full documentation** is available at [rubydoc.info](http://www.rubydoc.info/gems/vng/frames).
31
+
32
+ How to install
33
+ ==============
34
+
35
+ To install on your system, run
36
+
37
+ gem install vng
38
+
39
+ To use inside a bundled Ruby project, add this line to the Gemfile:
40
+
41
+ gem 'vng', '~> 0.1.13'
42
+
43
+ Since the gem follows [Semantic Versioning](http://semver.org),
44
+ indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
45
+ guarantees that your project won’t occur in any error when you `bundle update`
46
+ and a new version of Vng is released.
47
+
48
+ Configuring your app
49
+ ====================
50
+
51
+ In order to use Vng you must have credentials to the [Vonigo](https://www.vonigo.com/) API.
52
+
53
+ Add them to your code with the following snippet of code (replacing with your own credentials):
54
+
55
+ ```ruby
56
+ Vng.configure do |config|
57
+ config.host = 'subdomain.vonigo.com'
58
+ config.username = 'VonigoUser'
59
+ config.password = 'VonigoPassword'
60
+ end
61
+ ```
62
+
63
+ Configuring with environment variables
64
+ --------------------------------------
65
+
66
+ As an alternative to the approach above, you can configure your app with
67
+ variables. Setting the following environment variables:
68
+
69
+ ```bash
70
+ export VNG_HOST="subdomain.vonigo.com"
71
+ export VNG_USERNAME="VonigoUser"
72
+ export VNG_PASSWORD="VonigoPassword"
73
+ ```
74
+
75
+ is equivalent to the previous approach so pick the one you prefer.
76
+ If a variable is set in both places, then `Vng.configure` takes precedence.
77
+
78
+ How to test
79
+ ===========
80
+
81
+ To run tests:
82
+
83
+ ```bash
84
+ rspec
85
+ ```
86
+
87
+ How to release new versions
88
+ ===========================
89
+
90
+ If you are a manager of this project, remember to upgrade the [Vng gem](http://rubygems.org/gems/vng)
91
+ whenever a new feature is added or a bug gets fixed.
92
+ Make sure all the tests are passing and the code is 100% test-covered.
93
+ Document the changes in CHANGELOG.md and README.md, bump the version, then run:
94
+
95
+ rake release
96
+
97
+ Remember that the vng gem follows [Semantic Versioning](http://semver.org).
98
+ Any new release that is fully backward-compatible should bump the *patch* version (0.0.x).
99
+ Any new version that breaks compatibility should bump the *minor* version (0.x.0)
100
+
101
+ How to contribute
102
+ =================
103
+
104
+ Vng needs your support!
105
+ The goal of Vng is to provide a Ruby interface for all the methods exposed by the Vonigo API.
106
+
107
+ If you find that a method is missing, fork the project, add the missing code,
108
+ write the appropriate tests, then submit a pull request, and it will gladly
109
+ be merged!
data/Rakefile CHANGED
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
5
3
 
6
4
  RSpec::Core::RakeTask.new(:spec)
7
5
 
data/lib/vng/asset.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo assets.
5
+ class Asset < Resource
6
+ PATH = '/api/v1/data/Assets/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(name:, weight:, breed_option_id:, client_id:)
15
+ body = {
16
+ method: '3',
17
+ clientID: client_id,
18
+ Fields: [
19
+ {fieldID: 1013, fieldValue: name},
20
+ {fieldID: 1017, fieldValue: weight},
21
+ {fieldID: 1014, optionID: breed_option_id},
22
+ ]
23
+ }
24
+
25
+ data = request path: PATH, body: body
26
+
27
+ # curl = 'curl'.tap do |curl|
28
+ # curl << ' -X POST'
29
+ # request.each_header{|k, v| curl << %Q{ -H "#{k}: #{v}"}}
30
+ # curl << %Q{ -d '#{request.body}'} if request.body
31
+ # curl << %Q{ "#{uri.to_s}"}
32
+ # end
33
+ # puts curl
34
+
35
+ new id: data['Asset']['objectID']
36
+ end
37
+
38
+ def destroy
39
+ body = {
40
+ method: '4',
41
+ objectID: id,
42
+ }
43
+
44
+ data = self.class.request path: PATH, body: body
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,37 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo availabilities.
5
+ class Availability < Resource
6
+ PATH = '/api/v1/resources/availability/'
7
+
8
+ attr_reader :route_id, :date, :minutes
9
+
10
+ def initialize(route_id:, date:, minutes:)
11
+ @route_id = route_id
12
+ @date = date
13
+ @minutes = minutes
14
+ end
15
+
16
+ def self.where(location_id:, duration:, from_time:, to_time:)
17
+ body = {
18
+ method: '0',
19
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
20
+ locationID: location_id,
21
+ duration: [duration.to_i, 30].max, # or 'duration is not provided'
22
+ dateStart: from_time.to_i,
23
+ dateEnd: to_time.to_i,
24
+ }
25
+
26
+ data = request path: PATH, body: body
27
+
28
+ data['Availability'].map do |availability|
29
+ route_id = availability['routeID']
30
+ date = Date.strptime availability['dayID'], '%Y%m%d'
31
+ minutes = availability['startTime'].to_i
32
+
33
+ new route_id: route_id, date: date, minutes: minutes
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/vng/breed.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo breeds.
5
+ class Breed < Resource
6
+ PATH = '/api/v1/resources/breeds/'
7
+
8
+ attr_reader :id, :name, :species, :option_id, :low_weight, :high_weight
9
+
10
+ def initialize(id:, name:, species:, option_id:, low_weight:, high_weight:)
11
+ @id = id
12
+ @name = name
13
+ @species = species
14
+ @option_id = option_id
15
+ @low_weight = low_weight
16
+ @high_weight = high_weight
17
+ end
18
+
19
+ # TODO: Needs pagination
20
+ def self.all
21
+ data = request path: PATH
22
+
23
+ data['Breeds'].map do |breed|
24
+ id = breed['breedID']
25
+ name = breed['breed']
26
+ species = breed['species']
27
+ option_id = breed['optionID']
28
+ low_weight = breed['breedLowWeight']
29
+ high_weight = breed['breedHighWeight']
30
+
31
+ new id: id, name: name, species: species, option_id: option_id, low_weight: low_weight, high_weight: high_weight
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/vng/case.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo cases.
5
+ class Case < Resource
6
+ PATH = '/api/v1/data/Cases/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(client_id:, summary:, comments:, phone:, email:, zip:)
15
+ body = {
16
+ method: '3',
17
+ clientID: client_id,
18
+ Fields: [
19
+ {fieldID: 219, optionID: 239}, # Status: open
20
+ {fieldID: 220, fieldValue: summary}, # Summary:
21
+ {fieldID: 230, fieldValue: comments}, # Comments:
22
+ {fieldID: 226, optionID: 227}, # Type: 'General request'
23
+ {fieldID: 227, optionID: 232}, # Preferred Contact Method: 'Phone'
24
+ {fieldID: 228, fieldValue: phone}, # Phone Me Back at:
25
+ {fieldID: 229, fieldValue: email}, # Email:
26
+ {fieldID: 1023, fieldValue: zip}, # Zip Code:
27
+ ]
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ new id: data['Case']['objectID']
33
+ end
34
+
35
+ def destroy
36
+ body = {
37
+ method: '4',
38
+ objectID: id,
39
+ }
40
+
41
+ self.class.request path: PATH, body: body
42
+ end
43
+ end
44
+ end
45
+
data/lib/vng/config.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'vng/configuration'
2
+
3
+ # An object-oriented Ruby client for Voonigo.
4
+ # @see http://www.rubydoc.info/gems/vng/
5
+ module Vng
6
+ # Provides methods to read and write global configuration settings.
7
+ #
8
+ # A typical usage is to set the Security Token for the API calls.
9
+ #
10
+ # @example Set the Security Token for the API client:
11
+ # Vng.configure do |config|
12
+ # config.security_token = 'ABCDEFGHIJ1234567890'
13
+ # end
14
+ #
15
+ module Config
16
+ # Yields the global configuration to the given block.
17
+ #
18
+ # @example
19
+ # Vng.configure do |config|
20
+ # config.security_token = 'ABCDEFGHIJ1234567890'
21
+ # end
22
+ #
23
+ # @yield [Vng::Models::Configuration] The global configuration.
24
+ def configure
25
+ yield configuration if block_given?
26
+ end
27
+
28
+ # Returns the global {Vng::Models::Configuration} object.
29
+ #
30
+ # While this method _can_ be used to read and write configuration settings,
31
+ # it is easier to use {Vng::Config#configure} Vng.configure}.
32
+ #
33
+ # @example
34
+ # Vng.configuration.security_token = 'ABCDEFGHIJ1234567890'
35
+ #
36
+ # @return [Vng::Models::Configuration] The global configuration.
37
+ def configuration
38
+ @configuration ||= Vng::Configuration.new
39
+ end
40
+ end
41
+
42
+ # @note Config is the only module auto-loaded in the Vng module,
43
+ # in order to have a syntax as easy as Vng.configure
44
+ extend Config
45
+ end
@@ -0,0 +1,51 @@
1
+ module Vng
2
+ # Provides an object to store global configuration settings.
3
+ #
4
+ # This class is typically not used directly, but by calling
5
+ # {Vng::Config#configure Vng.configure}, which creates and updates a single
6
+ # instance of {Vng::Models::Configuration}.
7
+ #
8
+ # @example Set the Security Token for the API client:
9
+ # Vng.configure do |config|
10
+ # config.security_token = 'ABCDEFGHIJ1234567890'
11
+ # end
12
+ #
13
+ # @see Vng::Config for more examples.
14
+ #
15
+ # An alternative way to set global configuration settings is by storing
16
+ # them in the following environment variables:
17
+ #
18
+ # * +VNG_HOST+ to store the host for the Vonigo API
19
+ # * +VNG_USERNAME+ to store the username for the Vonigo API
20
+ # * +VNG_PASSWORD+ to store the password for the Vonigo API
21
+ #
22
+ # In case both methods are used together,
23
+ # {Vng::Config#configure Vng.configure} takes precedence.
24
+ #
25
+ # @example Set the API credentials
26
+ # ENV['VNG_HOST'] = 'subdomain.vonigo.com'
27
+ # ENV['VNG_USERNAME'] = 'VonigoUser'
28
+ # ENV['VNG_Password'] = 'VonigoPassword'
29
+ #
30
+ class Configuration
31
+ # @return [String] the Security Token for the API calls.
32
+ attr_accessor :security_token
33
+
34
+ # @return [String] the URI host for the API calls.
35
+ attr_accessor :host
36
+
37
+ # @return [String] the username for the API calls.
38
+ attr_accessor :username
39
+
40
+ # @return [String] the password for the API calls.
41
+ attr_accessor :password
42
+
43
+ # Initialize the global configuration settings, using the values of
44
+ # the specified following environment variables by default.
45
+ def initialize
46
+ @host = ENV['VNG_HOST']
47
+ @username = ENV['VNG_USERNAME']
48
+ @password = ENV['VNG_PASSWORD']
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Vng
2
+ # A wrapper around StandardError.
3
+ class ConnectionError < StandardError
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo contacts.
5
+ class Contact < Resource
6
+ PATH = '/api/v1/data/Contacts/'
7
+
8
+ attr_reader :id, :first_name, :last_name, :email, :phone
9
+
10
+ def initialize(id:, first_name:, last_name:, email:, phone:)
11
+ @id = id
12
+ @first_name = first_name
13
+ @last_name = last_name
14
+ @email = email
15
+ @phone = phone
16
+ end
17
+
18
+ def self.create(first_name:, last_name:, email:, phone:, client_id:)
19
+ body = {
20
+ method: '3',
21
+ clientID: client_id,
22
+ Fields: [
23
+ {fieldID: 127, fieldValue: first_name},
24
+ {fieldID: 128, fieldValue: last_name},
25
+ {fieldID: 97, fieldValue: email},
26
+ {fieldID: 96, fieldValue: phone},
27
+ ]
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ id = data['Contact']['objectID']
33
+ first_name = data['Fields'].find{|field| field['fieldID'] == 127}['fieldValue']
34
+ last_name = data['Fields'].find{|field| field['fieldID'] == 128}['fieldValue']
35
+ email = data['Fields'].find{|field| field['fieldID'] == 97}['fieldValue']
36
+ phone = data['Fields'].find{|field| field['fieldID'] == 96}['fieldValue']
37
+
38
+ new id: id, first_name: first_name, last_name: last_name, email: email, phone: phone
39
+ end
40
+
41
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-1408, "errMsg"=>"Primary contact cannot be deleted."}]
42
+ # def destroy
43
+ # body = {
44
+ # method: '4',
45
+ # objectID: id,
46
+ # }
47
+ #
48
+ # self.class.request path: PATH, body: body
49
+ # end
50
+ end
51
+ end
52
+
data/lib/vng/error.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Vng
2
+ # A wrapper around StandardError.
3
+ class Error < StandardError
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ require 'vng/availability'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo franchises.
5
+ class Franchise < Resource
6
+ PATH = '/api/v1/resources/franchises/'
7
+
8
+ attr_reader :id, :name, :gmt_offset
9
+
10
+ def initialize(id:, name: nil, gmt_offset: nil)
11
+ @id = id
12
+ @name = name
13
+ @gmt_offset = gmt_offset
14
+ end
15
+
16
+
17
+ def self.find_by(zip:)
18
+ body = {
19
+ method: '1',
20
+ zip: zip,
21
+ }
22
+
23
+ data = request path: Vng::Availability::PATH, body: body
24
+
25
+ franchise_id = data['Ids']['franchiseID']
26
+ new(id: franchise_id) unless franchise_id == '0'
27
+ end
28
+
29
+ def self.all
30
+ data = request path: PATH
31
+
32
+ data['Franchises'].filter do |franchise|
33
+ franchise['isActive']
34
+ end.map do |franchise|
35
+ id = franchise['franchiseID']
36
+ name = franchise['franchiseName']
37
+ gmt_offset = franchise['gmtOffsetFranchise']
38
+
39
+ new id: id, name: name, gmt_offset: gmt_offset
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/vng/lead.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo leads.
5
+ class Lead < Resource
6
+ PATH = '/api/v1/data/Leads/'
7
+
8
+ attr_reader :id, :name, :email, :phone
9
+
10
+ def initialize(id:, name:, email:, phone:)
11
+ @id = id
12
+ @name = name
13
+ @email = email
14
+ @phone = phone
15
+ end
16
+
17
+ def self.create(name:, email:, phone:)
18
+ body = {
19
+ method: '3',
20
+ Fields: [
21
+ {fieldID: 121, optionID: '59'},
22
+ {fieldID: 126, fieldValue: name},
23
+ {fieldID: 238, fieldValue: email},
24
+ {fieldID: 1024, fieldValue: phone},
25
+ ]
26
+ }
27
+
28
+ data = request path: PATH, body: body
29
+
30
+ id = data['Client']['objectID']
31
+ name = data['Fields'].find{|field| field['fieldID'] == 126}['fieldValue']
32
+ email = data['Fields'].find{|field| field['fieldID'] == 238}['fieldValue']
33
+ phone = data['Fields'].find{|field| field['fieldID'] == 1024}['fieldValue']
34
+
35
+ new id: id, name: name, email: email, phone: phone
36
+ end
37
+
38
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-1201, "errMsg"=>"Lead ID does not exist."}]
39
+ # TODO: has become an account meanwhile!! so this doesn't work
40
+ # def destroy
41
+ # body = {
42
+ # method: '4',
43
+ # objectID: id,
44
+ # }
45
+ #
46
+ # self.class.request path: PATH, body: body
47
+ # end
48
+ end
49
+ end
@@ -0,0 +1,55 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo locations.
5
+ class Location < Resource
6
+ PATH = '/api/v1/data/Locations/'
7
+
8
+ # TODO: fetch from /system/objects/ method: 1, objectID: 20
9
+ STATES_OPTION_ID = {
10
+ AK: 9879, AL: 9878, AR: 9877, AZ: 9880, CA: 9883, CO: 9876, CT: 9875,
11
+ DC: 9874, DE: 9873, FL: 9872, GA: 9871, HI: 9870, IA: 9869, ID: 9868,
12
+ IL: 9867, IN: 9866, KS: 9865, KY: 9864, LA: 9863, MA: 9862, MD: 9861,
13
+ ME: 9860, MI: 9859, MN: 9858, MO: 9857, MS: 9856, MT: 9855, NC: 9854,
14
+ ND: 9853, NE: 9852, NH: 9851, NJ: 9850, NM: 9849, NV: 9829, NY: 9848,
15
+ OH: 9847, OK: 9846, OR: 9881, PA: 9845, RI: 9843, SC: 9841, SD: 9842,
16
+ TN: 9840, TX: 9839, UT: 9838, VA: 9837, VT: 9836, WA: 9882, WI: 9828,
17
+ WV: 9835, WY: 9834,
18
+ }
19
+
20
+ attr_reader :id
21
+
22
+ def initialize(id:)
23
+ @id = id
24
+ end
25
+
26
+ def self.create(address:, city:, zip:, state:, client_id:)
27
+ body = {
28
+ method: '3',
29
+ clientID: client_id,
30
+ Fields: [
31
+ {fieldID: 779, optionID: 9906}, # 'USA'
32
+ {fieldID: 778, optionID: STATES_OPTION_ID[state.to_sym]},
33
+ {fieldID: 776, fieldValue: city},
34
+ {fieldID: 773, fieldValue: address},
35
+ {fieldID: 775, fieldValue: zip},
36
+ ]
37
+ }
38
+
39
+ data = request path: PATH, body: body
40
+
41
+ new id: data['Location']['objectID']
42
+ end
43
+
44
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>"", "errNo"=>-1809, "errMsg"=>"Primary location cannot be deleted."}]
45
+ # def destroy
46
+ # body = {
47
+ # method: '4',
48
+ # objectID: id,
49
+ # }
50
+ #
51
+ # self.class.request path: PATH, body: body
52
+ # end
53
+ end
54
+ end
55
+
data/lib/vng/lock.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'vng/availability'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo locks.
5
+ class Lock < Resource
6
+ PATH = '/api/v1/resources/availability/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(duration:, location_id:, date:, minutes:, route_id:)
15
+ body = {
16
+ method: '2',
17
+ serviceTypeID: '14', # only create items of serviceType 'Pet Grooming'
18
+ duration: duration.to_i,
19
+ locationID: location_id,
20
+ dayID: date.strftime('%Y%m%d'),
21
+ routeID: route_id,
22
+ startTime: minutes,
23
+ }
24
+
25
+ data = request path: Vng::Availability::PATH, body: body
26
+
27
+ new id: data['Ids']['lockID']
28
+ end
29
+
30
+ # def destroy
31
+ # body = {
32
+ # method: '4',
33
+ # lockID: id,
34
+ # }
35
+ #
36
+ # self.class.request path: PATH, body: body
37
+ # end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo price items.
5
+ class PriceItem < Resource
6
+ PATH = '/api/v1/data/priceLists/'
7
+
8
+ attr_reader :id, :price_item, :value, :tax_id, :duration_per_unit, :service_badge, :service_category
9
+
10
+ def initialize(id:, price_item:, value:, tax_id:, duration_per_unit:, service_badge:, service_category:)
11
+ @id = id
12
+ @price_item = price_item
13
+ @value = value
14
+ @tax_id = tax_id
15
+ @duration_per_unit = duration_per_unit
16
+ @service_badge = service_badge
17
+ @service_category = service_category
18
+ end
19
+
20
+ def self.where(location_id:, asset_id:)
21
+ body = {
22
+ method: '2',
23
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
24
+ locationID: location_id,
25
+ assetID: asset_id,
26
+ }
27
+
28
+ data = request path: PATH, body: body
29
+
30
+ data['PriceItems'].filter do |body|
31
+ body['isOnline'] && body['isActive']
32
+ end.map do |body|
33
+ id = body['priceItemID']
34
+ price_item = body['priceItem']
35
+ value = body['value']
36
+ tax_id = body['taxID']
37
+ duration_per_unit = body['durationPerUnit']
38
+ service_badge = body['serviceBadge']
39
+ service_category = body['serviceCategory']
40
+
41
+ new id: id, price_item: price_item, value: value, tax_id: tax_id, duration_per_unit: duration_per_unit, service_badge: service_badge, service_category: service_category
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ require 'vng/connection_error'
2
+ require 'vng/error'
3
+
4
+ module Vng
5
+ # Provides an abstract class for every Vonigo resource.
6
+ class Resource
7
+ private
8
+ def self.request(path:, body: {}, query: {}, include_security_token: true)
9
+ uri = URI::HTTPS.build host: host, path: path
10
+ uri.query = URI.encode_www_form(query) if query.any?
11
+
12
+ method = query.any? ? Net::HTTP::Get : Net::HTTP::Post
13
+ request = method.new uri.request_uri
14
+ request.initialize_http_header 'Content-Type' => 'application/json'
15
+
16
+ if query.none?
17
+ body = body.merge(securityToken: security_token) if include_security_token
18
+ request.body = body.to_json
19
+ end
20
+
21
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
22
+ http.request request
23
+ end
24
+
25
+ JSON(response.body).tap do |data|
26
+ raise Vng::Error, "#{data['errMsg']} #{data['Errors']}" unless data['errNo'].zero?
27
+ end
28
+ rescue Errno::ECONNREFUSED, SocketError => e
29
+ raise Vng::ConnectionError, e.message
30
+ end
31
+
32
+ def self.host
33
+ Vng.configuration.host
34
+ end
35
+
36
+ def self.security_token
37
+ Vng.configuration.security_token
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,51 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo work security tokens.
5
+ class SecurityToken < Resource
6
+ PATH = '/api/v1/security/login/'
7
+
8
+ attr_reader :token
9
+
10
+ def initialize(token:)
11
+ @token = token
12
+ end
13
+
14
+ def self.create
15
+ body = {
16
+ app_version: '1',
17
+ company: 'Vonigo',
18
+ username: Vng.configuration.username,
19
+ password: Digest::MD5.hexdigest(Vng.configuration.password),
20
+ }
21
+
22
+ data = request path: PATH, body: body, include_security_token: false
23
+
24
+ new token: data['securityToken']
25
+ end
26
+
27
+ # TODO: Check if it's not the correct one already or catch
28
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-5213, "errMsg"=>"Same franchise ID supplied."}]
29
+ def assign_to(franchise_id:)
30
+ body = {
31
+ securityToken: @token,
32
+ method: "2",
33
+ franchiseID: franchise_id,
34
+ }
35
+
36
+ self.class.request path: '/api/v1/security/session/', body: body, include_security_token: false
37
+ rescue Vng::Error => e
38
+ # TODO: improve: ignore if the token was already assigned to the franchise
39
+ raise unless e.message.include? 'Same franchise ID supplied'
40
+ end
41
+
42
+ def destroy
43
+ query = { securityToken: @token }
44
+ self.class.request path: '/api/v1/security/logout/', query: query
45
+ rescue Vng::Error => e
46
+ p "Vng:Error! #{e}"
47
+ # TODO: improve: ignore if the token was already destroyed
48
+ raise unless e.message.include?('Session expired') || e.message.include?('Session does not exist')
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,47 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo service types.
5
+ class ServiceType < Resource
6
+ PATH = '/api/v1/resources/serviceTypes/'
7
+
8
+ attr_reader :id, :type, :duration
9
+
10
+ def initialize(id:, type:, duration:)
11
+ @id = id
12
+ @type = type
13
+ @duration = duration
14
+ end
15
+
16
+ # TODO: Needs pagination
17
+ def self.all
18
+ data = request path: PATH
19
+
20
+ data['ServiceTypes'].map do |body|
21
+ id = body['serviceTypeID']
22
+ type = body['serviceType']
23
+ duration = body['duration']
24
+
25
+ new id: id, type: type, duration: duration
26
+ end
27
+ end
28
+
29
+ def self.where(zip:)
30
+ body = {
31
+ zip: zip,
32
+ }
33
+
34
+ data = request path: PATH, body: body
35
+
36
+ data.fetch('ServiceTypes', []).filter do |body|
37
+ body['isActive']
38
+ end.map do |body|
39
+ id = body['serviceTypeID']
40
+ type = body['serviceType']
41
+ duration = body['duration']
42
+
43
+ new id: id, type: type, duration: duration
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/vng/version.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Vng
4
- VERSION = "0.1.0"
2
+ VERSION = '0.1.14'
5
3
  end
@@ -0,0 +1,70 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo work orders.
5
+ class WorkOrder < Resource
6
+ PATH = '/api/v1/data/WorkOrders/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(lock_id:, client_id:, contact_id:, location_id:, duration:, summary:, line_items:)
15
+ body = {
16
+ method: '3',
17
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
18
+ lockID: lock_id,
19
+ clientID: client_id,
20
+ contactID: contact_id,
21
+ locationID: location_id,
22
+ Fields: [
23
+ {fieldID: 200, fieldValue: summary},
24
+ {fieldID: 186, fieldValue: duration.to_i},
25
+ {fieldID: 201, optionID: '9537'} # label: Online Tentative
26
+ ],
27
+ Charges: charges_for(line_items)
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ new id: data['WorkOrder']['objectID']
33
+ end
34
+
35
+ # TODO: This moves to "archived". actual cancelation is different
36
+ # def destroy
37
+ # body = {
38
+ # method: '8',
39
+ # objectID: id,
40
+ # }
41
+ #
42
+ # data = self.class.request path: PATH, body: body
43
+ # end
44
+
45
+ private
46
+
47
+ def self.charges_for(line_items)
48
+ line_items.map do |line_item|
49
+ {
50
+ priceItemID: line_item[:price_item_id],
51
+ taxID: line_item[:tax_id],
52
+ assetID: line_item[:asset_id],
53
+ Fields: charge_fields_for(line_item)
54
+ }
55
+ end
56
+ end
57
+
58
+ def self.charge_fields_for(line_item)
59
+ [
60
+ {fieldID: 9289, fieldValue: line_item[:description]},
61
+ {fieldID: 9287, fieldValue: line_item[:price]}, # Unit price
62
+ {fieldID: 8673, fieldValue: line_item[:price]}, # Price item
63
+ {fieldID: 813, fieldValue: line_item[:price]}, # Price
64
+ {fieldID: 9286, fieldValue: line_item[:price]}, # Subtotal
65
+ {fieldID: 9283, fieldValue: line_item[:price]}, # Total
66
+ {fieldID: 9288, fieldValue: 1}, # Qty
67
+ ]
68
+ end
69
+ end
70
+ end
data/lib/vng/zip.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo ZIP codes.
5
+ class Zip < Resource
6
+ PATH = '/api/v1/resources/zips/'
7
+
8
+ attr_reader :zip, :state, :zone_name
9
+
10
+ def initialize(zip:, state:, zone_name:)
11
+ @zip = zip
12
+ @state = state
13
+ @zone_name = zone_name
14
+ end
15
+
16
+ # TODO: Needs pagination
17
+ def self.all
18
+ data = request path: PATH
19
+
20
+ data['Zips'].map do |body|
21
+ zip = body['zip']
22
+ state = body['state']
23
+ zone_name = body['zoneName']
24
+
25
+ new zip: zip, state: state, zone_name: zone_name
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/vng.rb CHANGED
@@ -1,8 +1,23 @@
1
- # frozen_string_literal: true
1
+ require 'date'
2
+ require 'digest/md5'
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
2
6
 
3
- require_relative "vng/version"
4
-
5
- module Vng
6
- class Error < StandardError; end
7
- # Your code goes here...
8
- end
7
+ require_relative 'vng/asset'
8
+ require_relative 'vng/availability'
9
+ require_relative 'vng/breed'
10
+ require_relative 'vng/case'
11
+ require_relative 'vng/config'
12
+ require_relative 'vng/connection_error'
13
+ require_relative 'vng/contact'
14
+ require_relative 'vng/franchise'
15
+ require_relative 'vng/lead'
16
+ require_relative 'vng/location'
17
+ require_relative 'vng/lock'
18
+ require_relative 'vng/price_item'
19
+ require_relative 'vng/security_token'
20
+ require_relative 'vng/service_type'
21
+ require_relative 'vng/version'
22
+ require_relative 'vng/work_order'
23
+ require_relative 'vng/zip'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-10 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: simplecov
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: A Ruby client for the Vonigo API.
14
28
  email:
15
29
  - claudiob@users.noreply.github.com
@@ -23,12 +37,31 @@ files:
23
37
  - README.md
24
38
  - Rakefile
25
39
  - lib/vng.rb
40
+ - lib/vng/asset.rb
41
+ - lib/vng/availability.rb
42
+ - lib/vng/breed.rb
43
+ - lib/vng/case.rb
44
+ - lib/vng/config.rb
45
+ - lib/vng/configuration.rb
46
+ - lib/vng/connection_error.rb
47
+ - lib/vng/contact.rb
48
+ - lib/vng/error.rb
49
+ - lib/vng/franchise.rb
50
+ - lib/vng/lead.rb
51
+ - lib/vng/location.rb
52
+ - lib/vng/lock.rb
53
+ - lib/vng/price_item.rb
54
+ - lib/vng/resource.rb
55
+ - lib/vng/security_token.rb
56
+ - lib/vng/service_type.rb
26
57
  - lib/vng/version.rb
27
- homepage: https://github.com/HouseAccountEng/vng
58
+ - lib/vng/work_order.rb
59
+ - lib/vng/zip.rb
60
+ homepage: https://rubygems.org/gems/vng
28
61
  licenses:
29
62
  - MIT
30
63
  metadata:
31
- homepage_uri: https://github.com/HouseAccountEng/vng
64
+ homepage_uri: https://rubygems.org/gems/vng
32
65
  source_code_uri: https://github.com/HouseAccountEng/vng
33
66
  changelog_uri: https://github.com/HouseAccountEng/vng/blob/main/CHANGELOG.md
34
67
  post_install_message: