econt 0.1.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 +7 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +17 -0
- data/CONTRIBUTING.md +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +171 -0
- data/SECURITY.md +33 -0
- data/docs/API.md +571 -0
- data/docs/ENDPOINTS.md +42 -0
- data/docs/PUBLIC_API.md +81 -0
- data/docs/RELEASING.md +35 -0
- data/docs/RESPONSE_TESTS.md +46 -0
- data/docs/VCR.md +144 -0
- data/lib/econt/camel_case_middleware.rb +88 -0
- data/lib/econt/client.rb +99 -0
- data/lib/econt/collection.rb +59 -0
- data/lib/econt/error.rb +42 -0
- data/lib/econt/meta_resource.rb +6 -0
- data/lib/econt/model.rb +95 -0
- data/lib/econt/models/error.rb +53 -0
- data/lib/econt/models/hosted_file.rb +13 -0
- data/lib/econt/models/nomenclatures/address.rb +35 -0
- data/lib/econt/models/nomenclatures/address_service_info.rb +16 -0
- data/lib/econt/models/nomenclatures/address_service_times_response.rb +19 -0
- data/lib/econt/models/nomenclatures/address_validation_response.rb +19 -0
- data/lib/econt/models/nomenclatures/city.rb +32 -0
- data/lib/econt/models/nomenclatures/country.rb +16 -0
- data/lib/econt/models/nomenclatures/geo_location.rb +13 -0
- data/lib/econt/models/nomenclatures/office.rb +32 -0
- data/lib/econt/models/nomenclatures/quarter.rb +14 -0
- data/lib/econt/models/nomenclatures/serving_office_element.rb +12 -0
- data/lib/econt/models/nomenclatures/street.rb +14 -0
- data/lib/econt/models/nomenclatures/working_date_time.rb +16 -0
- data/lib/econt/models/nomenclatures/working_time.rb +12 -0
- data/lib/econt/models/payment_report/payment_report_row.rb +17 -0
- data/lib/econt/models/profile/cd_pay_options.rb +24 -0
- data/lib/econt/models/profile/client_profile.rb +29 -0
- data/lib/econt/models/profile/profiles_response_element.rb +16 -0
- data/lib/econt/models/shipments/cargo_vehicle_options.rb +16 -0
- data/lib/econt/models/shipments/check_possible_shipment_editions_result_element.rb +14 -0
- data/lib/econt/models/shipments/create_label_result_element.rb +19 -0
- data/lib/econt/models/shipments/customs_list_element.rb +14 -0
- data/lib/econt/models/shipments/delete_labels_result_element.rb +14 -0
- data/lib/econt/models/shipments/get_my_awb_response.rb +15 -0
- data/lib/econt/models/shipments/get_my_awb_result_element.rb +23 -0
- data/lib/econt/models/shipments/instruction.rb +20 -0
- data/lib/econt/models/shipments/next_shipment_element.rb +13 -0
- data/lib/econt/models/shipments/pack_element.rb +14 -0
- data/lib/econt/models/shipments/packing_list_element.rb +17 -0
- data/lib/econt/models/shipments/payment_addition_price.rb +14 -0
- data/lib/econt/models/shipments/payment_instruction.rb +11 -0
- data/lib/econt/models/shipments/previous_shipment.rb +14 -0
- data/lib/econt/models/shipments/request_courier_response.rb +13 -0
- data/lib/econt/models/shipments/request_courier_status.rb +14 -0
- data/lib/econt/models/shipments/request_courier_status_result_element.rb +14 -0
- data/lib/econt/models/shipments/return_instruction_params.rb +48 -0
- data/lib/econt/models/shipments/shipment_edition_response_element.rb +16 -0
- data/lib/econt/models/shipments/shipment_status.rb +64 -0
- data/lib/econt/models/shipments/shipment_status_result_element.rb +14 -0
- data/lib/econt/models/shipments/shipment_status_service.rb +16 -0
- data/lib/econt/models/shipments/shipment_tracking_event.rb +21 -0
- data/lib/econt/models/shipments/shipping_label.rb +59 -0
- data/lib/econt/models/shipments/shipping_label_services.rb +37 -0
- data/lib/econt/models/shipments/status_response.rb +11 -0
- data/lib/econt/models/shipments/update_label_response.rb +11 -0
- data/lib/econt/models/shipments/update_labels_result_element.rb +14 -0
- data/lib/econt/models/three_way_logistics/three_way_logistics.rb +50 -0
- data/lib/econt/models/three_way_logistics/three_way_logistics_response.rb +11 -0
- data/lib/econt/resource_attributes.rb +69 -0
- data/lib/econt/service_resource.rb +105 -0
- data/lib/econt/services/nomenclatures/address.rb +87 -0
- data/lib/econt/services/nomenclatures/nomenclatures.rb +122 -0
- data/lib/econt/services/nomenclatures.rb +11 -0
- data/lib/econt/services/payment_report/payment_report.rb +35 -0
- data/lib/econt/services/payment_report.rb +11 -0
- data/lib/econt/services/profile/profile.rb +45 -0
- data/lib/econt/services/profile.rb +11 -0
- data/lib/econt/services/shipments/label.rb +174 -0
- data/lib/econt/services/shipments/shipment.rb +109 -0
- data/lib/econt/services/shipments.rb +11 -0
- data/lib/econt/services/three_way_logistics/three_way_logistics.rb +30 -0
- data/lib/econt/services/three_way_logistics.rb +11 -0
- data/lib/econt/util.rb +63 -0
- data/lib/econt/version.rb +5 -0
- data/lib/econt.rb +118 -0
- data/sig/econt.rbs +465 -0
- metadata +166 -0
data/docs/API.md
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
# Ruby API reference
|
|
2
|
+
|
|
3
|
+
This gem supports Econt's JSON endpoints only, not SOAP.
|
|
4
|
+
|
|
5
|
+
Every public service method is listed below. Configure `Econt::Client` first
|
|
6
|
+
(see the [README](../README.md#configuration)); constructing a service does not
|
|
7
|
+
send a request. Services share the configured client by default, or accept
|
|
8
|
+
`new(client: configured_client)` for a separately configured `Econt::Client`.
|
|
9
|
+
|
|
10
|
+
## Receivers and method index
|
|
11
|
+
|
|
12
|
+
| Receiver | Class | Methods |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `nomenclatures` | `Econt::Services::Nomenclatures::Nomenclatures` | [get_countries](#get_countries), [get_cities](#get_cities), [get_regions](#get_regions), [get_offices](#get_offices), [get_streets](#get_streets), [get_quarters](#get_quarters) |
|
|
15
|
+
| `addresses` | `Econt::Services::Nomenclatures::Address` | [validate_address](#validate_address), [address_service_times](#address_service_times), [get_nearest_offices](#get_nearest_offices) |
|
|
16
|
+
| `labels` | `Econt::Services::Shipments::Label` | [create_label](#create_label), [create_labels](#create_labels), [delete_labels](#delete_labels), [update_label](#update_label), [check_possible_shipment_editions](#check_possible_shipment_editions), [update_labels](#update_labels), [grouping](#grouping), [grouping_cancelation](#grouping_cancelation) |
|
|
17
|
+
| `shipments` | `Econt::Services::Shipments::Shipment` | [request_courier](#request_courier), [get_shipment_statuses](#get_shipment_statuses), [get_request_courier_status](#get_request_courier_status), [get_my_awb](#get_my_awb), [set_itu_code](#set_itu_code) |
|
|
18
|
+
| `profiles` | `Econt::Services::Profile::Profile` | [get_client_profiles](#get_client_profiles), [create_cd_agreement](#create_cd_agreement) |
|
|
19
|
+
| `reports` | `Econt::Services::PaymentReport::PaymentReport` | [payment_report](#payment_report) |
|
|
20
|
+
| `logistics` | `Econt::Services::ThreeWayLogistics::ThreeWayLogistics` | [three_way_logistics](#three_way_logistics) |
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
nomenclatures = Econt::Services::Nomenclatures::Nomenclatures.new
|
|
24
|
+
addresses = Econt::Services::Nomenclatures::Address.new
|
|
25
|
+
labels = Econt::Services::Shipments::Label.new
|
|
26
|
+
shipments = Econt::Services::Shipments::Shipment.new
|
|
27
|
+
profiles = Econt::Services::Profile::Profile.new
|
|
28
|
+
reports = Econt::Services::PaymentReport::PaymentReport.new
|
|
29
|
+
logistics = Econt::Services::ThreeWayLogistics::ThreeWayLogistics.new
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How to use the reference
|
|
33
|
+
|
|
34
|
+
Signatures show Ruby defaults, not carrier requirements: an optional `nil`
|
|
35
|
+
or empty array does **not** mean the API accepts an incomplete request. Supply
|
|
36
|
+
the business fields required by the [official EEcont documentation](http://ee.econt.com/services/).
|
|
37
|
+
Nested hashes use snake_case except the legacy three-way logistics operation.
|
|
38
|
+
|
|
39
|
+
Examples are independent snippets, not a script to execute wholesale.
|
|
40
|
+
`address_payload`, `label_payload`, `updated_label_payload`,
|
|
41
|
+
`courier_payload`, `cd_pay_options_payload`, and `logistics_payload` are
|
|
42
|
+
application-owned hashes. Identifiers must belong to your account.
|
|
43
|
+
Dates and office/city IDs are illustrative; replace them before making requests.
|
|
44
|
+
|
|
45
|
+
Typed results expose snake_case readers and `to_h`. Collections are enumerable,
|
|
46
|
+
so use `each`, `first`, and `to_a`. Check `error?` on the result **and**
|
|
47
|
+
individual batch elements. HTTP failures raise `Econt::Error`. Connection,
|
|
48
|
+
timeout, and TLS failures raise `Econt::ConnectionError`, `Econt::TimeoutError`,
|
|
49
|
+
and `Econt::SSLError`, all subclasses of `Econt::TransportError < Econt::Error`.
|
|
50
|
+
Catch `Econt::Error` at your application's boundary. Parsing and programming
|
|
51
|
+
exceptions are not reclassified. The client never retries automatically; a
|
|
52
|
+
mutation timeout does not prove it failed.
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
begin
|
|
56
|
+
result = labels.create_label(label: label_payload, mode: 'validate')
|
|
57
|
+
if result.error?
|
|
58
|
+
# Handle result.error without exposing private carrier data.
|
|
59
|
+
else
|
|
60
|
+
result.label.to_h
|
|
61
|
+
end
|
|
62
|
+
rescue Econt::Error => error
|
|
63
|
+
# Record a safe diagnostic and decide whether reconciliation is needed.
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Transport exception messages are fixed diagnostics without adapter details.
|
|
68
|
+
Their `cause` preserves the original Faraday exception; `cause`, backtraces,
|
|
69
|
+
and `full_message` may contain credentials, URLs, or personal data. Do not log
|
|
70
|
+
those blindly. Without an HTTP response, `status` and `error` are `nil`.
|
|
71
|
+
|
|
72
|
+
Mutations affect the selected environment. Read [cleanup limitations](VCR.md)
|
|
73
|
+
before using them. `calculate_with_block` reserves a label number; it is not
|
|
74
|
+
equivalent to a read-only quote. No example promises deletion or cancellation
|
|
75
|
+
will succeed after processing has started.
|
|
76
|
+
|
|
77
|
+
## Methods
|
|
78
|
+
|
|
79
|
+
### get_countries
|
|
80
|
+
|
|
81
|
+
`nomenclatures.get_countries`
|
|
82
|
+
|
|
83
|
+
List countries available for delivery.
|
|
84
|
+
|
|
85
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::Country>`.
|
|
86
|
+
|
|
87
|
+
No arguments.
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
countries = nomenclatures.get_countries
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### get_cities
|
|
94
|
+
|
|
95
|
+
`nomenclatures.get_cities(country_code:)`
|
|
96
|
+
|
|
97
|
+
List cities for a country.
|
|
98
|
+
|
|
99
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::City>`.
|
|
100
|
+
|
|
101
|
+
| Parameter | Type | Meaning |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `country_code` | `String` | ISO country code, such as BGR. |
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
cities = nomenclatures.get_cities(country_code: 'BGR')
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### get_regions
|
|
110
|
+
|
|
111
|
+
`nomenclatures.get_regions(country_code:, language: nil)`
|
|
112
|
+
|
|
113
|
+
Fetch cities and derive unique, sorted region names; this is a local helper, not a separate endpoint.
|
|
114
|
+
|
|
115
|
+
Safety: **Read**. Returns `Array<String>`.
|
|
116
|
+
|
|
117
|
+
| Parameter | Type | Meaning |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| `country_code` | `String` | Country code. |
|
|
120
|
+
| `language` | `String, nil` | Only en selects English names; nil and other values select native names. |
|
|
121
|
+
|
|
122
|
+
This helper returns an ordinary array of names, not a model or collection. It
|
|
123
|
+
performs one `get_cities` request. HTTP/transport exceptions propagate, but an
|
|
124
|
+
embedded carrier error can yield an empty array. Use `get_cities` directly when
|
|
125
|
+
you need to distinguish that from a country with no regions.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
regions = nomenclatures.get_regions(country_code: 'BGR', language: 'en')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### get_offices
|
|
132
|
+
|
|
133
|
+
`nomenclatures.get_offices(country_code:, city_id: nil, show_cargo_receptions: nil, show_lc: nil, serving_receptions: nil)`
|
|
134
|
+
|
|
135
|
+
List offices with optional city and reception filters.
|
|
136
|
+
|
|
137
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::Office>`.
|
|
138
|
+
|
|
139
|
+
| Parameter | Type | Meaning |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `country_code` | `String` | Country code. |
|
|
142
|
+
| `city_id` | `Integer, nil` | City identifier. |
|
|
143
|
+
| `show_cargo_receptions` | `Boolean, nil` | Include cargo receptions. |
|
|
144
|
+
| `show_lc` | `Boolean, nil` | Carrier LC filter. |
|
|
145
|
+
| `serving_receptions` | `Boolean, nil` | Carrier serving-reception filter. |
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
offices = nomenclatures.get_offices(country_code: 'BGR', city_id: 41)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### get_streets
|
|
152
|
+
|
|
153
|
+
`nomenclatures.get_streets(city_id: nil)`
|
|
154
|
+
|
|
155
|
+
List streets, optionally restricted to a city.
|
|
156
|
+
|
|
157
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::Street>`.
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Meaning |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| `city_id` | `Integer, nil` | City identifier. |
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
streets = nomenclatures.get_streets(city_id: 41)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### get_quarters
|
|
168
|
+
|
|
169
|
+
`nomenclatures.get_quarters(city_id: nil)`
|
|
170
|
+
|
|
171
|
+
List quarters, optionally restricted to a city.
|
|
172
|
+
|
|
173
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::Quarter>`.
|
|
174
|
+
|
|
175
|
+
| Parameter | Type | Meaning |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| `city_id` | `Integer, nil` | City identifier. |
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
quarters = nomenclatures.get_quarters(city_id: 41)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### validate_address
|
|
184
|
+
|
|
185
|
+
`addresses.validate_address(address)`
|
|
186
|
+
|
|
187
|
+
Validate and normalize an address.
|
|
188
|
+
|
|
189
|
+
Safety: **Read**. Returns `Econt::Models::Nomenclatures::AddressValidationResponse`.
|
|
190
|
+
|
|
191
|
+
| Parameter | Type | Meaning |
|
|
192
|
+
| --- | --- | --- |
|
|
193
|
+
| `address` | `Hash, Econt::Models::Nomenclatures::Address` | Address fields; the argument is positional. |
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
validation = addresses.validate_address(city: { id: 41 }, street: 'Витоша', num: '1')
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### address_service_times
|
|
200
|
+
|
|
201
|
+
`addresses.address_service_times(city:, address:, date:, shipment_type:)`
|
|
202
|
+
|
|
203
|
+
Look up address service availability for a date and shipment type.
|
|
204
|
+
|
|
205
|
+
Safety: **Read**. Returns `Econt::Models::Nomenclatures::AddressServiceTimesResponse`.
|
|
206
|
+
|
|
207
|
+
| Parameter | Type | Meaning |
|
|
208
|
+
| --- | --- | --- |
|
|
209
|
+
| `city` | `Hash` | City fields. |
|
|
210
|
+
| `address` | `Hash` | Address fields. |
|
|
211
|
+
| `date` | `String` | Requested service date in the carrier format. |
|
|
212
|
+
| `shipment_type` | `String` | Carrier shipment type, such as pack. |
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
times = addresses.address_service_times(
|
|
216
|
+
city: { id: 41 }, address: address_payload,
|
|
217
|
+
date: '2026-10-12', shipment_type: 'pack'
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### get_nearest_offices
|
|
222
|
+
|
|
223
|
+
`addresses.get_nearest_offices(address:, shipment_type:)`
|
|
224
|
+
|
|
225
|
+
Find offices near an address for a shipment type.
|
|
226
|
+
|
|
227
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Nomenclatures::Office>`.
|
|
228
|
+
|
|
229
|
+
| Parameter | Type | Meaning |
|
|
230
|
+
| --- | --- | --- |
|
|
231
|
+
| `address` | `Hash` | Address fields. |
|
|
232
|
+
| `shipment_type` | `String` | Carrier shipment type. |
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
offices = addresses.get_nearest_offices(address: address_payload, shipment_type: 'pack')
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### create_label
|
|
239
|
+
|
|
240
|
+
`labels.create_label(label: nil, request_courier_time_from: nil, request_courier_time_to: nil, mode: nil)`
|
|
241
|
+
|
|
242
|
+
Calculate, validate, or create one shipping label.
|
|
243
|
+
|
|
244
|
+
Safety: **Conditional mutation**. Returns `Econt::Models::Shipments::CreateLabelResultElement`.
|
|
245
|
+
|
|
246
|
+
| Parameter | Type | Meaning |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| `label` | `Hash, nil` | Shipping-label payload. |
|
|
249
|
+
| `request_courier_time_from` | `String, nil` | Requested courier window start. |
|
|
250
|
+
| `request_courier_time_to` | `String, nil` | Requested courier window end. |
|
|
251
|
+
| `mode` | `String, nil` | calculate or validate are non-mutating; create and calculate_with_block have effects. Always choose explicitly. |
|
|
252
|
+
|
|
253
|
+
The result contains `label` (a `ShippingLabel`), including prices and PDF URLs when supplied by the carrier. Use `mode: 'create'` only deliberately; `mode: 'calculate_with_block'` can reserve a number. Courier window arguments may request collection. PDF URLs are carrier-hosted; this method does not download them.
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
quote = labels.create_label(label: label_payload, mode: 'calculate')
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### create_labels
|
|
260
|
+
|
|
261
|
+
`labels.create_labels(labels: [], run_async_and_email_result_to: nil, mode: nil)`
|
|
262
|
+
|
|
263
|
+
Calculate, validate, or create a batch of labels.
|
|
264
|
+
|
|
265
|
+
Safety: **Conditional mutation**. Returns `Econt::Collection<Econt::Models::Shipments::CreateLabelResultElement>`.
|
|
266
|
+
|
|
267
|
+
| Parameter | Type | Meaning |
|
|
268
|
+
| --- | --- | --- |
|
|
269
|
+
| `labels` | `Array<Hash>` | Shipping-label payloads. |
|
|
270
|
+
| `run_async_and_email_result_to` | `String, nil` | Email address for asynchronous results; the returned data may differ. |
|
|
271
|
+
| `mode` | `String, nil` | Choose calculate, validate, or create explicitly. |
|
|
272
|
+
|
|
273
|
+
Each item is a `CreateLabelResultElement`; inspect its `error?` before reading `label`. Use synchronous requests when you need the immediate batch result. Asynchronous email behavior is not independently contract-verified.
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
quotes = labels.create_labels(labels: [label_payload], mode: 'calculate')
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### delete_labels
|
|
280
|
+
|
|
281
|
+
`labels.delete_labels(shipment_numbers: [])`
|
|
282
|
+
|
|
283
|
+
Delete selected labels when allowed by carrier state and account permissions.
|
|
284
|
+
|
|
285
|
+
Safety: **Mutation**. Returns `Econt::Collection<Econt::Models::Shipments::DeleteLabelsResultElement>`.
|
|
286
|
+
|
|
287
|
+
| Parameter | Type | Meaning |
|
|
288
|
+
| --- | --- | --- |
|
|
289
|
+
| `shipment_numbers` | `Array<String>` | Numbers of labels owned by your account. |
|
|
290
|
+
|
|
291
|
+
Inspect every `DeleteLabelsResultElement` and reconcile via `shipments.get_shipment_statuses`; a successful HTTP response alone does not prove every label was deleted.
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
deleted = labels.delete_labels(shipment_numbers: [shipment_number])
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### update_label
|
|
298
|
+
|
|
299
|
+
`labels.update_label(label: nil, request_courier_time_from: nil, request_courier_time_to: nil, destroy: nil, payment_addition_price: nil, payment_instruction: nil)`
|
|
300
|
+
|
|
301
|
+
Update or destroy an existing label.
|
|
302
|
+
|
|
303
|
+
Safety: **Mutation**. Returns `Econt::Models::Shipments::UpdateLabelResponse`.
|
|
304
|
+
|
|
305
|
+
| Parameter | Type | Meaning |
|
|
306
|
+
| --- | --- | --- |
|
|
307
|
+
| `label` | `Hash, nil` | Existing label with its shipment number and requested changes. |
|
|
308
|
+
| `request_courier_time_from` | `String, nil` | Courier window start. |
|
|
309
|
+
| `request_courier_time_to` | `String, nil` | Courier window end. |
|
|
310
|
+
| `destroy` | `Boolean, nil` | Whether to destroy the label. |
|
|
311
|
+
| `payment_addition_price` | `Hash, nil` | PaymentAdditionPrice fields. |
|
|
312
|
+
| `payment_instruction` | `Hash, nil` | PaymentInstruction fields. |
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
updated = labels.update_label(label: updated_label_payload, destroy: false)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### check_possible_shipment_editions
|
|
319
|
+
|
|
320
|
+
`labels.check_possible_shipment_editions(shipment_nums: [])`
|
|
321
|
+
|
|
322
|
+
Check which edits are permitted for existing shipments.
|
|
323
|
+
|
|
324
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Shipments::CheckPossibleShipmentEditionsResultElement>`.
|
|
325
|
+
|
|
326
|
+
| Parameter | Type | Meaning |
|
|
327
|
+
| --- | --- | --- |
|
|
328
|
+
| `shipment_nums` | `Array<Integer>` | Shipment numbers in the carrier field type. |
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
editions = labels.check_possible_shipment_editions(shipment_nums: [1234567890123])
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### update_labels
|
|
335
|
+
|
|
336
|
+
`labels.update_labels(labels: [])`
|
|
337
|
+
|
|
338
|
+
Update several existing labels.
|
|
339
|
+
|
|
340
|
+
Safety: **Mutation**. Returns `Econt::Collection<Econt::Models::Shipments::UpdateLabelsResultElement>`.
|
|
341
|
+
|
|
342
|
+
| Parameter | Type | Meaning |
|
|
343
|
+
| --- | --- | --- |
|
|
344
|
+
| `labels` | `Array<Hash>` | Existing labels and requested changes. |
|
|
345
|
+
|
|
346
|
+
Each item exposes optional `labels` (an `UpdateLabelResponse`) and `error`. The unusual plural reader matches the carrier contract.
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
updated = labels.update_labels(labels: [updated_label_payload])
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### grouping
|
|
353
|
+
|
|
354
|
+
`labels.grouping(labels: [])`
|
|
355
|
+
|
|
356
|
+
Group existing labels into a group label.
|
|
357
|
+
|
|
358
|
+
Safety: **Mutation**. Returns `Econt::Models::Shipments::ShipmentStatus`.
|
|
359
|
+
|
|
360
|
+
| Parameter | Type | Meaning |
|
|
361
|
+
| --- | --- | --- |
|
|
362
|
+
| `labels` | `Array<Integer>` | Shipment numbers to group. |
|
|
363
|
+
|
|
364
|
+
`labels` contains integer shipment numbers, not shipping-label hashes. Grouping cleanup is carrier-state dependent.
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
group = labels.grouping(labels: [1234567890123, 1234567890124])
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### grouping_cancelation
|
|
371
|
+
|
|
372
|
+
`labels.grouping_cancelation(group_label: nil)`
|
|
373
|
+
|
|
374
|
+
Cancel an existing group label.
|
|
375
|
+
|
|
376
|
+
Safety: **Mutation**. Returns `Econt::Models::Shipments::StatusResponse`.
|
|
377
|
+
|
|
378
|
+
| Parameter | Type | Meaning |
|
|
379
|
+
| --- | --- | --- |
|
|
380
|
+
| `group_label` | `Integer, nil` | Group-label shipment number. |
|
|
381
|
+
|
|
382
|
+
The Ruby spelling preserves the carrier operation `groupingCancelation`.
|
|
383
|
+
|
|
384
|
+
```ruby
|
|
385
|
+
result = labels.grouping_cancelation(group_label: group_label_number)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### request_courier
|
|
389
|
+
|
|
390
|
+
`shipments.request_courier(**attributes)`
|
|
391
|
+
|
|
392
|
+
Book a courier collection; pass the documented request fields as keywords.
|
|
393
|
+
|
|
394
|
+
Safety: **Mutation**. Returns `Econt::Models::Shipments::RequestCourierResponse`.
|
|
395
|
+
|
|
396
|
+
| Parameter | Type | Meaning |
|
|
397
|
+
| --- | --- | --- |
|
|
398
|
+
| `attributes` | `Hash` | Courier payload as keywords: request_time_from, request_time_to, shipment_type, shipment_pack_count, shipment_weight, sender_client, sender_agent, sender_address, attach_shipments, pack12, cargo_vehicle_options. |
|
|
399
|
+
|
|
400
|
+
```ruby
|
|
401
|
+
booking = shipments.request_courier(**courier_payload)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### get_shipment_statuses
|
|
405
|
+
|
|
406
|
+
`shipments.get_shipment_statuses(shipment_numbers: [])`
|
|
407
|
+
|
|
408
|
+
Read shipment status, tracking events, and label information.
|
|
409
|
+
|
|
410
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Shipments::ShipmentStatusResultElement>`.
|
|
411
|
+
|
|
412
|
+
| Parameter | Type | Meaning |
|
|
413
|
+
| --- | --- | --- |
|
|
414
|
+
| `shipment_numbers` | `Array<String>` | Shipment numbers to query. |
|
|
415
|
+
|
|
416
|
+
Each item exposes `status` (a `ShipmentStatus`) and an optional error. Read `statuses.first.status` only after checking both collection and item errors.
|
|
417
|
+
|
|
418
|
+
```ruby
|
|
419
|
+
statuses = shipments.get_shipment_statuses(shipment_numbers: [shipment_number])
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### get_request_courier_status
|
|
423
|
+
|
|
424
|
+
`shipments.get_request_courier_status(request_courier_ids: [])`
|
|
425
|
+
|
|
426
|
+
Read the status of courier collection requests.
|
|
427
|
+
|
|
428
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Shipments::RequestCourierStatusResultElement>`.
|
|
429
|
+
|
|
430
|
+
| Parameter | Type | Meaning |
|
|
431
|
+
| --- | --- | --- |
|
|
432
|
+
| `request_courier_ids` | `Array<Integer>` | Courier request identifiers. |
|
|
433
|
+
|
|
434
|
+
```ruby
|
|
435
|
+
statuses = shipments.get_request_courier_status(request_courier_ids: [courier_request_id])
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### get_my_awb
|
|
439
|
+
|
|
440
|
+
`shipments.get_my_awb(date_from: nil, date_to: nil, page: nil, side: nil)`
|
|
441
|
+
|
|
442
|
+
List account waybills using optional date, page, and side filters.
|
|
443
|
+
|
|
444
|
+
Safety: **Read**. Returns `Econt::Models::Shipments::GetMyAwbResponse`.
|
|
445
|
+
|
|
446
|
+
| Parameter | Type | Meaning |
|
|
447
|
+
| --- | --- | --- |
|
|
448
|
+
| `date_from` | `String, nil` | Start date in carrier format. |
|
|
449
|
+
| `date_to` | `String, nil` | End date in carrier format. |
|
|
450
|
+
| `page` | `Integer, nil` | Requested page. |
|
|
451
|
+
| `side` | `String, nil` | Carrier side filter; see getMyAWB documentation for allowed values. |
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
waybills = shipments.get_my_awb(date_from: '2026-10-01', date_to: '2026-10-12', page: 1)
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### set_itu_code
|
|
458
|
+
|
|
459
|
+
`shipments.set_itu_code(awb_barcode:, truck_reg_num:, itu_code:)`
|
|
460
|
+
|
|
461
|
+
Assign ITU transport metadata to a waybill.
|
|
462
|
+
|
|
463
|
+
Safety: **Mutation**. Returns `Faraday::Response`.
|
|
464
|
+
|
|
465
|
+
| Parameter | Type | Meaning |
|
|
466
|
+
| --- | --- | --- |
|
|
467
|
+
| `awb_barcode` | `String` | Waybill barcode. |
|
|
468
|
+
| `truck_reg_num` | `String` | Truck registration number. |
|
|
469
|
+
| `itu_code` | `String` | ITU code; sent with the carrier spelling ITU_code. |
|
|
470
|
+
|
|
471
|
+
This operation returns the raw Faraday response, unlike typed endpoints. Inspect `response.body`; no `error?` reader is provided.
|
|
472
|
+
|
|
473
|
+
```ruby
|
|
474
|
+
response = shipments.set_itu_code(
|
|
475
|
+
awb_barcode: shipment_number, truck_reg_num: truck_registration, itu_code: itu_code
|
|
476
|
+
)
|
|
477
|
+
response.body
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### get_client_profiles
|
|
481
|
+
|
|
482
|
+
`profiles.get_client_profiles`
|
|
483
|
+
|
|
484
|
+
Read account client profiles and payment settings.
|
|
485
|
+
|
|
486
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::Profile::ProfilesResponseElement>`.
|
|
487
|
+
|
|
488
|
+
No arguments.
|
|
489
|
+
|
|
490
|
+
Each item is a `ProfilesResponseElement`; account-specific profiles are nested in its documented readers.
|
|
491
|
+
|
|
492
|
+
```ruby
|
|
493
|
+
clients = profiles.get_client_profiles
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### create_cd_agreement
|
|
497
|
+
|
|
498
|
+
`profiles.create_cd_agreement(cd_pay_options:)`
|
|
499
|
+
|
|
500
|
+
Create a cash-on-delivery payment agreement.
|
|
501
|
+
|
|
502
|
+
Safety: **Mutation**. Returns `Econt::Models::Profile::CDPayOptions`.
|
|
503
|
+
|
|
504
|
+
| Parameter | Type | Meaning |
|
|
505
|
+
| --- | --- | --- |
|
|
506
|
+
| `cd_pay_options` | `Hash` | CDPayOptions payload; account eligibility is carrier-controlled. |
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
agreement = profiles.create_cd_agreement(cd_pay_options: cd_pay_options_payload)
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### payment_report
|
|
513
|
+
|
|
514
|
+
`reports.payment_report(date_to: nil, date_from: nil)`
|
|
515
|
+
|
|
516
|
+
Read payment reports for an optional date range.
|
|
517
|
+
|
|
518
|
+
Safety: **Read**. Returns `Econt::Collection<Econt::Models::PaymentReport::PaymentReportRow>, Econt::Models::PaymentReport::PaymentReportRow`.
|
|
519
|
+
|
|
520
|
+
| Parameter | Type | Meaning |
|
|
521
|
+
| --- | --- | --- |
|
|
522
|
+
| `date_to` | `String, nil` | End date in carrier format. |
|
|
523
|
+
| `date_from` | `String, nil` | Start date in carrier format. |
|
|
524
|
+
|
|
525
|
+
The reviewed demo `PaymentReportRows` envelope returns a collection. The documented bare-row response returns one row; the example normalizes both to an array.
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
rows = reports.payment_report(date_from: '2026-10-01', date_to: '2026-10-12')
|
|
529
|
+
rows = rows.is_a?(Econt::Collection) ? rows.to_a : [rows]
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### three_way_logistics
|
|
533
|
+
|
|
534
|
+
`logistics.three_way_logistics(**attributes)`
|
|
535
|
+
|
|
536
|
+
Submit a three-way logistics request using legacy snake_case fields without camelCase conversion.
|
|
537
|
+
|
|
538
|
+
Safety: **Mutation**. Returns `Econt::Models::ThreeWayLogistics::ThreeWayLogisticsResponse`.
|
|
539
|
+
|
|
540
|
+
| Parameter | Type | Meaning |
|
|
541
|
+
| --- | --- | --- |
|
|
542
|
+
| `attributes` | `Hash` | Legacy request fields as keywords; see ThreeWayLogisticsRequest for available field names. |
|
|
543
|
+
|
|
544
|
+
Request fields retain legacy snake_case on the wire. See `Econt::Models::ThreeWayLogistics::ThreeWayLogisticsRequest` for declared field names. The response contract has no authoritative successful fields, so the typed response currently has no data readers. Do not infer successful fulfillment from an empty model.
|
|
545
|
+
|
|
546
|
+
```ruby
|
|
547
|
+
result = logistics.three_way_logistics(**logistics_payload)
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Payload and model details
|
|
551
|
+
|
|
552
|
+
For nested request fields, consult the corresponding operation and model on the
|
|
553
|
+
[official service index](http://ee.econt.com/services/): `ShippingLabel`,
|
|
554
|
+
`Address`, `ClientProfile`, `CDPayOptions`, `PaymentInstruction`,
|
|
555
|
+
`PaymentAdditionPrice`, and `ThreeWayLogisticsRequest`.
|
|
556
|
+
The gem forwards supplied request fields; it does not perform full business validation.
|
|
557
|
+
|
|
558
|
+
Generate a searchable local method reference directly from
|
|
559
|
+
the source comments:
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
bundle install
|
|
563
|
+
bundle exec rake yard
|
|
564
|
+
open doc/index.html
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
Generated HTML lives in ignored `doc/`; Markdown lives in this repository and
|
|
568
|
+
is included in the gem. No GitHub wiki or hosted documentation is required.
|
|
569
|
+
YARD is a development dependency only. See [public API decisions](PUBLIC_API.md)
|
|
570
|
+
for coercion and response limitations, and [endpoint coverage](ENDPOINTS.md) for
|
|
571
|
+
the Ruby-to-carrier operation mapping.
|
data/docs/ENDPOINTS.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Supported endpoints
|
|
2
|
+
|
|
3
|
+
All service operations use HTTP `POST`. Model names below omit the
|
|
4
|
+
`Econt::Models` namespace; collections are `Econt::Collection` instances.
|
|
5
|
+
|
|
6
|
+
| Area | Ruby method | Econt operation | Return value |
|
|
7
|
+
| --- | --- | --- | --- |
|
|
8
|
+
| Nomenclatures | `get_countries` | `getCountries` | collection of `Country` |
|
|
9
|
+
| Nomenclatures | `get_cities` | `getCities` | collection of `City` |
|
|
10
|
+
| Nomenclatures | `get_offices` | `getOffices` | collection of `Office` |
|
|
11
|
+
| Nomenclatures | `get_streets` | `getStreets` | collection of `Street` |
|
|
12
|
+
| Nomenclatures | `get_quarters` | `getQuarters` | collection of `Quarter` |
|
|
13
|
+
| Address | `validate_address` | `validateAddress` | `AddressValidationResponse` |
|
|
14
|
+
| Address | `address_service_times` | `addressServiceTimes` | `AddressServiceTimesResponse` |
|
|
15
|
+
| Address | `get_nearest_offices` | `getNearestOffices` | collection of `Office` |
|
|
16
|
+
| Labels | `create_label` | `createLabel` | `CreateLabelResultElement` |
|
|
17
|
+
| Labels | `create_labels` | `createLabels` | collection of results |
|
|
18
|
+
| Labels | `delete_labels` | `deleteLabels` | collection of results |
|
|
19
|
+
| Labels | `update_label` | `updateLabel` | `UpdateLabelResponse` |
|
|
20
|
+
| Labels | `check_possible_shipment_editions` | `checkPossibleShipmentEditions` | collection of results |
|
|
21
|
+
| Labels | `update_labels` | `updateLabels` | collection of results |
|
|
22
|
+
| Labels | `grouping` | `grouping` | `ShipmentStatus` |
|
|
23
|
+
| Labels | `grouping_cancelation` | `groupingCancelation` | `StatusResponse` |
|
|
24
|
+
| Shipments | `request_courier` | `requestCourier` | `RequestCourierResponse` |
|
|
25
|
+
| Shipments | `get_shipment_statuses` | `getShipmentStatuses` | collection of results |
|
|
26
|
+
| Shipments | `get_request_courier_status` | `getRequestCourierStatus` | collection of results |
|
|
27
|
+
| Shipments | `get_my_awb` | `getMyAWB` | `GetMyAwbResponse` |
|
|
28
|
+
| Shipments | `set_itu_code` | `setITUCode` | raw documented response |
|
|
29
|
+
| Profile | `get_client_profiles` | `getClientProfiles` | collection of profiles |
|
|
30
|
+
| Profile | `create_cd_agreement` | `createCDAgreement` | `CDPayOptions` |
|
|
31
|
+
| Payment reports | `payment_report` | `PaymentReport` | collection of `PaymentReportRow`; bare row also supported |
|
|
32
|
+
| Three-way logistics | `three_way_logistics` | `threeWayLogistics` | empty typed response |
|
|
33
|
+
|
|
34
|
+
`get_regions(country_code:, language: nil)` is a local helper, not an Econt
|
|
35
|
+
endpoint. It calls `get_cities` and returns unique, sorted region names (English
|
|
36
|
+
when `language: 'en'`).
|
|
37
|
+
|
|
38
|
+
The spelling of `PaymentReport`, `getMyAWB`, `groupingCancelation`, and
|
|
39
|
+
`setITUCode` follows Econt's published operation names. Every listed operation
|
|
40
|
+
has offline example-response coverage; that is not equivalent to live contract
|
|
41
|
+
verification. See [response tests](RESPONSE_TESTS.md), [VCR coverage](VCR.md), and
|
|
42
|
+
[public API limitations](PUBLIC_API.md).
|
data/docs/PUBLIC_API.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Public API contract
|
|
2
|
+
|
|
3
|
+
This document records the compatibility decisions intended to remain stable
|
|
4
|
+
once the gem reaches version 1.0. The official EEcont documentation remains the
|
|
5
|
+
authority for required business fields and carrier behavior.
|
|
6
|
+
|
|
7
|
+
## Request and response conventions
|
|
8
|
+
|
|
9
|
+
- Only JSON endpoints are supported; SOAP is outside this gem's scope.
|
|
10
|
+
- Public service methods accept snake_case Ruby keywords and hashes. The
|
|
11
|
+
middleware converts request keys to Econt's documented JSON casing.
|
|
12
|
+
- Responses are typed `Econt::Model` instances or `Econt::Collection` objects,
|
|
13
|
+
as listed in the [Ruby API reference](API.md). Model readers use snake_case.
|
|
14
|
+
- Declared scalar fields are coerced to the documented primitive Ruby type.
|
|
15
|
+
Dates and date-times become `Date` and `DateTime` when parsing succeeds.
|
|
16
|
+
- Missing optional fields return `nil`. Unknown response fields are ignored so
|
|
17
|
+
an additive carrier response does not unexpectedly expand the gem's public
|
|
18
|
+
model API. A new gem release can expose a documented field deliberately.
|
|
19
|
+
- `to_h` returns snake_case symbol keys. `to_json` converts keys back to Econt's
|
|
20
|
+
request casing.
|
|
21
|
+
- `Econt::Collection` is enumerable and exposes `to_a`, `size`, `length`, and
|
|
22
|
+
`count`; it is intentionally not an `Array` subclass.
|
|
23
|
+
|
|
24
|
+
Request parameters remain hashes because Econt's label and courier requests are
|
|
25
|
+
large, nested, and frequently revised. Responses are models because stable
|
|
26
|
+
readers, coercion, and nested types provide useful guarantees without forcing
|
|
27
|
+
callers to construct a very large request object graph.
|
|
28
|
+
|
|
29
|
+
## Errors
|
|
30
|
+
|
|
31
|
+
Transport failures and non-success HTTP statuses raise `Econt::Error`. Its
|
|
32
|
+
`status` contains the HTTP status when available, and `error` contains a typed
|
|
33
|
+
`Econt::Models::Error` when Econt returned one.
|
|
34
|
+
|
|
35
|
+
`Econt::TransportError < Econt::Error` is the common parent of
|
|
36
|
+
`ConnectionError`, `TimeoutError`, and `SSLError`. These wrap only Faraday's
|
|
37
|
+
connection, timeout, and TLS exceptions; parsing and programming errors retain
|
|
38
|
+
their original types. The original exception is preserved as `cause`, while
|
|
39
|
+
the wrapper message is a fixed safe diagnostic. No response means `status` and
|
|
40
|
+
`error` are `nil`. Causes, backtraces, and `full_message` may expose sensitive
|
|
41
|
+
adapter details, so scrub them before logging. No request is retried
|
|
42
|
+
automatically; reconcile mutations before deciding to retry.
|
|
43
|
+
|
|
44
|
+
Some Econt operations return an error object within an otherwise successful
|
|
45
|
+
response. Models and collections therefore expose `error?` and `error`. This is
|
|
46
|
+
kept separate from HTTP exceptions because it matches the carrier contract and
|
|
47
|
+
allows batch operations to contain both successful and failed elements.
|
|
48
|
+
|
|
49
|
+
## Documented response decisions
|
|
50
|
+
|
|
51
|
+
- `update_labels` returns a collection of `UpdateLabelsResultElement`. Econt
|
|
52
|
+
explicitly documents each element as optional `error` plus optional `labels`,
|
|
53
|
+
where `labels` is an `UpdateLabelResponse`. The unusual plural field name is
|
|
54
|
+
retained instead of inventing a singular alias.
|
|
55
|
+
- `PreviousShipment#shipment_number` is an `Integer`, because Econt documents
|
|
56
|
+
that particular field as `int`, even though most other shipment-number fields
|
|
57
|
+
are strings. The inconsistency is intentionally represented rather than
|
|
58
|
+
normalized.
|
|
59
|
+
- `payment_report` returns a collection of `PaymentReportRow` when the response
|
|
60
|
+
contains `PaymentReportRows`, as seen in the reviewed demo recording. A null
|
|
61
|
+
envelope produces an empty collection. The OpenAPI schema instead describes a
|
|
62
|
+
bare row; that shape remains supported and returns one `PaymentReportRow`.
|
|
63
|
+
- `three_way_logistics` returns `ThreeWayLogisticsResponse`, currently an empty
|
|
64
|
+
typed model. Econt documents request fields but no successful response fields.
|
|
65
|
+
The gem does not invent undocumented readers.
|
|
66
|
+
|
|
67
|
+
Three-way logistics should be checked against demo responses before the 1.0 API
|
|
68
|
+
is frozen. If its actual response differs, correct the model during the pre-1.0
|
|
69
|
+
series and record the evidence in a scrubbed contract test.
|
|
70
|
+
|
|
71
|
+
## Compatibility policy
|
|
72
|
+
|
|
73
|
+
The gem follows [Semantic Versioning 2.0.0](https://semver.org/). During the
|
|
74
|
+
pre-1.0 series, minor versions may refine or correct the public contract as
|
|
75
|
+
real carrier responses are verified. These changes must still be documented.
|
|
76
|
+
|
|
77
|
+
After 1.0, removing or renaming public methods, changing accepted keywords, or
|
|
78
|
+
changing documented return types requires a new major version. Adding endpoint
|
|
79
|
+
methods, optional model readers, or new error subclasses is normally backward
|
|
80
|
+
compatible. Carrier-side behavior outside the gem's control is documented but
|
|
81
|
+
cannot be covered by semantic-version guarantees.
|