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 +4 -4
- data/CHANGELOG.md +56 -0
- data/README.md +108 -2
- data/Rakefile +2 -4
- data/lib/vng/asset.rb +48 -0
- data/lib/vng/availability.rb +37 -0
- data/lib/vng/breed.rb +35 -0
- data/lib/vng/case.rb +45 -0
- data/lib/vng/config.rb +45 -0
- data/lib/vng/configuration.rb +51 -0
- data/lib/vng/connection_error.rb +5 -0
- data/lib/vng/contact.rb +52 -0
- data/lib/vng/error.rb +5 -0
- data/lib/vng/franchise.rb +43 -0
- data/lib/vng/lead.rb +49 -0
- data/lib/vng/location.rb +55 -0
- data/lib/vng/lock.rb +39 -0
- data/lib/vng/price_item.rb +45 -0
- data/lib/vng/resource.rb +40 -0
- data/lib/vng/security_token.rb +51 -0
- data/lib/vng/service_type.rb +47 -0
- data/lib/vng/version.rb +1 -3
- data/lib/vng/work_order.rb +70 -0
- data/lib/vng/zip.rb +29 -0
- data/lib/vng.rb +22 -7
- metadata +38 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de1ec03096db648933db7f9886eaa72b6c4af73e614e475bc0ad26126f35015e
|
4
|
+
data.tar.gz: 0adb9f129d030b78ec726f6a2a1cedd61ebc37c88a88df524aca23ff4d2583ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
1
|
+
Vng - a Ruby client for the Vonigo API
|
2
|
+
======================================================
|
2
3
|
|
3
|
-
|
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
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
|
data/lib/vng/contact.rb
ADDED
@@ -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,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
|
data/lib/vng/location.rb
ADDED
@@ -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
|
data/lib/vng/resource.rb
ADDED
@@ -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
@@ -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
|
-
|
1
|
+
require 'date'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
2
6
|
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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.
|
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-
|
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
|
-
|
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://
|
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:
|