conexa 0.0.8 → 0.0.9

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: '050626980d9f896ca2c70ed30a27c1cfda7d0d30c89e25c90e86422d122fb1af'
4
- data.tar.gz: 43134221ca4dd8f38bc3f8a62bfffe2807d1565e949e2adf72d446fa14db01cf
3
+ metadata.gz: b1dba13ee9b0dc841e758d5cb0b9c468a480ca564129666814c0e25892ea4cff
4
+ data.tar.gz: 732eebd11f3ec3b5ee727f030ac51493fbce0e6212fb510dfcf5ba476d6424c4
5
5
  SHA512:
6
- metadata.gz: 298ef626afb7783dff6d66161e20ff4b28b30b0d81761ce3f5debf187f1d60e0d01814004d3372ad6531b5e40e22e110af471522bbc577e3c0baac63dfb17014
7
- data.tar.gz: 768fbc470c103074ff5b768bf78ba6ed47385c4d06a7cadba7aed395947da92c73756c8f72c06e66be15520974c7f0c2e8438dfc567181cc83357416cad251d5
6
+ metadata.gz: c45016bd74d1e830ae18915be67be0f5a5e5dfc87581a9fe6685775b83c0aa08a4b8a10389d1ae9c92bfbffbf1b1b076b244c736c74002688f6be84e4115f02e
7
+ data.tar.gz: ff7831e660833da76d85325532889590d27f60451747980427ce7e2d8f7f5e48ec02960d5fdad5bfb5ea7abadab9fdec19e6e30c5bb2adbc0be2e9527680ec3b
data/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.9] - 2026-03-30
11
+
12
+ ### Added
13
+ - **New pagination model**: `limit`/`offset`/`hasNext` — more efficient, avoids calculating total records
14
+ - Pass `limit:` to any `.all` or `.find_by` to use the new pagination
15
+ - Old `page`/`size` pagination emits deprecation warning (deadline: 2026-08-01)
16
+ - **New resources:**
17
+ - `Conexa::ReceivingMethod` — Meios de Recebimento (`/receivingMethods`, `/receivingMethod/:id`)
18
+ - `Conexa::PaymentMethod` — Meios de Pagamento (`/paymentMethods`, `/paymentMethod/:id`)
19
+ - `Conexa::BillCategory` — Categorias de Despesa (`/billCategories`, `/billCategory/:id`)
20
+ - `Conexa::BillSubcategory` — Subcategorias de Despesa (`/billSubcategories`, `/billSubcategory/:id`)
21
+ - `Conexa::CostCenter` — Centros de Custo (`/costCenters`, `/costCenter/:id`)
22
+ - `Conexa::Account` — Contas Bancárias (`/accounts`, `/account/:id`)
23
+ - `Conexa::ServiceCategory` — Categorias de Serviço (`/serviceCategories`, `/serviceCategory/:id`)
24
+ - `Conexa::RoomBooking` — Reservas de Sala (`/room/bookings`, `/room/booking`) with cancel, checkout, checkin
25
+ - `Product` now supports full CRUD (POST `/product`, DELETE `/product/:id`) per API v2 update
26
+
27
+ ### Changed
28
+ - `Model#extract_page_size_or_params` rewritten to support dual pagination modes
29
+ - `Customer.persons`, `Customer.contracts`, `Customer.charges` migrated to `limit: 100`
30
+
31
+ ### Fixed
32
+ - `Supplier` list URL corrected from `/supplier` to `/suppliers`
33
+ - `Supplier` now has `primary_key_attribute :supplier_id`
34
+ - `Product` now has `primary_key_attribute :product_id`
35
+
10
36
  ## [0.0.8] - 2026-02-19
11
37
 
12
38
  ### Fixed
@@ -61,7 +87,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
61
87
  - Charge with settle and PIX methods
62
88
  - Pagination support
63
89
 
64
- [Unreleased]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.8...HEAD
90
+ [Unreleased]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.9...HEAD
91
+ [0.0.9]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.8...v0.0.9
65
92
  [0.0.8]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.7...v0.0.8
66
93
  [0.0.7]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.6...v0.0.7
67
94
  [0.0.6]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.5...v0.0.6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- conexa (0.0.8)
4
+ conexa (0.0.9)
5
5
  jwt
6
6
  multi_json
7
7
  rest-client
data/README.md CHANGED
@@ -120,12 +120,11 @@ customer.legal_person['cnpj'] # => "99.557.155/0001-90"
120
120
  # Update customer
121
121
  Conexa::Customer.update(127, name: 'New Name', cell_number: '11888887777')
122
122
 
123
- # List customers with filters
123
+ # List customers with filters (new pagination)
124
124
  customers = Conexa::Customer.list(
125
125
  company_id: [3],
126
126
  is_active: true,
127
- page: 1,
128
- size: 20
127
+ limit: 20
129
128
  )
130
129
  ```
131
130
 
@@ -266,10 +265,75 @@ plan.price # => 99.90
266
265
 
267
266
  ```ruby
268
267
  # List products
269
- products = Conexa::Product.list(company_id: [3])
268
+ products = Conexa::Product.list(company_id: [3], limit: 50)
270
269
 
271
270
  # Retrieve product
272
271
  product = Conexa::Product.retrieve(101)
272
+
273
+ # Create product
274
+ product = Conexa::Product.create(name: 'Novo Produto', company_id: 3)
275
+
276
+ # Delete product
277
+ Conexa::Product.delete(101)
278
+ ```
279
+
280
+ ### Receiving Method
281
+
282
+ ```ruby
283
+ methods = Conexa::ReceivingMethod.list(limit: 50)
284
+ method = Conexa::ReceivingMethod.retrieve(11)
285
+ method.name # => "Cartão de Crédito"
286
+ ```
287
+
288
+ ### Payment Method
289
+
290
+ ```ruby
291
+ methods = Conexa::PaymentMethod.list(limit: 50)
292
+ method = Conexa::PaymentMethod.retrieve(2)
293
+ ```
294
+
295
+ ### Bill Category / Subcategory
296
+
297
+ ```ruby
298
+ categories = Conexa::BillCategory.list(limit: 50)
299
+ subcategories = Conexa::BillSubcategory.list(limit: 50)
300
+ ```
301
+
302
+ ### Cost Center
303
+
304
+ ```ruby
305
+ centers = Conexa::CostCenter.list(limit: 50)
306
+ center = Conexa::CostCenter.retrieve(11)
307
+ ```
308
+
309
+ ### Account
310
+
311
+ ```ruby
312
+ accounts = Conexa::Account.list(limit: 50)
313
+ account = Conexa::Account.retrieve(23)
314
+ ```
315
+
316
+ ### Service Category
317
+
318
+ ```ruby
319
+ categories = Conexa::ServiceCategory.list(limit: 50)
320
+ category = Conexa::ServiceCategory.retrieve(1)
321
+ ```
322
+
323
+ ### Room Booking
324
+
325
+ ```ruby
326
+ # List bookings
327
+ bookings = Conexa::RoomBooking.list(limit: 20)
328
+
329
+ # Create booking
330
+ booking = Conexa::RoomBooking.create(room_id: 5, customer_id: 127)
331
+
332
+ # Cancel booking
333
+ Conexa::RoomBooking.cancel(143063)
334
+
335
+ # Checkin
336
+ Conexa::RoomBooking.checkin(room_id: 5, customer_id: 127)
273
337
  ```
274
338
 
275
339
  ### Credit Card
@@ -306,25 +370,110 @@ company.document # => "12.345.678/0001-90"
306
370
 
307
371
  ## Pagination
308
372
 
309
- All list endpoints return paginated results:
373
+ ### New Pagination (recommended) `limit`/`offset`/`hasNext`
310
374
 
311
375
  ```ruby
312
- result = Conexa::Customer.list(page: 1, size: 20)
376
+ result = Conexa::Customer.list(limit: 50)
313
377
 
314
- result.data # Array of customers
378
+ result.data # Array of customers
379
+ result.pagination.limit # => 50
380
+ result.pagination.offset # => 0
381
+ result.pagination.has_next # => true/false
382
+
383
+ # Iterate through all pages
384
+ offset = 0
385
+ loop do
386
+ result = Conexa::Customer.list(limit: 50, offset: offset)
387
+ result.data.each { |customer| process(customer) }
388
+ break unless result.pagination.has_next
389
+ offset += 50
390
+ end
391
+ ```
392
+
393
+ ### Legacy Pagination (deprecated — will be removed 2026-08-01)
394
+
395
+ ```ruby
396
+ # Still works but emits a deprecation warning
397
+ result = Conexa::Customer.list(page: 1, size: 20)
315
398
  result.pagination.current_page # => 1
316
399
  result.pagination.total_pages # => 10
317
- result.pagination.total_items # => 195
318
- result.pagination.item_per_page # => 20
400
+ ```
401
+
402
+ ### Migration Guide: Legacy → New Pagination
403
+
404
+ The legacy `page`/`size` pagination is deprecated and will be removed on **2026-08-01**. Follow these steps to migrate:
405
+
406
+ #### 1. Replace `page`/`size` with `limit`/`offset`
407
+
408
+ ```ruby
409
+ # BEFORE (legacy — deprecated)
410
+ result = Conexa::Customer.list(page: 1, size: 50)
411
+ result = Conexa::Customer.list(page: 2, size: 50)
412
+
413
+ # AFTER (new)
414
+ result = Conexa::Customer.list(limit: 50) # offset defaults to 0
415
+ result = Conexa::Customer.list(limit: 50, offset: 50) # second page
416
+ ```
417
+
418
+ **Conversion formula:** `offset = (page - 1) * size`
319
419
 
320
- # Iterate through pages
420
+ #### 2. Update pagination metadata access
421
+
422
+ ```ruby
423
+ # BEFORE (legacy)
424
+ result.pagination # => { "page" => 1, "size" => 50, "total" => 150 }
425
+ total_pages = (result.pagination["total"].to_f / size).ceil
426
+
427
+ # AFTER (new)
428
+ result.pagination.limit # => 50
429
+ result.pagination.offset # => 0
430
+ result.pagination.has_next # => true/false
431
+ ```
432
+
433
+ #### 3. Update iteration loops
434
+
435
+ ```ruby
436
+ # BEFORE (legacy)
437
+ page = 1
321
438
  loop do
322
- result.data.each { |customer| process(customer) }
323
- break if result.pagination.current_page >= result.pagination.total_pages
324
- result = Conexa::Customer.list(page: result.pagination.current_page + 1)
439
+ result = Conexa::Customer.list(page: page, size: 100)
440
+ break if result.empty?
441
+ result.data.each { |c| process(c) }
442
+ page += 1
325
443
  end
444
+
445
+ # AFTER (new)
446
+ offset = 0
447
+ loop do
448
+ result = Conexa::Customer.list(limit: 100, offset: offset)
449
+ result.data.each { |c| process(c) }
450
+ break unless result.pagination.has_next
451
+ offset += 100
452
+ end
453
+ ```
454
+
455
+ #### 4. Remove positional arguments
456
+
457
+ ```ruby
458
+ # BEFORE (legacy positional args)
459
+ Conexa::Customer.all(2, 50) # page 2, size 50
460
+ Conexa::Customer.find_by({}, 1, 20) # page 1, size 20
461
+
462
+ # AFTER (new keyword args)
463
+ Conexa::Customer.all(limit: 50, offset: 50)
464
+ Conexa::Customer.find_by(limit: 20)
326
465
  ```
327
466
 
467
+ #### Quick reference
468
+
469
+ | Legacy | New |
470
+ |---|---|
471
+ | `page: 1, size: 50` | `limit: 50` (offset defaults to 0) |
472
+ | `page: N, size: S` | `limit: S, offset: (N-1)*S` |
473
+ | `pagination["total"]` | `pagination.has_next` |
474
+ | `pagination["page"]` | `pagination.offset` |
475
+ | Positional `(page, size)` | Keyword `limit:`, `offset:` |
476
+
328
477
  ## Error Handling
329
478
 
330
479
  ```ruby
@@ -360,7 +509,7 @@ end
360
509
 
361
510
  ## Rate Limiting
362
511
 
363
- The Conexa API has a limit of **100 requests per minute**. Response headers include:
512
+ The Conexa API has a limit of **100 requests per minute** (changing to **60 requests per minute** on 2026-04-27). Response headers include:
364
513
 
365
514
  - `X-Rate-Limit-Limit`: Maximum requests in 60s
366
515
  - `X-Rate-Limit-Remaining`: Remaining requests in 60s
data/README_pt-BR.md CHANGED
@@ -281,7 +281,7 @@ paginacao = pagina1.pagination
281
281
  # paginacao contém total de registros, total de páginas, etc.
282
282
  ```
283
283
 
284
- #### Iterando Por Todas as Páginas
284
+ #### Iterando Por Todas as Páginas (legado — depreciado)
285
285
 
286
286
  ```ruby
287
287
  pagina = 1
@@ -301,6 +301,101 @@ loop do
301
301
  end
302
302
  ```
303
303
 
304
+ ### Nova Paginação (recomendada) — `limit`/`offset`/`hasNext`
305
+
306
+ ```ruby
307
+ resultado = Conexa::Customer.all(limit: 50)
308
+
309
+ resultado.data # Array de clientes
310
+ resultado.pagination.limit # => 50
311
+ resultado.pagination.offset # => 0
312
+ resultado.pagination.has_next # => true/false
313
+
314
+ # Iterar por todas as páginas
315
+ offset = 0
316
+ loop do
317
+ resultado = Conexa::Customer.all(limit: 50, offset: offset)
318
+ resultado.data.each { |cliente| processa(cliente) }
319
+ break unless resultado.pagination.has_next
320
+ offset += 50
321
+ end
322
+ ```
323
+
324
+ ### Guia de Migração: Paginação Legada → Nova
325
+
326
+ A paginação legada `page`/`size` está depreciada e será removida em **01/08/2026**. Siga estes passos para migrar:
327
+
328
+ #### 1. Substitua `page`/`size` por `limit`/`offset`
329
+
330
+ ```ruby
331
+ # ANTES (legado — depreciado)
332
+ resultado = Conexa::Customer.all(page: 1, size: 50)
333
+ resultado = Conexa::Customer.all(page: 2, size: 50)
334
+
335
+ # DEPOIS (novo)
336
+ resultado = Conexa::Customer.all(limit: 50) # offset padrão 0
337
+ resultado = Conexa::Customer.all(limit: 50, offset: 50) # segunda página
338
+ ```
339
+
340
+ **Fórmula de conversão:** `offset = (page - 1) * size`
341
+
342
+ #### 2. Atualize o acesso aos metadados de paginação
343
+
344
+ ```ruby
345
+ # ANTES (legado)
346
+ resultado.pagination # => { "page" => 1, "size" => 50, "total" => 150 }
347
+ total_paginas = (resultado.pagination["total"].to_f / tamanho).ceil
348
+
349
+ # DEPOIS (novo)
350
+ resultado.pagination.limit # => 50
351
+ resultado.pagination.offset # => 0
352
+ resultado.pagination.has_next # => true/false
353
+ ```
354
+
355
+ #### 3. Atualize os loops de iteração
356
+
357
+ ```ruby
358
+ # ANTES (legado)
359
+ pagina = 1
360
+ loop do
361
+ resultado = Conexa::Customer.all(page: pagina, size: 100)
362
+ break if resultado.empty?
363
+ resultado.data.each { |c| processa(c) }
364
+ pagina += 1
365
+ end
366
+
367
+ # DEPOIS (novo)
368
+ offset = 0
369
+ loop do
370
+ resultado = Conexa::Customer.all(limit: 100, offset: offset)
371
+ resultado.data.each { |c| processa(c) }
372
+ break unless resultado.pagination.has_next
373
+ offset += 100
374
+ end
375
+ ```
376
+
377
+ #### 4. Remova argumentos posicionais
378
+
379
+ ```ruby
380
+ # ANTES (argumentos posicionais legados)
381
+ Conexa::Customer.all(2, 50) # página 2, tamanho 50
382
+ Conexa::Customer.find_by({}, 1, 20) # página 1, tamanho 20
383
+
384
+ # DEPOIS (argumentos nomeados)
385
+ Conexa::Customer.all(limit: 50, offset: 50)
386
+ Conexa::Customer.find_by(limit: 20)
387
+ ```
388
+
389
+ #### Referência rápida
390
+
391
+ | Legado | Novo |
392
+ |---|---|
393
+ | `page: 1, size: 50` | `limit: 50` (offset padrão 0) |
394
+ | `page: N, size: S` | `limit: S, offset: (N-1)*S` |
395
+ | `pagination["total"]` | `pagination.has_next` |
396
+ | `pagination["page"]` | `pagination.offset` |
397
+ | Posicional `(page, size)` | Nomeado `limit:`, `offset:` |
398
+
304
399
  ### Exemplos
305
400
 
306
401
  #### Clientes (Customers)
data/REFERENCE.md CHANGED
@@ -14,17 +14,25 @@ Conexa is a Brazilian SaaS platform for **recurring billing**, **subscription ma
14
14
  - **Sale** - One-time sale (not recurring)
15
15
  - **RecurringSale** - Recurring sale item within a contract
16
16
  - **Plan** - Pricing plan with products and periodicities
17
- - **Product** - Billable item/service
17
+ - **Product** - Billable item/service (now supports full CRUD)
18
18
  - **InvoicingMethod** - Payment method configuration (boleto, PIX, credit card)
19
+ - **ReceivingMethod** - Receiving method configuration (cash, transfer, card machine)
20
+ - **PaymentMethod** - Payment method type (boleto, PIX, cartão)
21
+ - **BillCategory** - Bill expense category
22
+ - **BillSubcategory** - Bill expense subcategory
23
+ - **CostCenter** - Cost center for expense allocation
24
+ - **Account** - Bank account
25
+ - **ServiceCategory** - Service category for plans
19
26
  - **Person** - Requester (solicitante) linked to a customer
20
27
  - **Bill** - Financial bill (conta a pagar)
21
28
  - **Supplier** - Supplier with PF/PJ data
29
+ - **RoomBooking** - Room booking with checkin/checkout (Conexa Coworking)
22
30
 
23
31
  ## Installation
24
32
 
25
33
  ```ruby
26
34
  # Gemfile
27
- gem 'conexa', '~> 0.0.7'
35
+ gem 'conexa', '~> 0.0.9'
28
36
 
29
37
  # Or install directly
30
38
  gem install conexa
@@ -694,12 +702,16 @@ product = Conexa::Product.find(100)
694
702
  product.name # => "Mensalidade"
695
703
 
696
704
  # List products
697
- products = Conexa::Product.all(company_id: [3])
698
- ```
705
+ products = Conexa::Product.all(company_id: [3], limit: 50)
706
+
707
+ # Create product
708
+ product = Conexa::Product.create(name: 'Novo Produto', company_id: 3)
699
709
 
700
- **API endpoints:** `GET /product/:id`, `GET /products`
710
+ # Delete product
711
+ Conexa::Product.destroy(100)
712
+ ```
701
713
 
702
- **Note:** Products cannot be created/updated via this gem. `save` raises `NoMethodError`.
714
+ **API endpoints:** `GET /product/:id`, `GET /products`, `POST /product`, `DELETE /product/:id`
703
715
 
704
716
  ---
705
717
 
@@ -855,7 +867,7 @@ supplier.save
855
867
  supplier.destroy
856
868
  ```
857
869
 
858
- **API endpoints:** `POST /supplier`, `GET /supplier/:id`, `PATCH /supplier/:id`, `DELETE /supplier/:id`, `GET /supplier` (list)
870
+ **API endpoints:** `POST /supplier`, `GET /supplier/:id`, `PATCH /supplier/:id`, `DELETE /supplier/:id`, `GET /suppliers`
859
871
 
860
872
  **Create attributes:**
861
873
  - `name` - Supplier name (required)
@@ -1020,27 +1032,121 @@ person.destroy
1020
1032
 
1021
1033
  ### Pagination
1022
1034
 
1023
- All `#all` and `#find_by` methods support pagination:
1035
+ #### New Pagination (recommended) `limit`/`offset`/`hasNext`
1036
+
1037
+ All `#all` and `#find_by` methods support the new pagination when `limit:` is passed:
1038
+
1039
+ ```ruby
1040
+ # Page 1, 50 items
1041
+ result = Conexa::Customer.all(limit: 50)
1042
+
1043
+ result.data # => Array of customers
1044
+ result.pagination.limit # => 50
1045
+ result.pagination.offset # => 0
1046
+ result.pagination.has_next # => true/false
1047
+ result.empty? # => false
1048
+
1049
+ # Iterate all pages
1050
+ offset = 0
1051
+ loop do
1052
+ result = Conexa::Customer.all(limit: 100, offset: offset)
1053
+ break if result.empty?
1054
+
1055
+ result.each { |customer| process(customer) }
1056
+ break unless result.pagination.has_next
1057
+ offset += 100
1058
+ end
1059
+ ```
1060
+
1061
+ #### Legacy Pagination (deprecated — removed 2026-08-01)
1062
+
1063
+ If `limit:` is NOT passed, the gem falls back to the old `page`/`size` model
1064
+ (emitting a deprecation warning):
1024
1065
 
1025
1066
  ```ruby
1026
- # Page 1, 50 items per page
1067
+ # Emits: DEPRECATION WARNING: O modelo antigo de paginação...
1027
1068
  result = Conexa::Customer.all(page: 1, size: 50)
1028
1069
 
1029
1070
  result.data # => Array of customers
1030
1071
  result.pagination # => { "page" => 1, "size" => 50, "total" => 150 }
1031
1072
  result.empty? # => false
1073
+ ```
1032
1074
 
1033
- # Iterate all pages
1075
+ #### Migration Guide: Legacy → New Pagination
1076
+
1077
+ The legacy `page`/`size` pagination is deprecated and will be removed on **2026-08-01**. Follow these steps to migrate:
1078
+
1079
+ **Step 1 — Replace `page`/`size` with `limit`/`offset`:**
1080
+
1081
+ ```ruby
1082
+ # BEFORE (legacy)
1083
+ result = Conexa::Customer.all(page: 1, size: 50)
1084
+ result = Conexa::Customer.all(page: 2, size: 50)
1085
+
1086
+ # AFTER (new)
1087
+ result = Conexa::Customer.all(limit: 50) # offset defaults to 0
1088
+ result = Conexa::Customer.all(limit: 50, offset: 50) # second page
1089
+ ```
1090
+
1091
+ Conversion formula: `offset = (page - 1) * size`
1092
+
1093
+ **Step 2 — Update pagination metadata access:**
1094
+
1095
+ ```ruby
1096
+ # BEFORE
1097
+ result.pagination # => { "page" => 1, "size" => 50, "total" => 150 }
1098
+ total_pages = (result.pagination["total"].to_f / size).ceil
1099
+
1100
+ # AFTER
1101
+ result.pagination.limit # => 50
1102
+ result.pagination.offset # => 0
1103
+ result.pagination.has_next # => true/false
1104
+ ```
1105
+
1106
+ **Step 3 — Update iteration loops:**
1107
+
1108
+ ```ruby
1109
+ # BEFORE
1034
1110
  page = 1
1035
1111
  loop do
1036
1112
  result = Conexa::Customer.all(page: page, size: 100)
1037
1113
  break if result.empty?
1038
-
1039
- result.each { |customer| process(customer) }
1114
+ result.each { |c| process(c) }
1040
1115
  page += 1
1041
1116
  end
1117
+
1118
+ # AFTER
1119
+ offset = 0
1120
+ loop do
1121
+ result = Conexa::Customer.all(limit: 100, offset: offset)
1122
+ result.each { |c| process(c) }
1123
+ break unless result.pagination.has_next
1124
+ offset += 100
1125
+ end
1126
+ ```
1127
+
1128
+ **Step 4 — Remove positional arguments:**
1129
+
1130
+ ```ruby
1131
+ # BEFORE
1132
+ Conexa::Customer.all(2, 50) # page 2, size 50
1133
+ Conexa::Customer.find_by({}, 1, 20) # page 1, size 20
1134
+
1135
+ # AFTER
1136
+ Conexa::Customer.all(limit: 50, offset: 50)
1137
+ Conexa::Customer.find_by(limit: 20)
1042
1138
  ```
1043
1139
 
1140
+ **Quick reference:**
1141
+
1142
+ | Legacy | New |
1143
+ |---|---|
1144
+ | `page: 1, size: 50` | `limit: 50` (offset defaults to 0) |
1145
+ | `page: N, size: S` | `limit: S, offset: (N-1)*S` |
1146
+ | `pagination["total"]` | `pagination.has_next` |
1147
+ | `pagination["page"]` | `pagination.offset` |
1148
+ | Positional `(page, size)` | Keyword `limit:`, `offset:` |
1149
+
1044
1150
  ### Result Object
1045
1151
 
1046
1152
  API calls return `Conexa::Result` objects:
@@ -1109,7 +1215,7 @@ Conexa::Charge.all(
1109
1215
 
1110
1216
  ### Rate Limiting
1111
1217
 
1112
- The API enforces a limit of 100 requests per minute. Response headers:
1218
+ The API enforces a limit of 100 requests per minute (changing to **60 requests per minute** on 2026-04-27). Response headers:
1113
1219
  - `X-Rate-Limit-Limit` - Maximum requests per minute
1114
1220
  - `X-Rate-Limit-Remaining` - Remaining requests
1115
1221
  - `X-Rate-Limit-Reset` - Seconds until limit resets
@@ -1127,13 +1233,21 @@ The API enforces a limit of 100 requests per minute. Response headers:
1127
1233
  | Sale | yes | yes | yes | yes | yes | billed?, paid?, editable? |
1128
1234
  | RecurringSale | yes | yes | yes | yes | yes | end_recurring_sale |
1129
1235
  | Plan | yes | yes | yes | - | yes | - |
1130
- | Product | yes | yes | - | - | - | - |
1236
+ | Product | yes | yes | yes | - | yes | - |
1131
1237
  | InvoicingMethod | yes | yes | yes | yes | yes | - |
1238
+ | ReceivingMethod | yes | yes | - | - | - | - |
1239
+ | PaymentMethod | yes | yes | - | - | - | - |
1240
+ | BillCategory | yes | yes | - | - | - | - |
1241
+ | BillSubcategory | yes | yes | - | - | - | - |
1242
+ | CostCenter | yes | yes | - | - | - | - |
1243
+ | Account | yes | yes | - | - | - | - |
1244
+ | ServiceCategory | yes | yes | - | - | - | - |
1132
1245
  | Bill | yes | yes | yes | - | - | - |
1133
1246
  | Company | yes | yes | yes | yes | yes | - |
1134
1247
  | Supplier | yes | yes | yes | yes | yes | - |
1135
1248
  | CreditCard | yes | - | yes | yes | yes | - |
1136
1249
  | Person | - | - | yes | yes | yes | - |
1250
+ | RoomBooking | yes | yes | yes | - | - | cancel, checkout, checkin |
1137
1251
 
1138
1252
  **Legend:** `yes` = available, `-` = not available (raises NoMethodError or not implemented)
1139
1253
 
data/lib/conexa/model.rb CHANGED
@@ -104,7 +104,7 @@ module Conexa
104
104
 
105
105
  def find_by(params = Hash.new, page = nil, size = nil)
106
106
  params = extract_page_size_or_params(page, size, **params)
107
- raise RequestError.new('Invalid page size') if params[:page] < 1 or params[:size] < 1
107
+ raise RequestError.new('Invalid page size') if (!params.key?(:limit)) && (params[:page] < 1 or params[:size] < 1)
108
108
 
109
109
  Conexa::Request.get(url, params: params).call underscored_class_name
110
110
  end
@@ -139,8 +139,28 @@ module Conexa
139
139
  end
140
140
 
141
141
  def extract_page_size_or_params(*args, **params)
142
- params[:page] ||= args[0] || 1
143
- params[:size] ||= args[1] || 100
142
+ if args[0].is_a?(Hash)
143
+ params = args[0].merge(params)
144
+ page_val = nil
145
+ else
146
+ page_val = args[0]
147
+ end
148
+ size_val = args[1]
149
+
150
+ if params.key?(:limit)
151
+ params[:offset] ||= size_val if size_val.is_a?(Integer)
152
+ params[:offset] ||= 0
153
+ params.delete(:page) # Fix to ensure the api doesn't get confused
154
+ params.delete(:size)
155
+ return params
156
+ end
157
+
158
+ if params.key?(:page) || params.key?(:size) || page_val.is_a?(Integer)
159
+ warn "DEPRECATION WARNING: O modelo antigo de paginação (page/size) será removido em 01 de agosto de 2026. Utilize limit e offset."
160
+ end
161
+
162
+ params[:page] ||= page_val || 1
163
+ params[:size] ||= size_val || 100
144
164
  params
145
165
  end
146
166
  end
data/lib/conexa/object.rb CHANGED
@@ -111,7 +111,12 @@ module Conexa
111
111
  end
112
112
 
113
113
  if args.size == 0
114
- return self[name] || self[name.to_sym]
114
+ if @attributes.key?(name)
115
+ return @attributes[name]
116
+ elsif @attributes.key?(name.to_sym)
117
+ return @attributes[name.to_sym]
118
+ end
119
+ return nil
115
120
  end
116
121
  end
117
122
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # Account resource (Conta Bancária)
5
+ #
6
+ # @example Find an account
7
+ # account = Conexa::Account.find(23)
8
+ # account.name # => "Banco do Brasil"
9
+ #
10
+ # @example List accounts
11
+ # accounts = Conexa::Account.all(limit: 50)
12
+ #
13
+ # @!attribute [r] account_id
14
+ # @return [Integer] Account ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Account name
17
+ # @!attribute [r] is_active
18
+ # @return [Boolean] Whether the account is active
19
+ #
20
+ class Account < Model
21
+ primary_key_attribute :account_id
22
+
23
+ class << self
24
+ def url(*params)
25
+ ["/accounts", *params].join '/'
26
+ end
27
+
28
+ def show_url(*params)
29
+ ["/account", *params].join '/'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # BillCategory resource (Categoria de Despesa)
5
+ #
6
+ # @example Find a bill category
7
+ # category = Conexa::BillCategory.find(4)
8
+ # category.name # => "Impostos"
9
+ #
10
+ # @example List bill categories
11
+ # categories = Conexa::BillCategory.all(limit: 50)
12
+ #
13
+ # @!attribute [r] bill_category_id
14
+ # @return [Integer] Bill category ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Category name
17
+ # @!attribute [r] is_active
18
+ # @return [Boolean] Whether the category is active
19
+ #
20
+ class BillCategory < Model
21
+ primary_key_attribute :bill_category_id
22
+
23
+ class << self
24
+ def url(*params)
25
+ ["/billCategories", *params].join '/'
26
+ end
27
+
28
+ def show_url(*params)
29
+ ["/billCategory", *params].join '/'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # BillSubcategory resource (Subcategoria de Despesa)
5
+ #
6
+ # @example Find a bill subcategory
7
+ # subcategory = Conexa::BillSubcategory.find(29)
8
+ # subcategory.name # => "Taxa de Cartão"
9
+ #
10
+ # @example List bill subcategories
11
+ # subcategories = Conexa::BillSubcategory.all(limit: 50)
12
+ #
13
+ # @!attribute [r] bill_subcategory_id
14
+ # @return [Integer] Bill subcategory ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Subcategory name
17
+ # @!attribute [r] bill_category_id
18
+ # @return [Integer] Parent bill category ID
19
+ # @!attribute [r] is_active
20
+ # @return [Boolean] Whether the subcategory is active
21
+ #
22
+ class BillSubcategory < Model
23
+ primary_key_attribute :bill_subcategory_id
24
+
25
+ class << self
26
+ def url(*params)
27
+ ["/billSubcategories", *params].join '/'
28
+ end
29
+
30
+ def show_url(*params)
31
+ ["/billSubcategory", *params].join '/'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -8,7 +8,7 @@ module Conexa
8
8
  # charge.status # => "pending"
9
9
  #
10
10
  # @example List charges
11
- # charges = Conexa::Charge.all(customer_id: [127], status: 'pending')
11
+ # charges = Conexa::Charge.all(customer_id: [127], status: 'pending', limit: 50)
12
12
  #
13
13
  # @example Settle (pay) a charge
14
14
  # Conexa::Charge.settle(789)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # CostCenter resource (Centro de Custo)
5
+ #
6
+ # @example Find a cost center
7
+ # center = Conexa::CostCenter.find(11)
8
+ # center.name # => "Marketing"
9
+ #
10
+ # @example List cost centers
11
+ # centers = Conexa::CostCenter.all(limit: 50)
12
+ #
13
+ # @!attribute [r] cost_center_id
14
+ # @return [Integer] Cost center ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Cost center name
17
+ # @!attribute [r] is_active
18
+ # @return [Boolean] Whether the cost center is active
19
+ #
20
+ class CostCenter < Model
21
+ primary_key_attribute :cost_center_id
22
+
23
+ class << self
24
+ def url(*params)
25
+ ["/costCenters", *params].join '/'
26
+ end
27
+
28
+ def show_url(*params)
29
+ ["/costCenter", *params].join '/'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -76,21 +76,21 @@ module Conexa
76
76
  # @param customer_id [Integer] Customer ID
77
77
  # @return [Result] List of persons
78
78
  def persons(customer_id)
79
- Conexa::Person.all(customer_id: customer_id)
79
+ Conexa::Person.all(customer_id: customer_id, limit: 100)
80
80
  end
81
81
 
82
82
  # List contracts for a customer
83
83
  # @param customer_id [Integer] Customer ID
84
84
  # @return [Result] List of contracts
85
85
  def contracts(customer_id)
86
- Conexa::Contract.all(customer_id: [customer_id])
86
+ Conexa::Contract.all(customer_id: [customer_id], limit: 100)
87
87
  end
88
88
 
89
89
  # List charges for a customer
90
90
  # @param customer_id [Integer] Customer ID
91
91
  # @return [Result] List of charges
92
92
  def charges(customer_id)
93
- Conexa::Charge.all(customer_id: [customer_id])
93
+ Conexa::Charge.all(customer_id: [customer_id], limit: 100)
94
94
  end
95
95
  end
96
96
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # PaymentMethod resource (Meio de Pagamento)
5
+ #
6
+ # @example Find a payment method
7
+ # method = Conexa::PaymentMethod.find(2)
8
+ # method.name # => "Boleto"
9
+ #
10
+ # @example List payment methods
11
+ # methods = Conexa::PaymentMethod.all(limit: 50)
12
+ #
13
+ # @!attribute [r] payment_method_id
14
+ # @return [Integer] Payment method ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Name
17
+ # @!attribute [r] is_active
18
+ # @return [Boolean] Whether the payment method is active
19
+ #
20
+ class PaymentMethod < Model
21
+ primary_key_attribute :payment_method_id
22
+
23
+ class << self
24
+ def url(*params)
25
+ ["/paymentMethods", *params].join '/'
26
+ end
27
+
28
+ def show_url(*params)
29
+ ["/paymentMethod", *params].join '/'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,7 +1,29 @@
1
1
  module Conexa
2
- class Product < Model
3
- def save
4
- raise NoMethodError
5
- end
2
+ # Product resource (read-only for listing/retrieving)
3
+ #
4
+ # The new API v2 also supports POST /product and DELETE /product/:id.
5
+ #
6
+ # @example Find a product
7
+ # product = Conexa::Product.find(100)
8
+ # product.name # => "Mensalidade"
9
+ #
10
+ # @example List products
11
+ # products = Conexa::Product.all(company_id: [3], limit: 50)
12
+ #
13
+ # @example Create a product
14
+ # product = Conexa::Product.create(name: 'Novo Produto', company_id: 3)
15
+ #
16
+ # @example Delete a product
17
+ # Conexa::Product.destroy(100)
18
+ #
19
+ # @!attribute [r] product_id
20
+ # @return [Integer] Product ID (also accessible as #id)
21
+ # @!attribute [r] name
22
+ # @return [String] Product name
23
+ # @!attribute [r] is_active
24
+ # @return [Boolean] Whether the product is active
25
+ #
26
+ class Product < Model
27
+ primary_key_attribute :product_id
6
28
  end
7
29
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # ReceivingMethod resource (Meio de Recebimento)
5
+ #
6
+ # @example Find a receiving method
7
+ # method = Conexa::ReceivingMethod.find(11)
8
+ # method.name # => "Cartão de Crédito"
9
+ #
10
+ # @example List receiving methods
11
+ # methods = Conexa::ReceivingMethod.all(limit: 50)
12
+ #
13
+ # @!attribute [r] receiving_method_id
14
+ # @return [Integer] Receiving method ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Name
17
+ # @!attribute [r] max_installments
18
+ # @return [Integer] Maximum number of installments
19
+ # @!attribute [r] credit_days
20
+ # @return [Integer] Days until credit
21
+ # @!attribute [r] is_installment_fee
22
+ # @return [Boolean] Whether fee is per installment
23
+ # @!attribute [r] transaction_fee
24
+ # @return [Float] Fee per transaction
25
+ # @!attribute [r] transaction_rate
26
+ # @return [Float] Rate percentage per transaction
27
+ # @!attribute [r] account_id
28
+ # @return [Integer, nil] Associated account ID
29
+ # @!attribute [r] cost_center_id
30
+ # @return [Integer, nil] Cost center ID
31
+ # @!attribute [r] bill_category_id
32
+ # @return [Integer, nil] Bill category ID
33
+ # @!attribute [r] bill_subcategory_id
34
+ # @return [Integer, nil] Bill subcategory ID
35
+ # @!attribute [r] payment_method_id
36
+ # @return [Integer, nil] Payment method ID
37
+ # @!attribute [r] supplier_id
38
+ # @return [Integer, nil] Supplier ID
39
+ # @!attribute [r] is_active
40
+ # @return [Boolean] Whether the receiving method is active
41
+ #
42
+ class ReceivingMethod < Model
43
+ primary_key_attribute :receiving_method_id
44
+
45
+ class << self
46
+ def url(*params)
47
+ ["/receivingMethods", *params].join '/'
48
+ end
49
+
50
+ def show_url(*params)
51
+ ["/receivingMethod", *params].join '/'
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # RoomBooking resource (Reserva de Sala)
5
+ #
6
+ # @example Create a room booking
7
+ # booking = Conexa::RoomBooking.create(
8
+ # room_id: 5,
9
+ # customer_id: 127,
10
+ # start_date: '2026-04-01T10:00:00-03:00',
11
+ # end_date: '2026-04-01T12:00:00-03:00'
12
+ # )
13
+ #
14
+ # @example Find a room booking
15
+ # booking = Conexa::RoomBooking.find(143063)
16
+ #
17
+ # @example List room bookings
18
+ # bookings = Conexa::RoomBooking.all(limit: 50)
19
+ #
20
+ # @example Cancel a booking
21
+ # Conexa::RoomBooking.cancel(143063)
22
+ #
23
+ # @!attribute [r] booking_id
24
+ # @return [Integer] Booking ID (also accessible as #id)
25
+ # @!attribute [r] room_id
26
+ # @return [Integer] Room ID
27
+ # @!attribute [r] customer_id
28
+ # @return [Integer] Customer ID
29
+ # @!attribute [r] status
30
+ # @return [String] Booking status
31
+ #
32
+ class RoomBooking < Model
33
+ primary_key_attribute :booking_id
34
+
35
+ # Cancel this booking
36
+ # @return [self]
37
+ def cancel
38
+ Conexa::Request.patch(self.class.show_url(primary_key, "cancel")).call(class_name)
39
+ self
40
+ end
41
+
42
+ # Checkout this booking
43
+ # @return [self]
44
+ def checkout
45
+ Conexa::Request.post(self.class.show_url(primary_key, "checkout")).call(class_name)
46
+ self
47
+ end
48
+
49
+ class << self
50
+ def url(*params)
51
+ ["/room/bookings", *params].join '/'
52
+ end
53
+
54
+ def show_url(*params)
55
+ ["/room/booking", *params].join '/'
56
+ end
57
+
58
+ # Cancel a booking by ID
59
+ # @param id [Integer, String] booking ID
60
+ # @return [RoomBooking]
61
+ def cancel(id)
62
+ find(id).cancel
63
+ end
64
+
65
+ # Checkout a booking by ID
66
+ # @param id [Integer, String] booking ID
67
+ # @return [RoomBooking]
68
+ def checkout(id)
69
+ find(id).checkout
70
+ end
71
+
72
+ # Perform a checkin
73
+ # @param params [Hash] checkin parameters
74
+ # @return [ConexaObject]
75
+ def checkin(params = {})
76
+ Conexa::Request.post("/checkin", params: params).call("room_booking")
77
+ end
78
+
79
+ # Perform a checkout (standalone)
80
+ # @param params [Hash] checkout parameters
81
+ # @return [ConexaObject]
82
+ def standalone_checkout(params = {})
83
+ Conexa::Request.post("/checkout", params: params).call("room_booking")
84
+ end
85
+ end
86
+ end
87
+ end
@@ -12,7 +12,7 @@ module Conexa
12
12
  # )
13
13
  #
14
14
  # @example List sales
15
- # sales = Conexa::Sale.all(customer_id: [450], status: 'notBilled')
15
+ # sales = Conexa::Sale.all(customer_id: [450], status: 'notBilled', limit: 50)
16
16
  #
17
17
  # @!attribute [r] sale_id
18
18
  # @return [Integer] Sale ID (also accessible as #id)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conexa
4
+ # ServiceCategory resource (Categoria de Serviço)
5
+ #
6
+ # @example Find a service category
7
+ # category = Conexa::ServiceCategory.find(1)
8
+ # category.name # => "Consultoria"
9
+ #
10
+ # @example List service categories
11
+ # categories = Conexa::ServiceCategory.all(limit: 50)
12
+ #
13
+ # @!attribute [r] service_category_id
14
+ # @return [Integer] Service category ID (also accessible as #id)
15
+ # @!attribute [r] name
16
+ # @return [String] Category name
17
+ # @!attribute [r] is_active
18
+ # @return [Boolean] Whether the category is active
19
+ #
20
+ class ServiceCategory < Model
21
+ primary_key_attribute :service_category_id
22
+
23
+ class << self
24
+ def url(*params)
25
+ ["/serviceCategories", *params].join '/'
26
+ end
27
+
28
+ def show_url(*params)
29
+ ["/serviceCategory", *params].join '/'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,8 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Conexa
4
+ # Supplier resource (Fornecedor)
5
+ #
6
+ # @example Create a supplier
7
+ # supplier = Conexa::Supplier.create(name: 'Fornecedor ABC')
8
+ #
9
+ # @example Find a supplier
10
+ # supplier = Conexa::Supplier.find(10)
11
+ # supplier.name # => "Fornecedor ABC"
12
+ #
13
+ # @example List suppliers
14
+ # suppliers = Conexa::Supplier.all(limit: 50)
15
+ #
16
+ # @!attribute [r] supplier_id
17
+ # @return [Integer] Supplier ID (also accessible as #id)
18
+ # @!attribute [r] name
19
+ # @return [String] Supplier name
20
+ # @!attribute [r] is_active
21
+ # @return [Boolean] Whether the supplier is active
22
+ #
2
23
  class Supplier < Model
24
+ primary_key_attribute :supplier_id
25
+
3
26
  class << self
4
27
  def url(*params)
5
- ["/supplier", *params].join '/'
28
+ ["/suppliers", *params].join '/'
6
29
  end
7
30
 
8
31
  def show_url(*params)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Conexa
4
- VERSION = "0.0.8"
4
+ VERSION = "0.0.9"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conexa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guilherme Gazzinelli
@@ -155,23 +155,31 @@ files:
155
155
  - lib/conexa/object.rb
156
156
  - lib/conexa/order_commom.rb
157
157
  - lib/conexa/request.rb
158
+ - lib/conexa/resources/account.rb
158
159
  - lib/conexa/resources/address.rb
159
160
  - lib/conexa/resources/auth.rb
160
161
  - lib/conexa/resources/bill.rb
162
+ - lib/conexa/resources/bill_category.rb
163
+ - lib/conexa/resources/bill_subcategory.rb
161
164
  - lib/conexa/resources/charge.rb
162
165
  - lib/conexa/resources/company.rb
163
166
  - lib/conexa/resources/contract.rb
167
+ - lib/conexa/resources/cost_center.rb
164
168
  - lib/conexa/resources/credit_card.rb
165
169
  - lib/conexa/resources/customer.rb
166
170
  - lib/conexa/resources/invoicing_method.rb
167
171
  - lib/conexa/resources/legal_person.rb
168
172
  - lib/conexa/resources/pagination.rb
173
+ - lib/conexa/resources/payment_method.rb
169
174
  - lib/conexa/resources/person.rb
170
175
  - lib/conexa/resources/plan.rb
171
176
  - lib/conexa/resources/product.rb
177
+ - lib/conexa/resources/receiving_method.rb
172
178
  - lib/conexa/resources/recurring_sale.rb
173
179
  - lib/conexa/resources/result.rb
180
+ - lib/conexa/resources/room_booking.rb
174
181
  - lib/conexa/resources/sale.rb
182
+ - lib/conexa/resources/service_category.rb
175
183
  - lib/conexa/resources/supplier.rb
176
184
  - lib/conexa/token_manager.rb
177
185
  - lib/conexa/util.rb