belpost 0.4.0 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d81d235b373255e2bff25ee56958cd6658a44e9546560ae96486986d278c1f0b
4
- data.tar.gz: a155cca07102f767c12b05bf2c574fdb827f46cae00357e0b213c4daf0d35c84
3
+ metadata.gz: 9573611d63a9e36e61ada02613b8e8764fb2a45125a17f5938f2521b0a80586d
4
+ data.tar.gz: 1db61fe84045560aac6dc7e362aeb5698413851d23851c1b2601f84ca1ff6bc3
5
5
  SHA512:
6
- metadata.gz: cf7a95cd9ae059a9f0d4e10c3ac7e24cf7f94447817d9d104a58a2d2eb9382f4c13ea1fe5bb512589a9e92141ea92100632864a0637ac47150eed848af1d11d7
7
- data.tar.gz: 4fa75a2f2af404733bcf1eff337ac0c102629d8c8ae67085e44858174e9f17975ffb70cced4e5444834801419391e484c340e5d9c87813ec1ce105f9616e2687
6
+ metadata.gz: 4ee356c1da9fad9db8b62a6d5bbe135c317b4c49cd818e856f8a81e70c8452dc98deeb03ffc59dd4d108efac8434c4ed1e9ce0ba5dcca41b23485f327255a26c
7
+ data.tar.gz: 40826b94cf8e911fc7930c5dae67412f4688ccc68f43bed53d5d5317d3146c848fd0f95111e24706cca2a5907b76d54fadf3d53c93eaa6a91aef8dc11f7190f9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0] - 2025-04-15
4
+
5
+ ### Changed
6
+ - Refactored API paths into a separate module `Belpost::ApiPaths` for better maintainability
7
+ - Moved all hardcoded API endpoints into constants in `api_paths.rb`
8
+ - Updated all client methods to use the new API path constants
9
+ - Updated tests to use the new API path constants
10
+
11
+ ### Added
12
+ - Added batch retrieval functionality via `find_batch_by_id` method
13
+ - Support for finding batch mailings by their ID with validation
14
+ - New module `Belpost::ApiPaths` with organized API endpoint constants:
15
+ - Batch mailing paths
16
+ - Postal deliveries paths
17
+ - Geo directory paths
18
+
19
+ ## [0.8.0] - 2025-04-14
20
+ ### Added
21
+ - Added batch mailing functionality via `create_batch` method
22
+ - Support for creating batch mailings with various delivery types
23
+ - Added comprehensive validation for batch mailing data
24
+ - Added new models and schemas for batch mailing
25
+ - Added tests for batch mailing functionality
26
+
27
+ ## [0.7.0] - 2025-04-01
28
+ ### Added
29
+ - Added postal code search functionality via `search_postcode` method
30
+ - Support for searching postal codes by city, street, and building number
31
+ - Added comprehensive tests for the new postal code search endpoint
32
+
33
+ ## [0.6.0] - 2025-04-01
34
+ ### Added
35
+ - Added search addresses search functionality via `find_address_by_string` method
36
+
37
+ ## [0.5.1] - 2025-04-01
38
+ ### Fixed
39
+ - Improved error handling for invalid timeout environment variable
40
+ - Added fallback for BELPOST_API_URL environment variable to make tests more robust
41
+ - Fixed Configuration class by implementing `validate!` and `to_h` methods
42
+
43
+ ## [0.5.0] - 2025-04-01
44
+ ### Added
45
+ - Added address search functionality via `find_address_by_string` method
46
+ - Support for query parameters in GET requests
47
+
3
48
  ## [0.4.0] - 2025-04-01
4
49
  ### Added
5
50
  - Added configuration via environment variables
data/README.md CHANGED
@@ -295,6 +295,27 @@ hs_codes = client.fetch_hs_codes
295
295
  puts hs_codes
296
296
  ```
297
297
 
298
+ ### Searching for postal codes
299
+
300
+ ```ruby
301
+ client = Belpost::Client.new
302
+ postcodes = client.search_postcode(
303
+ city: "Витебск",
304
+ street: "Ильинского",
305
+ building: "51/1", # optional
306
+ limit: 50 # optional, default: 50, range: 1-200
307
+ )
308
+ puts postcodes
309
+ ```
310
+
311
+ ### Finding a batch by ID
312
+
313
+ ```ruby
314
+ client = Belpost::Client.new
315
+ batch = client.find_batch_by_id(123)
316
+ puts batch
317
+ ```
318
+
298
319
  ## Error handling
299
320
 
300
321
  The client may throw the following exceptions:
data/belpost.gemspec CHANGED
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
 
35
35
  spec.add_dependency "dotenv"
36
36
  spec.add_dependency "dry-validation", "~> 1.0"
37
+ spec.add_dependency "dry-schema", "~> 1.0"
37
38
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belpost
4
+ # Constants for API paths
5
+ module ApiPaths
6
+ # Batch mailing paths
7
+ BATCH_MAILING_LIST = "/api/v1/business/batch-mailing/list"
8
+ BATCH_MAILING_DOCUMENTS = "/api/v1/business/batch-mailing/documents"
9
+ BATCH_MAILING_ITEM = "/api/v1/business/batch-mailing/item"
10
+ BATCH_MAILING_DUPLICATE = "/api/v1/business/batch-mailing/list/:id/duplicate"
11
+ BATCH_MAILING_DUPLICATE_FULL = "/api/v1/business/batch-mailing/list/:id/duplicate-full"
12
+ BATCH_MAILING_COMMIT = "/api/v1/business/batch-mailing/list/:id/commit"
13
+ BATCH_MAILING_GENERATE_BLANK = "/api/v1/business/batch-mailing/list/:id/generate-blank"
14
+
15
+ # Postal deliveries paths
16
+ POSTAL_DELIVERIES = "/api/v1/business/postal-deliveries"
17
+ POSTAL_DELIVERIES_HS_CODES = "/api/v1/business/postal-deliveries/hs-codes/list"
18
+ POSTAL_DELIVERIES_VALIDATION = "/api/v1/business/postal-deliveries/validation"
19
+ POSTAL_DELIVERIES_COUNTRIES = "/api/v1/business/postal-deliveries/countries"
20
+
21
+ # Geo directory paths
22
+ GEO_DIRECTORY_SEARCH_ADDRESS = "/api/v1/business/geo-directory/search-address"
23
+ GEO_DIRECTORY_POSTCODE = "/api/v1/business/geo-directory/postcode"
24
+ end
25
+ end
@@ -23,10 +23,12 @@ module Belpost
23
23
  # Performs a GET request to the specified path.
24
24
  #
25
25
  # @param path [String] The API endpoint path.
26
+ # @param params [Hash] The query parameters (default: {}).
26
27
  # @return [Models::ApiResponse] The parsed JSON response from the API.
27
- def get(path)
28
+ def get(path, params = {})
28
29
  Retry.with_retry do
29
30
  uri = URI("#{@base_url}#{path}")
31
+ uri.query = URI.encode_www_form(params) unless params.empty?
30
32
  request = Net::HTTP::Get.new(uri)
31
33
  add_headers(request)
32
34
 
@@ -149,7 +151,6 @@ module Belpost
149
151
  http = Net::HTTP.new(uri.host, uri.port)
150
152
  http.use_ssl = true if uri.scheme == "https"
151
153
  http.read_timeout = @timeout
152
-
153
154
  begin
154
155
  response = http.request(request)
155
156
  handle_response(response)
@@ -189,7 +190,7 @@ module Belpost
189
190
 
190
191
  def handle_response(response)
191
192
  case response.code
192
- when "200"
193
+ when "200", "201"
193
194
  response
194
195
  when "401", "403"
195
196
  raise AuthenticationError.new(
@@ -2,7 +2,11 @@
2
2
 
3
3
  require_relative "api_service"
4
4
  require_relative "models/parcel"
5
+ require_relative "models/batch"
5
6
  require_relative "models/api_response"
7
+ require_relative "validations/address_schema"
8
+ require_relative "validations/batch_schema"
9
+ require_relative "api_paths"
6
10
 
7
11
  module Belpost
8
12
  # Main client class for interacting with the BelPost API.
@@ -22,6 +26,23 @@ module Belpost
22
26
  )
23
27
  end
24
28
 
29
+ # Creates a new batch mailing by sending a POST request to the API.
30
+ #
31
+ # @param batch_data [Hash] The data for the batch mailing.
32
+ # @return [Hash] The parsed JSON response from the API.
33
+ # @raise [Belpost::InvalidRequestError] If the request data is invalid.
34
+ # @raise [Belpost::ApiError] If the API returns an error response.
35
+ def create_batch(batch_data)
36
+ validation_result = Validation::BatchSchema.new.call(batch_data)
37
+ unless validation_result.success?
38
+ raise ValidationError, "Invalid batch data: #{validation_result.errors.to_h}"
39
+ end
40
+
41
+ batch = Models::Batch.new(batch_data)
42
+ response = @api_service.post(ApiPaths::BATCH_MAILING_LIST, batch.to_h)
43
+ response.to_h
44
+ end
45
+
25
46
  # Creates a new postal parcel by sending a POST request to the API.
26
47
  #
27
48
  # @param parcel_data [Hash] The data for the postal parcel.
@@ -35,7 +56,7 @@ module Belpost
35
56
  end
36
57
 
37
58
  parcel = Models::Parcel.new(parcel_data)
38
- response = @api_service.post("/api/v1/business/postal-deliveries", parcel.to_h)
59
+ response = @api_service.post(ApiPaths::POSTAL_DELIVERIES, parcel.to_h)
39
60
  response.to_h
40
61
  end
41
62
 
@@ -44,7 +65,7 @@ module Belpost
44
65
  # @return [Array<Hash>] The HS codes tree as an array of hashes.
45
66
  # @raise [Belpost::ApiError] If the API returns an error response.
46
67
  def fetch_hs_codes
47
- response = @api_service.get("/api/v1/business/postal-deliveries/hs-codes/list")
68
+ response = @api_service.get(ApiPaths::POSTAL_DELIVERIES_HS_CODES)
48
69
  response.to_h
49
70
  end
50
71
 
@@ -55,7 +76,7 @@ module Belpost
55
76
  # @raise [Belpost::ApiError] If the API returns an error response.
56
77
  def validate_postal_delivery(country_code)
57
78
  country_code = country_code.upcase
58
- response = @api_service.get("/api/v1/business/postal-deliveries/validation/#{country_code}")
79
+ response = @api_service.get("#{ApiPaths::POSTAL_DELIVERIES_VALIDATION}/#{country_code}")
59
80
  response.to_h
60
81
  end
61
82
 
@@ -64,8 +85,91 @@ module Belpost
64
85
  # @return [Hash] The parsed JSON response containing available countries.
65
86
  # @raise [Belpost::ApiError] If the API returns an error response.
66
87
  def fetch_available_countries
67
- response = @api_service.get("/api/v1/business/postal-deliveries/countries")
88
+ response = @api_service.get(ApiPaths::POSTAL_DELIVERIES_COUNTRIES)
68
89
  response.to_h
69
90
  end
91
+
92
+ # Finds a batch by its ID.
93
+ #
94
+ # @param id [Integer] The ID of the batch to find.
95
+ # @return [Hash] The batch data if found.
96
+ # @raise [Belpost::ApiError] If the API returns an error response.
97
+ def find_batch_by_id(id)
98
+ raise ValidationError, "ID must be provided" if id.nil?
99
+ raise ValidationError, "ID must be a positive integer" unless id.is_a?(Integer) && id.positive?
100
+
101
+ response = @api_service.get("#{ApiPaths::BATCH_MAILING_LIST}/#{id}")
102
+ response.to_h
103
+ end
104
+
105
+ # Allows you to find an address by a string.
106
+ #
107
+ # Accepts a string with an address in any form and returns found addresses (up to 50 records).
108
+ # Building numbers should be specified without spaces: "building number""letter""building".
109
+ # The letter should be uppercase, and "building" (or "корп", "кор", "к") should be replaced with "/".
110
+ # Example: "город Минск улица Автодоровская 3Е корпус 4" should be transformed to "город Минск улица Автодоровская 3Е/4".
111
+ #
112
+ # @param address [String] The address string to search for.
113
+ # @return [Array<Hash>] An array of found addresses with postcode, region, city, street and other information.
114
+ # @raise [Belpost::ApiError] If the API returns an error response.
115
+ # @raise [Belpost::InvalidRequestError] If the address parameter is missing or has an incorrect format.
116
+ def find_address_by_string(address)
117
+ raise ValidationError, "Address must be filled" if address.nil?
118
+ raise ValidationError, "Address must be a string" unless address.is_a?(String)
119
+ raise ValidationError, "Address must be filled" if address.empty?
120
+
121
+ formatted_address = format_address(address)
122
+ response = @api_service.get(ApiPaths::GEO_DIRECTORY_SEARCH_ADDRESS, { search: formatted_address })
123
+ response.to_h
124
+ end
125
+
126
+ # Searches for postal codes by city, street, and building number.
127
+ #
128
+ # @param city [String] The city name (required)
129
+ # @param street [String] The street name (required)
130
+ # @param building [String] The building number (optional)
131
+ # @param limit [Integer] Maximum number of results (optional, default: 50, range: 1-200)
132
+ # @return [Array<Hash>] An array of found addresses with postcode, region, city, street and other information
133
+ # @raise [Belpost::ValidationError] If required parameters are missing or invalid
134
+ # @raise [Belpost::ApiError] If the API returns an error response
135
+ def search_postcode(city:, street:, building: nil, limit: 50)
136
+ raise ValidationError, "City must be filled" if city.nil?
137
+ raise ValidationError, "City must be a string" unless city.is_a?(String)
138
+ raise ValidationError, "City must be filled" if city.empty?
139
+ raise ValidationError, "Street must be filled" if street.nil?
140
+ raise ValidationError, "Street must be a string" unless street.is_a?(String)
141
+ raise ValidationError, "Street must be filled" if street.empty?
142
+ raise ValidationError, "Building must be a string" if building && !building.is_a?(String)
143
+ raise ValidationError, "Limit must be between 1 and 200" if limit < 1 || limit > 200
144
+
145
+ params = { city: city, street: street }
146
+ params[:building] = format_building_number(building) if building
147
+ params[:limit] = limit
148
+
149
+ response = @api_service.get(ApiPaths::GEO_DIRECTORY_POSTCODE, params)
150
+ response.to_h
151
+ end
152
+
153
+ private
154
+
155
+ def format_address(address)
156
+ address.gsub(/\s+/, " ")
157
+ .gsub(/\s*корпус\s*(\d+)\s*/i, '/\1')
158
+ .gsub(/\s*корп\s*(\d+)\s*/i, '/\1')
159
+ .gsub(/\s*кор\s*(\d+)\s*/i, '/\1')
160
+ .gsub(/\s*к\s*(\d+)\s*/i, '/\1')
161
+ .strip
162
+ end
163
+
164
+ def format_building_number(building)
165
+ return building unless building
166
+
167
+ building.gsub(/\s+/, " ")
168
+ .gsub(/\s*корпус\s*(\d+)\s*/i, '/\1')
169
+ .gsub(/\s*корп\s*(\d+)\s*/i, '/\1')
170
+ .gsub(/\s*кор\s*(\d+)\s*/i, '/\1')
171
+ .gsub(/\s*к\s*(\d+)\s*/i, '/\1')
172
+ .strip
173
+ end
70
174
  end
71
175
  end
@@ -7,9 +7,32 @@ module Belpost
7
7
  attr_accessor :base_url, :jwt_token, :timeout
8
8
 
9
9
  def initialize
10
- @base_url = ENV.fetch("BELPOST_API_URL")
10
+ @base_url = ENV.fetch("BELPOST_API_URL", "https://api.belpost.by")
11
11
  @jwt_token = ENV.fetch("BELPOST_JWT_TOKEN", nil)
12
- @timeout = ENV.fetch("BELPOST_TIMEOUT", 10).to_i
12
+
13
+ # Convert timeout to integer with a fallback to default
14
+ begin
15
+ @timeout = Integer(ENV.fetch("BELPOST_TIMEOUT", 10))
16
+ rescue ArgumentError
17
+ @timeout = 10
18
+ end
19
+ end
20
+
21
+ # Validates that all required configuration is present
22
+ # @raise [Belpost::ConfigurationError] If required configuration is missing
23
+ def validate!
24
+ raise ConfigurationError, "Base URL is required" if base_url.nil?
25
+ raise ConfigurationError, "JWT token is required" if jwt_token.nil?
26
+ end
27
+
28
+ # Returns a hash representation of the configuration
29
+ # @return [Hash] The configuration as a hash
30
+ def to_h
31
+ {
32
+ base_url: base_url,
33
+ jwt_token: jwt_token,
34
+ timeout: timeout
35
+ }
13
36
  end
14
37
  end
15
38
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belpost
4
+ module Models
5
+ # Model class for batch mailing
6
+ class Batch
7
+ attr_reader :postal_delivery_type, :direction, :payment_type, :negotiated_rate,
8
+ :name, :card_number, :postal_items_in_ops, :category, :is_document,
9
+ :is_declared_value, :is_partial_receipt
10
+
11
+ def initialize(data)
12
+ @postal_delivery_type = data[:postal_delivery_type]
13
+ @direction = data[:direction]
14
+ @payment_type = data[:payment_type]
15
+ @negotiated_rate = data[:negotiated_rate]
16
+ @name = data[:name]
17
+ @card_number = data[:card_number]
18
+ @postal_items_in_ops = data[:postal_items_in_ops]
19
+ @category = data[:category]
20
+ @is_document = data[:is_document]
21
+ @is_declared_value = data[:is_declared_value]
22
+ @is_partial_receipt = data[:is_partial_receipt]
23
+ end
24
+
25
+ def to_h
26
+ {
27
+ postal_delivery_type: @postal_delivery_type,
28
+ direction: @direction,
29
+ payment_type: @payment_type,
30
+ negotiated_rate: @negotiated_rate,
31
+ name: @name,
32
+ card_number: @card_number,
33
+ postal_items_in_ops: @postal_items_in_ops,
34
+ category: @category,
35
+ is_document: @is_document,
36
+ is_declared_value: @is_declared_value,
37
+ is_partial_receipt: @is_partial_receipt
38
+ }.compact
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-validation"
4
+
5
+ module Belpost
6
+ module Validation
7
+ # Schema for validating and formatting address strings
8
+ class AddressSchema < Dry::Validation::Contract
9
+ params do
10
+ required(:address).filled(:string)
11
+ end
12
+
13
+ rule(:address) do
14
+ # Remove extra spaces
15
+ address = value.gsub(/\s+/, " ").strip
16
+
17
+ # Replace building indicators with "/"
18
+ address = address.gsub(/\s+(корпус|корп|кор|к)\s+/, "/")
19
+
20
+ # Ensure building number format (no spaces between number, letter, and building)
21
+ address = address.gsub(/(\d+)\s*([А-Я])\s*(\d+)/, '\1\2/\3')
22
+
23
+ # Ensure letter is uppercase
24
+ address = address.gsub(/(\d+)([а-я])(\/\d+)/) { |m| "#{$1}#{$2.upcase}#{$3}" }
25
+
26
+ key.failure(address)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-validation"
4
+
5
+ module Belpost
6
+ module Validation
7
+ # Schema for validating batch mailing data
8
+ class BatchSchema < Dry::Validation::Contract
9
+ params do
10
+ required(:postal_delivery_type).filled(:str?).value(
11
+ included_in?: %w[
12
+ ordered_small_package
13
+ letter_declare_value
14
+ package
15
+ ems
16
+ ordered_parcel_post
17
+ ordered_letter
18
+ ordered_postcard
19
+ small_package_declare_value
20
+ package_declare_value
21
+ ecommerce_economical
22
+ ecommerce_standard
23
+ ecommerce_elite
24
+ ecommerce_express
25
+ ecommerce_light
26
+ ecommerce_optima
27
+ ]
28
+ )
29
+
30
+ required(:direction).filled(:str?).value(
31
+ included_in?: %w[internal CIS international]
32
+ )
33
+
34
+ required(:payment_type).filled(:str?).value(
35
+ included_in?: %w[
36
+ not_specified
37
+ cash
38
+ payment_order
39
+ electronic_personal_account
40
+ 4
41
+ 5
42
+ commitment_letter
43
+ ]
44
+ )
45
+
46
+ required(:negotiated_rate).filled(:bool?)
47
+
48
+ optional(:name).maybe(:str?)
49
+ optional(:card_number).maybe(:str?)
50
+ optional(:postal_items_in_ops).maybe(:bool?)
51
+ optional(:category).maybe(:int?)
52
+ optional(:is_document).maybe(:bool?)
53
+ optional(:is_declared_value).maybe(:bool?)
54
+ optional(:is_partial_receipt).maybe(:bool?)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,154 +1,172 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/schema"
3
+ require "dry-validation"
4
4
 
5
5
  module Belpost
6
6
  # rubocop:disable Metrics/ModuleLength
7
7
  module Validation
8
8
  # Validation schema for parcel_data
9
9
  # rubocop:disable Metrics/BlockLength
10
- ParcelSchema = Dry::Schema.JSON do
11
- required(:parcel).hash do
12
- required(:type).filled(:string, included_in?: %w[package small_package small_valued_package ems])
13
- required(:attachment_type).filled(:string, included_in?: %w[products documents])
14
- required(:measures).hash do
15
- required(:weight).filled(:integer, gt?: 0)
16
- optional(:long).value(:integer, gt?: 0)
17
- optional(:width).value(:integer, gt?: 0)
18
- optional(:height).value(:integer, gt?: 0)
19
- end
20
- required(:departure).hash do
21
- required(:country).filled(:string, eql?: "BY")
22
- required(:place).filled(:string, included_in?: %w[post_office])
23
- optional(:place_uuid).maybe(:string)
24
- end
25
- required(:arrival).hash do
26
- required(:country).filled(:string)
27
- required(:place).filled(:string, included_in?: %w[post_office])
28
- optional(:place_uuid).maybe(:string)
10
+ class ParcelSchema < Dry::Validation::Contract
11
+ params do
12
+ required(:parcel).hash do
13
+ required(:type).filled(:string, included_in?: %w[package small_package small_valued_package ems])
14
+ required(:attachment_type).filled(:string, included_in?: %w[products documents])
15
+ required(:measures).hash do
16
+ required(:weight).filled(:integer, gt?: 0)
17
+ optional(:long).value(:integer, gt?: 0)
18
+ optional(:width).value(:integer, gt?: 0)
19
+ optional(:height).value(:integer, gt?: 0)
20
+ end
21
+ required(:departure).hash do
22
+ required(:country).filled(:string, eql?: "BY")
23
+ required(:place).filled(:string, included_in?: %w[post_office])
24
+ optional(:place_uuid).maybe(:string)
25
+ end
26
+ required(:arrival).hash do
27
+ required(:country).filled(:string)
28
+ required(:place).filled(:string, included_in?: %w[post_office])
29
+ optional(:place_uuid).maybe(:string)
30
+ end
31
+ optional(:s10code).maybe(:string)
29
32
  end
30
- optional(:s10code).maybe(:string)
31
- end
32
33
 
33
- optional(:addons).hash do
34
- optional(:cash_on_delivery).maybe(:hash) do
35
- required(:currency).filled(:string, eql?: "BYN")
36
- required(:value).filled(:float, gt?: 0)
37
- end
38
- optional(:declared_value).maybe(:hash) do
39
- required(:currency).filled(:string, eql?: "BYN")
40
- required(:value).filled(:float, gt?: 0)
41
- end
42
- optional(:IOSS).maybe(:string)
43
- optional(:registered_notification).maybe(:string)
44
- optional(:simple_notification).maybe(:bool)
45
- optional(:sms_notification).maybe(:bool)
46
- optional(:email_notification).maybe(:bool)
47
- optional(:priority_parcel).maybe(:bool)
48
- optional(:attachment_inventory).maybe(:bool)
49
- optional(:paid_shipping).maybe(:bool)
50
- optional(:careful_fragile).maybe(:bool)
51
- optional(:bulky).maybe(:bool)
52
- optional(:ponderous).maybe(:bool)
53
- optional(:payment_upon_receipt).maybe(:bool)
54
- optional(:hand_over_personally).maybe(:bool)
55
- optional(:return_of_documents).maybe(:bool)
56
- optional(:open_upon_delivery).maybe(:bool)
57
- optional(:delivery_to_work).maybe(:bool)
58
- optional(:time_of_delivery).maybe(:hash) do
59
- required(:type).filled(:string, included_in?: %w[level1 level2 level3 level4])
60
- optional(:time_interval).hash do
61
- required(:from).filled(:string)
62
- required(:to).filled(:string)
34
+ optional(:addons).hash do
35
+ optional(:cash_on_delivery).maybe(:hash) do
36
+ required(:currency).filled(:string, eql?: "BYN")
37
+ required(:value).filled(:float, gt?: 0)
38
+ end
39
+ optional(:declared_value).maybe(:hash) do
40
+ required(:currency).filled(:string, eql?: "BYN")
41
+ required(:value).filled(:float, gt?: 0)
42
+ end
43
+ optional(:IOSS).maybe(:string)
44
+ optional(:registered_notification).maybe(:string)
45
+ optional(:simple_notification).maybe(:bool)
46
+ optional(:sms_notification).maybe(:bool)
47
+ optional(:email_notification).maybe(:bool)
48
+ optional(:priority_parcel).maybe(:bool)
49
+ optional(:attachment_inventory).maybe(:bool)
50
+ optional(:paid_shipping).maybe(:bool)
51
+ optional(:careful_fragile).maybe(:bool)
52
+ optional(:bulky).maybe(:bool)
53
+ optional(:ponderous).maybe(:bool)
54
+ optional(:payment_upon_receipt).maybe(:bool)
55
+ optional(:hand_over_personally).maybe(:bool)
56
+ optional(:return_of_documents).maybe(:bool)
57
+ optional(:open_upon_delivery).maybe(:bool)
58
+ optional(:delivery_to_work).maybe(:bool)
59
+ optional(:time_of_delivery).maybe(:hash) do
60
+ required(:type).filled(:string, included_in?: %w[level1 level2 level3 level4])
61
+ optional(:time_interval).hash do
62
+ required(:from).filled(:string)
63
+ required(:to).filled(:string)
64
+ end
63
65
  end
64
66
  end
65
- end
66
67
 
67
- required(:sender).hash do
68
- required(:type).filled(:string, included_in?: %w[legal_person sole_proprietor])
69
- required(:info).hash do
70
- required(:organization_name).filled(:string)
71
- optional(:taxpayer_number).maybe(:string)
72
- optional(:bank).maybe(:string)
73
- optional(:IBAN).maybe(:string)
74
- optional(:BIC).maybe(:string)
75
- end
76
- required(:location).hash do
77
- required(:code).filled(:string)
78
- required(:region).filled(:string)
79
- required(:district).filled(:string)
80
- required(:locality).hash do
81
- required(:type).filled(:string)
82
- required(:name).filled(:string)
68
+ optional(:sender).hash do
69
+ required(:type).filled(:string, included_in?: %w[legal_person natural_person])
70
+ required(:info).hash do
71
+ optional(:organization_name).maybe(:string)
72
+ optional(:taxpayer_number).maybe(:string)
73
+ optional(:IBAN).maybe(:string)
74
+ optional(:BIC).maybe(:string)
75
+ optional(:bank).maybe(:string)
76
+ optional(:first_name).maybe(:string)
77
+ optional(:second_name).maybe(:string)
78
+ optional(:last_name).maybe(:string)
83
79
  end
84
- required(:road).hash do
85
- required(:type).filled(:string)
86
- required(:name).filled(:string)
80
+ required(:location).hash do
81
+ required(:code).filled(:string)
82
+ required(:region).filled(:string)
83
+ required(:district).filled(:string)
84
+ required(:locality).hash do
85
+ required(:type).filled(:string)
86
+ required(:name).filled(:string)
87
+ end
88
+ required(:road).hash do
89
+ required(:type).filled(:string)
90
+ required(:name).filled(:string)
91
+ end
92
+ required(:building).filled(:string)
93
+ optional(:housing).maybe(:string)
94
+ optional(:apartment).maybe(:string)
87
95
  end
88
- required(:building).filled(:string)
89
- optional(:housing).maybe(:string)
90
- optional(:apartment).maybe(:string)
96
+ optional(:email).maybe(:string)
97
+ optional(:phone).maybe(:string)
91
98
  end
92
- required(:email).filled(:string)
93
- required(:phone).filled(:string)
94
- end
95
99
 
96
- required(:recipient).hash do
97
- required(:type).filled(:string, included_in?: %w[natural_person legal_person sole_proprietor])
98
- required(:info).hash do
99
- optional(:organization_name).maybe(:string)
100
- optional(:first_name).maybe(:string)
101
- optional(:second_name).maybe(:string)
102
- optional(:last_name).maybe(:string)
103
- end
104
- required(:location).hash do
105
- optional(:code).maybe(:string)
106
- optional(:region).maybe(:string)
107
- optional(:district).maybe(:string)
108
- optional(:locality).maybe(:hash) do
109
- optional(:type).maybe(:string)
110
- optional(:name).maybe(:string)
100
+ optional(:recipient).hash do
101
+ required(:type).filled(:string, included_in?: %w[legal_person natural_person])
102
+ required(:info).hash do
103
+ optional(:organization_name).maybe(:string)
104
+ optional(:taxpayer_number).maybe(:string)
105
+ optional(:IBAN).maybe(:string)
106
+ optional(:BIC).maybe(:string)
107
+ optional(:bank).maybe(:string)
108
+ optional(:first_name).maybe(:string)
109
+ optional(:second_name).maybe(:string)
110
+ optional(:last_name).maybe(:string)
111
111
  end
112
- optional(:road).maybe(:hash) do
113
- optional(:type).maybe(:string)
114
- optional(:name).maybe(:string)
112
+ required(:location).hash do
113
+ required(:code).filled(:string)
114
+ required(:region).filled(:string)
115
+ required(:district).filled(:string)
116
+ required(:locality).hash do
117
+ required(:type).filled(:string)
118
+ required(:name).filled(:string)
119
+ end
120
+ required(:road).hash do
121
+ required(:type).filled(:string)
122
+ required(:name).filled(:string)
123
+ end
124
+ required(:building).filled(:string)
125
+ optional(:housing).maybe(:string)
126
+ optional(:apartment).maybe(:string)
115
127
  end
116
- optional(:building).maybe(:string)
117
- optional(:housing).maybe(:string)
118
- optional(:apartment).maybe(:string)
119
- optional(:address).maybe(:string)
128
+ optional(:email).maybe(:string)
129
+ optional(:phone).maybe(:string)
120
130
  end
121
- optional(:email).maybe(:string)
122
- required(:phone).filled(:string)
123
- end
124
131
 
125
- optional(:cp72).hash do
126
- optional(:items).array(:hash) do
127
- required(:name).filled(:string)
128
- required(:local).filled(:string)
129
- required(:unit).hash do
132
+ optional(:cp72).hash do
133
+ optional(:items).array(:hash) do
134
+ required(:name).filled(:string)
130
135
  required(:local).filled(:string)
131
- required(:en).filled(:string)
136
+ required(:unit).hash do
137
+ required(:local).filled(:string)
138
+ required(:en).filled(:string)
139
+ end
140
+ required(:count).filled(:integer, gt?: 0)
141
+ required(:weight).filled(:integer, gt?: 0)
142
+ required(:price).hash do
143
+ required(:currency).filled(:string)
144
+ required(:value).filled(:float, gt?: 0)
145
+ end
146
+ optional(:code).maybe(:string)
147
+ optional(:country).maybe(:string)
132
148
  end
133
- required(:count).filled(:integer, gt?: 0)
134
- required(:weight).filled(:integer, gt?: 0)
135
- required(:price).hash do
149
+ optional(:price).hash do
136
150
  required(:currency).filled(:string)
137
151
  required(:value).filled(:float, gt?: 0)
138
152
  end
139
- optional(:code).maybe(:string)
140
- optional(:country).maybe(:string)
141
- end
142
- optional(:price).hash do
143
- required(:currency).filled(:string)
144
- required(:value).filled(:float, gt?: 0)
153
+ optional(:category).filled(:string, included_in?: %w[gift documents sample returned_goods merchandise other])
154
+ optional(:explanation).maybe(:string)
155
+ optional(:comments).maybe(:string)
156
+ optional(:invoice).maybe(:string)
157
+ optional(:licences).array(:string)
158
+ optional(:certificates).array(:string)
145
159
  end
146
- optional(:category).filled(:string, included_in?: %w[gift documents sample returned_goods merchandise other])
147
- optional(:explanation).maybe(:string)
148
- optional(:comments).maybe(:string)
149
- optional(:invoice).maybe(:string)
150
- optional(:licences).array(:string)
151
- optional(:certificates).array(:string)
160
+ end
161
+
162
+ # Add class method to make it compatible with the tests
163
+ def self.call(params)
164
+ new.call(params)
165
+ end
166
+
167
+ # Add validation rule for organization_name when sender type is legal_person
168
+ rule('sender.info.organization_name') do
169
+ key.failure('must be filled') if values[:sender] && values[:sender][:type] == 'legal_person' && !values.dig(:sender, :info, :organization_name)
152
170
  end
153
171
  end
154
172
  # rubocop:enable Metrics/BlockLength
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belpost
4
- VERSION = "0.4.0"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belpost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - KuberLite
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: dotenv
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: dry-schema
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
40
54
  description: Gem for working with the 'Belpost' delivery service via API
41
55
  email:
42
56
  - kuberlite@gmail.com
@@ -55,15 +69,19 @@ files:
55
69
  - Rakefile
56
70
  - belpost.gemspec
57
71
  - lib/belpost.rb
72
+ - lib/belpost/api_paths.rb
58
73
  - lib/belpost/api_service.rb
59
74
  - lib/belpost/client.rb
60
75
  - lib/belpost/configuration.rb
61
76
  - lib/belpost/errors.rb
62
77
  - lib/belpost/models/api_response.rb
78
+ - lib/belpost/models/batch.rb
63
79
  - lib/belpost/models/customs_declaration.rb
64
80
  - lib/belpost/models/parcel.rb
65
81
  - lib/belpost/models/parcel_builder.rb
66
82
  - lib/belpost/retry.rb
83
+ - lib/belpost/validations/address_schema.rb
84
+ - lib/belpost/validations/batch_schema.rb
67
85
  - lib/belpost/validations/parcel_schema.rb
68
86
  - lib/belpost/version.rb
69
87
  homepage: https://github.com/KuberLite/belpost
@@ -89,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
107
  - !ruby/object:Gem::Version
90
108
  version: '0'
91
109
  requirements: []
92
- rubygems_version: 3.6.2
110
+ rubygems_version: 3.6.7
93
111
  specification_version: 4
94
112
  summary: Belpost API wrapper
95
113
  test_files: []