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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/CONTRIBUTING.md +37 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +171 -0
  7. data/SECURITY.md +33 -0
  8. data/docs/API.md +571 -0
  9. data/docs/ENDPOINTS.md +42 -0
  10. data/docs/PUBLIC_API.md +81 -0
  11. data/docs/RELEASING.md +35 -0
  12. data/docs/RESPONSE_TESTS.md +46 -0
  13. data/docs/VCR.md +144 -0
  14. data/lib/econt/camel_case_middleware.rb +88 -0
  15. data/lib/econt/client.rb +99 -0
  16. data/lib/econt/collection.rb +59 -0
  17. data/lib/econt/error.rb +42 -0
  18. data/lib/econt/meta_resource.rb +6 -0
  19. data/lib/econt/model.rb +95 -0
  20. data/lib/econt/models/error.rb +53 -0
  21. data/lib/econt/models/hosted_file.rb +13 -0
  22. data/lib/econt/models/nomenclatures/address.rb +35 -0
  23. data/lib/econt/models/nomenclatures/address_service_info.rb +16 -0
  24. data/lib/econt/models/nomenclatures/address_service_times_response.rb +19 -0
  25. data/lib/econt/models/nomenclatures/address_validation_response.rb +19 -0
  26. data/lib/econt/models/nomenclatures/city.rb +32 -0
  27. data/lib/econt/models/nomenclatures/country.rb +16 -0
  28. data/lib/econt/models/nomenclatures/geo_location.rb +13 -0
  29. data/lib/econt/models/nomenclatures/office.rb +32 -0
  30. data/lib/econt/models/nomenclatures/quarter.rb +14 -0
  31. data/lib/econt/models/nomenclatures/serving_office_element.rb +12 -0
  32. data/lib/econt/models/nomenclatures/street.rb +14 -0
  33. data/lib/econt/models/nomenclatures/working_date_time.rb +16 -0
  34. data/lib/econt/models/nomenclatures/working_time.rb +12 -0
  35. data/lib/econt/models/payment_report/payment_report_row.rb +17 -0
  36. data/lib/econt/models/profile/cd_pay_options.rb +24 -0
  37. data/lib/econt/models/profile/client_profile.rb +29 -0
  38. data/lib/econt/models/profile/profiles_response_element.rb +16 -0
  39. data/lib/econt/models/shipments/cargo_vehicle_options.rb +16 -0
  40. data/lib/econt/models/shipments/check_possible_shipment_editions_result_element.rb +14 -0
  41. data/lib/econt/models/shipments/create_label_result_element.rb +19 -0
  42. data/lib/econt/models/shipments/customs_list_element.rb +14 -0
  43. data/lib/econt/models/shipments/delete_labels_result_element.rb +14 -0
  44. data/lib/econt/models/shipments/get_my_awb_response.rb +15 -0
  45. data/lib/econt/models/shipments/get_my_awb_result_element.rb +23 -0
  46. data/lib/econt/models/shipments/instruction.rb +20 -0
  47. data/lib/econt/models/shipments/next_shipment_element.rb +13 -0
  48. data/lib/econt/models/shipments/pack_element.rb +14 -0
  49. data/lib/econt/models/shipments/packing_list_element.rb +17 -0
  50. data/lib/econt/models/shipments/payment_addition_price.rb +14 -0
  51. data/lib/econt/models/shipments/payment_instruction.rb +11 -0
  52. data/lib/econt/models/shipments/previous_shipment.rb +14 -0
  53. data/lib/econt/models/shipments/request_courier_response.rb +13 -0
  54. data/lib/econt/models/shipments/request_courier_status.rb +14 -0
  55. data/lib/econt/models/shipments/request_courier_status_result_element.rb +14 -0
  56. data/lib/econt/models/shipments/return_instruction_params.rb +48 -0
  57. data/lib/econt/models/shipments/shipment_edition_response_element.rb +16 -0
  58. data/lib/econt/models/shipments/shipment_status.rb +64 -0
  59. data/lib/econt/models/shipments/shipment_status_result_element.rb +14 -0
  60. data/lib/econt/models/shipments/shipment_status_service.rb +16 -0
  61. data/lib/econt/models/shipments/shipment_tracking_event.rb +21 -0
  62. data/lib/econt/models/shipments/shipping_label.rb +59 -0
  63. data/lib/econt/models/shipments/shipping_label_services.rb +37 -0
  64. data/lib/econt/models/shipments/status_response.rb +11 -0
  65. data/lib/econt/models/shipments/update_label_response.rb +11 -0
  66. data/lib/econt/models/shipments/update_labels_result_element.rb +14 -0
  67. data/lib/econt/models/three_way_logistics/three_way_logistics.rb +50 -0
  68. data/lib/econt/models/three_way_logistics/three_way_logistics_response.rb +11 -0
  69. data/lib/econt/resource_attributes.rb +69 -0
  70. data/lib/econt/service_resource.rb +105 -0
  71. data/lib/econt/services/nomenclatures/address.rb +87 -0
  72. data/lib/econt/services/nomenclatures/nomenclatures.rb +122 -0
  73. data/lib/econt/services/nomenclatures.rb +11 -0
  74. data/lib/econt/services/payment_report/payment_report.rb +35 -0
  75. data/lib/econt/services/payment_report.rb +11 -0
  76. data/lib/econt/services/profile/profile.rb +45 -0
  77. data/lib/econt/services/profile.rb +11 -0
  78. data/lib/econt/services/shipments/label.rb +174 -0
  79. data/lib/econt/services/shipments/shipment.rb +109 -0
  80. data/lib/econt/services/shipments.rb +11 -0
  81. data/lib/econt/services/three_way_logistics/three_way_logistics.rb +30 -0
  82. data/lib/econt/services/three_way_logistics.rb +11 -0
  83. data/lib/econt/util.rb +63 -0
  84. data/lib/econt/version.rb +5 -0
  85. data/lib/econt.rb +118 -0
  86. data/sig/econt.rbs +465 -0
  87. 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).
@@ -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.