conexa 0.0.6 → 0.0.8

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.
data/REFERENCE.md ADDED
@@ -0,0 +1,1152 @@
1
+ # Conexa Ruby - LLM Reference
2
+
3
+ > Ruby client for the Conexa API - Brazilian billing and subscription management platform.
4
+
5
+ ## Overview
6
+
7
+ Conexa is a Brazilian SaaS platform for **recurring billing**, **subscription management**, and **financial operations**. This gem provides a Ruby interface to their REST API v2.
8
+
9
+ **Key concepts:**
10
+ - **Auth** - Username/password authentication (JWT)
11
+ - **Customer** - Client (PF or PJ) who receives invoices
12
+ - **Contract** - Recurring billing agreement with a plan
13
+ - **Charge** - Individual invoice/boleto generated for payment
14
+ - **Sale** - One-time sale (not recurring)
15
+ - **RecurringSale** - Recurring sale item within a contract
16
+ - **Plan** - Pricing plan with products and periodicities
17
+ - **Product** - Billable item/service
18
+ - **InvoicingMethod** - Payment method configuration (boleto, PIX, credit card)
19
+ - **Person** - Requester (solicitante) linked to a customer
20
+ - **Bill** - Financial bill (conta a pagar)
21
+ - **Supplier** - Supplier with PF/PJ data
22
+
23
+ ## Installation
24
+
25
+ ```ruby
26
+ # Gemfile
27
+ gem 'conexa', '~> 0.0.7'
28
+
29
+ # Or install directly
30
+ gem install conexa
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ ```ruby
36
+ Conexa.configure do |config|
37
+ config.api_host = 'https://api.conexa.com.br' # or sandbox URL
38
+ config.api_token = 'your_api_token_here'
39
+ end
40
+
41
+ # Rails: use generator
42
+ rails generate conexa:install
43
+ # Creates config/initializers/conexa.rb
44
+ ```
45
+
46
+ ## Convention: snake_case
47
+
48
+ This gem follows Ruby/Rails conventions. Use **snake_case** for all parameters - the gem automatically converts to camelCase for the API.
49
+
50
+ ```ruby
51
+ # Correct - snake_case
52
+ Conexa::Customer.create(
53
+ company_id: 3,
54
+ legal_person: { cnpj: '99.557.155/0001-90' }
55
+ )
56
+
57
+ # Avoid - camelCase (works but not idiomatic)
58
+ Conexa::Customer.create(
59
+ companyId: 3,
60
+ legalPerson: { cnpj: '99.557.155/0001-90' }
61
+ )
62
+ ```
63
+
64
+ Response attributes are also accessible in snake_case:
65
+ ```ruby
66
+ customer = Conexa::Customer.find(127)
67
+ customer.customer_id # => 127
68
+ customer.company_id # => 3
69
+ customer.is_active # => true
70
+ customer.legal_person # => { cnpj: '...' }
71
+ ```
72
+
73
+ ## Resources
74
+
75
+ ### Auth
76
+
77
+ Authenticates with username/password and returns a JWT token. Does not require a pre-configured `api_token`.
78
+
79
+ ```ruby
80
+ # Authenticate
81
+ auth = Conexa::Auth.login(username: 'admin', password: 'secret')
82
+ # or
83
+ auth = Conexa::Auth.authenticate(username: 'admin', password: 'secret')
84
+
85
+ auth.access_token # => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
86
+ auth.token_type # => "Bearer"
87
+ auth.expires_in # => 28800 (8 hours in seconds)
88
+ auth.user.id # => 1
89
+ auth.user.type # => "admin" or "employee"
90
+ auth.user.name # => "Luke Skywalker"
91
+
92
+ # Use the token for subsequent requests
93
+ Conexa.configure { |c| c.api_token = auth.access_token }
94
+ ```
95
+
96
+ **API endpoint:** `POST /auth`
97
+
98
+ **Request body:**
99
+ - `username` - Login username (admin login or employee email)
100
+ - `password` - Password
101
+
102
+ **Response attributes:**
103
+ - `user` - User object with `id`, `type`, `name`
104
+ - `token_type` - Always "Bearer"
105
+ - `access_token` - JWT token for subsequent requests
106
+ - `expires_in` - Token expiration in seconds (28800 = 8 hours)
107
+
108
+ **Errors:**
109
+ - `Conexa::ResponseError` - Invalid credentials (401), validation errors (400)
110
+
111
+ ---
112
+
113
+ ### Customer
114
+
115
+ Manages clients (pessoas fisicas ou juridicas).
116
+
117
+ ```ruby
118
+ # Create customer (Pessoa Juridica)
119
+ customer = Conexa::Customer.create(
120
+ company_id: 3,
121
+ name: 'Empresa ABC Ltda',
122
+ trade_name: 'ABC',
123
+ field_of_activity: 'Tecnologia',
124
+ notes: 'Cliente VIP',
125
+ cell_number: '31999998888',
126
+ phones: ['3133334444', '3144445555'],
127
+ website: 'https://www.abc.com.br',
128
+ has_login_access: true,
129
+ login: 'empresa@abc.com.br',
130
+ password: 'SecurePass123',
131
+ legal_person: {
132
+ cnpj: '99.557.155/0001-90',
133
+ foundation_date: '2020-01-15',
134
+ state_inscription: '123456789',
135
+ municipal_inscription: '98765432'
136
+ },
137
+ address: {
138
+ zip_code: '30130000',
139
+ state: 'MG',
140
+ city: 'Belo Horizonte',
141
+ street: 'Av. Afonso Pena',
142
+ number: '1000',
143
+ neighborhood: 'Centro',
144
+ additional_details: 'Sala 501'
145
+ },
146
+ emails_message: ['contato@abc.com.br'],
147
+ emails_financial_messages: ['financeiro@abc.com.br'],
148
+ default_due_day: 10,
149
+ tax_deductions: {
150
+ iss: 0, ir: 0, pis: 0, inss: 0, csll: 0, cofins: 0
151
+ },
152
+ extra_fields: [
153
+ { id: 1, value: 'Valor 1' }
154
+ ]
155
+ )
156
+
157
+ # Create customer (Pessoa Fisica)
158
+ customer = Conexa::Customer.create(
159
+ company_id: 3,
160
+ name: 'Joao Silva',
161
+ natural_person: {
162
+ cpf: '123.456.789-00',
163
+ rg: '123456789',
164
+ birth_date: '1990-05-15',
165
+ issuing_authority: 'SSP/MG',
166
+ profession: 'Desenvolvedor',
167
+ marital_status: 'single' # single, married, divorced, widowed, not informed
168
+ }
169
+ )
170
+
171
+ # Create customer (Estrangeiro)
172
+ customer = Conexa::Customer.create(
173
+ company_id: 3,
174
+ name: 'John Smith',
175
+ foreign: {
176
+ document: 'ABC123456',
177
+ birth_date: '1985-03-20',
178
+ profession: 'Engineer'
179
+ }
180
+ )
181
+
182
+ # Find by ID
183
+ customer = Conexa::Customer.find(127)
184
+ customer.name # => "Empresa ABC Ltda"
185
+ customer.customer_id # => 127
186
+ customer.is_active # => true
187
+ customer.address # => Address object or nil
188
+
189
+ # List with filters
190
+ customers = Conexa::Customer.all(
191
+ company_id: [3],
192
+ is_active: true,
193
+ page: 1,
194
+ size: 50
195
+ )
196
+
197
+ # Update
198
+ customer.name = 'Novo Nome'
199
+ customer.save
200
+
201
+ # Delete
202
+ customer.destroy
203
+ # or
204
+ Conexa::Customer.destroy(127)
205
+
206
+ # Sub-resources
207
+ Conexa::Customer.persons(127) # List persons for customer
208
+ Conexa::Customer.contracts(127) # List contracts for customer
209
+ Conexa::Customer.charges(127) # List charges for customer
210
+ ```
211
+
212
+ **API endpoints:** `POST /customer`, `GET /customer/:id`, `PATCH /customer/:id`, `DELETE /customer/:id`, `GET /customers`
213
+
214
+ **Create/update attributes:**
215
+ - `company_id` - Unit/company ID (required on create)
216
+ - `name` - Full name (required on create)
217
+ - `trade_name` - Trade name (optional)
218
+ - `pronunciation` - Name pronunciation (optional)
219
+ - `field_of_activity` - Field of activity
220
+ - `notes` - General notes
221
+ - `cell_number` - Cell phone number
222
+ - `phones` - Array of phone numbers
223
+ - `website` - Website URL
224
+ - `has_login_access` - Has portal access (boolean)
225
+ - `login` - Login email (when has_login_access is true)
226
+ - `password` - Login password (when has_login_access is true)
227
+ - `legal_person` - PJ data: `cnpj`, `foundation_date`, `state_inscription`, `municipal_inscription`
228
+ - `natural_person` - PF data: `cpf`, `rg`, `birth_date`, `issuing_authority`, `profession`, `marital_status`
229
+ - `foreign` - Foreign data: `document`, `birth_date`, `profession`
230
+ - `address` - Address: `zip_code`, `state`, `city`, `street`, `number`, `neighborhood`, `additional_details`
231
+ - `mailing_address` - Mailing address (same structure as address)
232
+ - `emails_message` - Array of general emails
233
+ - `emails_financial_messages` - Array of financial emails
234
+ - `tags_id` - Array of tag IDs
235
+ - `default_due_day` - Default due day for charges
236
+ - `tax_deductions` - Tax deductions: `iss`, `ir`, `pis`, `inss`, `csll`, `cofins`
237
+ - `extra_fields` - Array of `{ id, value }` for custom fields
238
+ - `automatically_issue_nfse` - Auto-generate NFSe (boolean)
239
+ - `notes_nfse` - NFSe notes
240
+ - `extension_numbers` - Extension numbers
241
+
242
+ **Read-only attributes:**
243
+ - `customer_id` - ID
244
+ - `is_active` - Active status
245
+ - `is_blocked` - Blocked status
246
+ - `is_juridical_person` - Is PJ (legal person)
247
+ - `is_foreign` - Is foreigner
248
+ - `created_at` - Creation timestamp
249
+
250
+ ---
251
+
252
+ ### Contract
253
+
254
+ Manages recurring billing contracts.
255
+
256
+ ```ruby
257
+ # Create contract with plan
258
+ contract = Conexa::Contract.create(
259
+ customer_id: 127,
260
+ plan_id: 5,
261
+ start_date: '2024-01-01',
262
+ due_day: 10,
263
+ payment_frequency: 'monthly',
264
+ amount: 500.00,
265
+ seller_id: 1,
266
+ contract_summary: 'Contrato mensal',
267
+ notes: 'Observacoes',
268
+ membership_fee: 100.00,
269
+ generate_sales: true,
270
+ prorata_type: 'proportional',
271
+ nfse_description: 'Servicos de coworking',
272
+ refund: {
273
+ amount: 1000.00,
274
+ date_limit: '2025-01-01',
275
+ is_to_generate_refund_billet: false
276
+ },
277
+ complementary_services: [
278
+ { product_or_service_id: 100, quantity: 1, amount: 50.00, notes: 'Servico extra' }
279
+ ],
280
+ service_correspondence_quotas: {
281
+ limited: true,
282
+ messages_limit: 100,
283
+ price_additional_message: 2.50
284
+ }
285
+ )
286
+
287
+ # Create with custom products (no plan)
288
+ contract = Conexa::Contract.create_with_products(
289
+ customer_id: 127,
290
+ start_date: '2024-01-01',
291
+ payment_day: 10,
292
+ items: [
293
+ { product_id: 100, quantity: 1, amount: 99.90 },
294
+ { product_id: 101, quantity: 2, amount: 50.00 }
295
+ ]
296
+ )
297
+
298
+ # Find contract
299
+ contract = Conexa::Contract.find(456)
300
+ contract.status # => "active"
301
+ contract.active? # => true
302
+ contract.ended? # => false
303
+
304
+ # List contracts
305
+ contracts = Conexa::Contract.all(
306
+ customer_id: [127],
307
+ status: 'active'
308
+ )
309
+
310
+ # End/terminate contract
311
+ contract.end_contract(date: '2024-12-31', reason_id: 1, unlink_customer: false)
312
+ # or
313
+ Conexa::Contract.end_contract(456, date: '2024-12-31')
314
+
315
+ # Update
316
+ contract.due_day = 15
317
+ contract.save
318
+
319
+ # Delete
320
+ contract.destroy
321
+ ```
322
+
323
+ **API endpoints:** `POST /contract`, `GET /contract/:id`, `PATCH /contract/:id`, `DELETE /contract/:id`, `PATCH /contract/end/:id`, `GET /contracts`
324
+
325
+ **Create attributes:**
326
+ - `customer_id` - Customer ID (required)
327
+ - `plan_id` - Plan ID (optional, alternative to items)
328
+ - `payment_frequency` - monthly, bimonthly, quarterly, semester, yearly
329
+ - `start_date` - Start date
330
+ - `end_date` - End date (optional)
331
+ - `due_day` - Day of month for billing (1-28)
332
+ - `fidelity_date` - Fidelity end date
333
+ - `amount` - Contract value
334
+ - `discount_value` - Discount value
335
+ - `seller_id` - Seller user ID
336
+ - `contract_summary` - Summary text
337
+ - `notes` - Notes
338
+ - `membership_fee` - Membership fee
339
+ - `generate_sales` - Auto-generate sales (boolean)
340
+ - `prorata_type` - Prorata type
341
+ - `nfse_description` - NFSe description
342
+ - `discount_on_rooms` - Room discount percentage
343
+ - `discount_on_workstation` - Workstation discount percentage
344
+ - `private_space_id` - Private space ID
345
+ - `is_sms_enabled` - SMS notifications (boolean)
346
+ - `refund` - Refund config: `amount`, `date_limit`, `is_to_generate_refund_billet`
347
+ - `complementary_services` - Array of: `product_or_service_id`, `quantity`, `amount`, `notes`
348
+ - `service_correspondence_quotas` - Quotas: `limited`, `messages_limit`, `price_additional_message`
349
+
350
+ **End contract params:**
351
+ - `date` - End date
352
+ - `reason_id` - Reason ID
353
+ - `unlink_customer` - Unlink customer (boolean)
354
+
355
+ **Read-only attributes:**
356
+ - `contract_id` - ID
357
+ - `status` - active, ended, cancelled
358
+
359
+ **Helper methods:**
360
+ - `active?` - Check if active
361
+ - `ended?` - Check if ended or cancelled
362
+
363
+ ---
364
+
365
+ ### Charge
366
+
367
+ Manages invoices/boletos. Charges are created from sales.
368
+
369
+ ```ruby
370
+ # Create charge from sales
371
+ charge = Conexa::Charge.create(
372
+ sales_ids: [1234, 1235],
373
+ invoicing_method_id: 1,
374
+ due_date: '2024-02-10',
375
+ notes: 'Cobranca mensal'
376
+ )
377
+
378
+ # Find charge
379
+ charge = Conexa::Charge.find(789)
380
+ charge.status # => "pending"
381
+ charge.amount # => 199.90
382
+ charge.due_date # => "2024-02-10"
383
+ charge.paid? # => false
384
+ charge.pending? # => true
385
+ charge.overdue? # => false
386
+
387
+ # List charges
388
+ charges = Conexa::Charge.all(
389
+ customer_id: [127],
390
+ status: 'pending',
391
+ due_date_from: '2024-01-01',
392
+ due_date_to: '2024-01-31'
393
+ )
394
+
395
+ # Settle (mark as paid)
396
+ charge.settle(
397
+ settlement_date: '2024-02-05',
398
+ paid_amount: 199.90,
399
+ account_id: 1,
400
+ send_email: true,
401
+ receiving_method: { id: 1, installments_quantity: 1 }
402
+ )
403
+ # or
404
+ Conexa::Charge.settle(789, settlement_date: '2024-02-05')
405
+
406
+ # Get PIX QR Code
407
+ pix = charge.pix
408
+ # or
409
+ pix = Conexa::Charge.pix(789)
410
+
411
+ # Send email notification
412
+ charge.send_email
413
+ # or
414
+ Conexa::Charge.send_email(789)
415
+
416
+ # Cancel charge
417
+ charge.cancel
418
+ # or
419
+ Conexa::Charge.cancel(789)
420
+ ```
421
+
422
+ **API endpoints:** `POST /charge`, `GET /charge/:id`, `GET /charges`, `PATCH /charge/settle/:id`, `GET /charge/pix/:id`
423
+
424
+ **Create attributes:**
425
+ - `sales_ids` - Array of sale IDs to include in charge
426
+ - `invoicing_method_id` - Payment method ID
427
+ - `due_date` - Due date
428
+ - `notes` - Notes
429
+
430
+ **Settle attributes:**
431
+ - `settlement_date` - Payment date
432
+ - `paid_amount` - Amount paid
433
+ - `account_id` - Bank account ID
434
+ - `send_email` - Send confirmation email (boolean)
435
+ - `receiving_method` - Payment method: `id`, `installments_quantity`
436
+
437
+ **Read-only attributes:**
438
+ - `charge_id` - ID
439
+ - `customer_id` - Customer ID
440
+ - `status` - pending, paid, overdue, cancelled
441
+ - `amount` - Amount due
442
+ - `due_date` - Due date
443
+ - `paid_at` - Payment date
444
+
445
+ **Helper methods:**
446
+ - `paid?` - Check if paid
447
+ - `pending?` - Check if pending
448
+ - `overdue?` - Check if overdue
449
+
450
+ **Special methods:**
451
+ - `settle(params)` / `Charge.settle(id, params)` - Mark as paid
452
+ - `pix` / `Charge.pix(id)` - Get PIX payment data
453
+ - `send_email` / `Charge.send_email(id)` - Send notification
454
+ - `cancel` / `Charge.cancel(id)` - Cancel charge
455
+
456
+ ---
457
+
458
+ ### Sale
459
+
460
+ Manages one-time sales.
461
+
462
+ ```ruby
463
+ # Create sale
464
+ sale = Conexa::Sale.create(
465
+ customer_id: 450,
466
+ product_id: 2521,
467
+ requester_id: 10,
468
+ seller_id: 1,
469
+ quantity: 1,
470
+ amount: 80.99,
471
+ reference_date: '2024-01-15',
472
+ notes: 'Venda avulsa'
473
+ )
474
+
475
+ # Find sale
476
+ sale = Conexa::Sale.find(1234)
477
+ sale.status # => "notBilled"
478
+ sale.amount # => 80.99
479
+ sale.editable? # => true
480
+ sale.billed? # => false
481
+ sale.paid? # => false
482
+
483
+ # List sales
484
+ sales = Conexa::Sale.all(
485
+ customer_id: [450],
486
+ status: 'notBilled',
487
+ reference_date_from: '2024-01-01',
488
+ reference_date_to: '2024-01-31'
489
+ )
490
+
491
+ # Update (only if notBilled)
492
+ sale.amount = 90.00
493
+ sale.save
494
+
495
+ # Delete
496
+ sale.destroy
497
+ ```
498
+
499
+ **API endpoints:** `POST /sale`, `GET /sale/:id`, `PATCH /sale/:id`, `DELETE /sale/:id`, `GET /sales`
500
+
501
+ **Create/update attributes:**
502
+ - `customer_id` - Customer ID (required)
503
+ - `product_id` - Product ID (required)
504
+ - `requester_id` - Requester (person) ID
505
+ - `seller_id` - Seller (user) ID
506
+ - `quantity` - Quantity (required)
507
+ - `amount` - Amount (required)
508
+ - `reference_date` - Reference date
509
+ - `notes` - Notes
510
+
511
+ **Read-only attributes:**
512
+ - `sale_id` - ID
513
+ - `status` - Status string
514
+ - `original_amount` - Original amount before discount
515
+ - `discount_value` - Discount value
516
+ - `created_at` - Creation timestamp
517
+ - `updated_at` - Update timestamp
518
+
519
+ **Sale statuses:**
520
+ - `notBilled` - Not yet billed (editable)
521
+ - `billed` - Included in a charge
522
+ - `paid` - Paid
523
+ - `cancelled` - Cancelled
524
+ - `deductedFromQuota` - Deducted from quota
525
+ - `billedCancelled` - Billing cancelled
526
+ - `billedNegociated` - Negotiated
527
+ - `partiallyPaid` - Partially paid
528
+
529
+ **Helper methods:**
530
+ - `editable?` - Check if status is notBilled
531
+ - `billed?` - Check if billed
532
+ - `paid?` - Check if paid
533
+
534
+ ---
535
+
536
+ ### RecurringSale
537
+
538
+ Manages recurring items within contracts.
539
+
540
+ ```ruby
541
+ # Create recurring sale
542
+ rs = Conexa::RecurringSale.create(
543
+ customer_id: 127,
544
+ type: 'product', # 'package' or 'product'
545
+ reference_id: 100, # product or package ID
546
+ requester_id: 10,
547
+ seller_id: 1,
548
+ is_repeat: true,
549
+ occurrence_quantity: 12,
550
+ frequency: 'monthly',
551
+ start_date: '2024-01-01',
552
+ quantity: 1,
553
+ amount: 99.90,
554
+ notes: 'Venda recorrente'
555
+ )
556
+
557
+ # Find recurring sale
558
+ rs = Conexa::RecurringSale.find(555)
559
+
560
+ # List recurring sales
561
+ recurring = Conexa::RecurringSale.all(
562
+ contract_id: [456],
563
+ status: 'active'
564
+ )
565
+
566
+ # Update
567
+ rs.amount = 109.90
568
+ rs.save
569
+
570
+ # End recurring sale
571
+ rs.end_recurring_sale(date: '2024-12-31')
572
+ # or
573
+ Conexa::RecurringSale.end_recurring_sale(555, date: '2024-12-31')
574
+
575
+ # Delete
576
+ rs.destroy
577
+ ```
578
+
579
+ **API endpoints:** `POST /recurringSale`, `GET /recurringSale/:id`, `PATCH /recurringSale/:id`, `DELETE /recurringSale/:id`, `PATCH /recurringSale/end/:id`, `GET /recurringSales`
580
+
581
+ **Create attributes:**
582
+ - `customer_id` - Customer ID
583
+ - `type` - 'package' or 'product'
584
+ - `reference_id` - Product or package ID
585
+ - `requester_id` - Requester ID
586
+ - `seller_id` - Seller ID
587
+ - `is_repeat` - Is repeating (boolean)
588
+ - `occurrence_quantity` - Number of occurrences
589
+ - `frequency` - Frequency string
590
+ - `start_date` - Start date
591
+ - `last_adjustment_date` - Last price adjustment date
592
+ - `quantity` - Quantity
593
+ - `amount` - Amount
594
+ - `notes` - Notes
595
+ - `is_discount_previous_reservations` - Discount previous reservations (boolean)
596
+ - `is_calculate_pro_rata` - Calculate pro rata (boolean)
597
+
598
+ **Update attributes:**
599
+ - `requester_id`, `amount`, `quantity`, `last_adjustment_date`, `notes`
600
+
601
+ ---
602
+
603
+ ### Plan
604
+
605
+ Pricing plans with products and periodicities.
606
+
607
+ ```ruby
608
+ # Create plan
609
+ plan = Conexa::Plan.create(
610
+ company_id: 1,
611
+ name: 'Plano Basico',
612
+ service_category_id: 1,
613
+ cost_center_id: 10,
614
+ description: 'Plano com todos os recursos',
615
+ membership_fee: 200.00,
616
+ refund_value: 1500.00,
617
+ fidelity_months: 12,
618
+ due_day: 10,
619
+ nfse_description: 'Servicos de coworking',
620
+ receipt_description: 'Recibo mensal',
621
+ discount_on_rooms: 15.5,
622
+ discount_on_workstation: 10.0,
623
+ is_sms_enabled: true,
624
+ payment_periodicities: [
625
+ { periodicity: 'monthly', amount: 500.00 },
626
+ { periodicity: 'quarterly', amount: 1400.00 },
627
+ { periodicity: 'yearly', amount: 5000.00 }
628
+ ],
629
+ product_quotas: [
630
+ { product_id: 100, quantity: 10 },
631
+ { product_id: 101, quantity: 5 }
632
+ ],
633
+ service_correspondence_quotas: {
634
+ limited: true,
635
+ messages_limit: 100,
636
+ price_additional_message: 2.50
637
+ },
638
+ booking_models: [
639
+ { id: 1, stations: 2 }
640
+ ],
641
+ private_space_ids: [10, 11, 12],
642
+ hour_quotas: [
643
+ { hours: 10, periodicity: 'daily', space_id: 5 },
644
+ { hours: 40, periodicity: 'weekly', group_id: 3 }
645
+ ]
646
+ )
647
+
648
+ # Find plan
649
+ plan = Conexa::Plan.find(5)
650
+ plan.name # => "Plano Basico"
651
+
652
+ # List plans
653
+ plans = Conexa::Plan.all(company_id: [3])
654
+
655
+ # Delete
656
+ plan.destroy
657
+ ```
658
+
659
+ **API endpoints:** `POST /plan`, `GET /plan/:id`, `PATCH /plan/:id`, `DELETE /plan/:id`, `GET /plans`
660
+
661
+ **Note:** `save` (update) raises `NoMethodError` in the gem. Use `Conexa::Request` directly if needed.
662
+
663
+ **Create attributes:**
664
+ - `company_id` - Company ID (required)
665
+ - `name` - Plan name (required, must be unique)
666
+ - `service_category_id` - Service category ID (required)
667
+ - `cost_center_id` - Cost center ID
668
+ - `description` - Description
669
+ - `membership_fee` - Membership fee
670
+ - `refund_value` - Refund value
671
+ - `fidelity_months` - Fidelity period in months
672
+ - `due_day` - Default due day
673
+ - `nfse_description` - NFSe description
674
+ - `receipt_description` - Receipt description
675
+ - `discount_on_rooms` - Room discount percentage
676
+ - `discount_on_workstation` - Workstation discount percentage
677
+ - `is_sms_enabled` - SMS enabled (boolean)
678
+ - `payment_periodicities` - Array of: `periodicity` (monthly, bimonthly, quarterly, semester, yearly), `amount`
679
+ - `product_quotas` - Array of: `product_id`, `quantity`
680
+ - `service_correspondence_quotas` - `limited`, `messages_limit`, `price_additional_message`
681
+ - `booking_models` - Array of: `id`, `stations`
682
+ - `private_space_ids` - Array of space IDs
683
+ - `hour_quotas` - Array of: `hours`, `periodicity`, `group_id`, `space_id`
684
+
685
+ ---
686
+
687
+ ### Product
688
+
689
+ Read-only resource for billable products/services.
690
+
691
+ ```ruby
692
+ # Find product
693
+ product = Conexa::Product.find(100)
694
+ product.name # => "Mensalidade"
695
+
696
+ # List products
697
+ products = Conexa::Product.all(company_id: [3])
698
+ ```
699
+
700
+ **API endpoints:** `GET /product/:id`, `GET /products`
701
+
702
+ **Note:** Products cannot be created/updated via this gem. `save` raises `NoMethodError`.
703
+
704
+ ---
705
+
706
+ ### InvoicingMethod
707
+
708
+ Manages payment method configurations (boleto, PIX, credit card, etc.).
709
+
710
+ ```ruby
711
+ # Find invoicing method
712
+ method = Conexa::InvoicingMethod.find(1)
713
+ method.invoicing_method_id # => 1
714
+
715
+ # List invoicing methods
716
+ methods = Conexa::InvoicingMethod.all
717
+
718
+ # Create
719
+ method = Conexa::InvoicingMethod.create(
720
+ name: 'Boleto Bancario'
721
+ )
722
+
723
+ # Update
724
+ method.save
725
+
726
+ # Delete
727
+ method.destroy
728
+ ```
729
+
730
+ **API endpoints:** `GET /invoicingMethod/:id`, `GET /invoicingMethods`, `POST /invoicingMethod`, `PATCH /invoicingMethod/:id`, `DELETE /invoicingMethod/:id`
731
+
732
+ ---
733
+
734
+ ### Bill
735
+
736
+ Financial bills (contas a pagar).
737
+
738
+ ```ruby
739
+ # Create bill
740
+ bill = Conexa::Bill.create(
741
+ company_id: 1,
742
+ due_date: '2024-03-15',
743
+ amount: 500.00,
744
+ subcategory_id: 1,
745
+ supplier_id: 50,
746
+ description: 'Aluguel escritorio',
747
+ account_id: 1,
748
+ document_date: '2024-03-01',
749
+ competence_date: '2024-03-01',
750
+ document_number: 'NF-001',
751
+ digitable_line: '23793.38128 60000.000003 00000.000400 1 84340000050000',
752
+ cost_centers: [
753
+ { id: 1, percentage: 100 }
754
+ ],
755
+ cac: { is_included: true, percentage: 5.0 }
756
+ )
757
+
758
+ # Find bill
759
+ bill = Conexa::Bill.find(321)
760
+
761
+ # List bills
762
+ bills = Conexa::Bill.all(company_id: [3])
763
+ ```
764
+
765
+ **API endpoints:** `POST /bill`, `GET /bill/:id`, `GET /bills`
766
+
767
+ **Note:** `save` (update) raises `NoMethodError` in the gem.
768
+
769
+ **Create attributes:**
770
+ - `company_id` - Company ID
771
+ - `due_date` - Due date
772
+ - `amount` - Amount
773
+ - `subcategory_id` - Subcategory ID
774
+ - `supplier_id` - Supplier ID
775
+ - `description` - Description
776
+ - `account_id` - Bank account ID
777
+ - `document_date` - Document date
778
+ - `competence_date` - Competence date
779
+ - `document_number` - Document number
780
+ - `digitable_line` - Barcode digitable line
781
+ - `cost_centers` - Array of: `id`, `percentage`
782
+ - `cac` - CAC config: `is_included`, `percentage`
783
+
784
+ ---
785
+
786
+ ### Company
787
+
788
+ Manages units/companies within the account.
789
+
790
+ ```ruby
791
+ # Find company
792
+ company = Conexa::Company.find(3)
793
+
794
+ # List companies
795
+ companies = Conexa::Company.all
796
+ ```
797
+
798
+ **API endpoints:** `GET /company/:id`, `GET /companies`
799
+
800
+ ---
801
+
802
+ ### Supplier
803
+
804
+ Manages suppliers (fornecedores) with PF/PJ data.
805
+
806
+ ```ruby
807
+ # Create supplier (PJ)
808
+ supplier = Conexa::Supplier.create(
809
+ name: 'Fornecedor XYZ Ltda',
810
+ field_of_activity: 'Servicos',
811
+ notes: 'Fornecedor principal',
812
+ cell_number: '31999998888',
813
+ phones: ['3133334444'],
814
+ emails: ['contato@xyz.com.br'],
815
+ website: 'https://www.xyz.com.br',
816
+ contact_person_names: ['Joao', 'Maria'],
817
+ legal_person: {
818
+ legal_name: 'Fornecedor XYZ Servicos Ltda',
819
+ cnpj: '12.345.678/0001-90',
820
+ state_inscription: '123456789',
821
+ municipal_inscription: '98765432'
822
+ },
823
+ address: {
824
+ zip_code: '30130000',
825
+ state: 'MG',
826
+ city: 'Belo Horizonte',
827
+ street: 'Rua da Bahia',
828
+ number: '500',
829
+ neighborhood: 'Centro',
830
+ additional_details: 'Sala 10'
831
+ }
832
+ )
833
+
834
+ # Create supplier (PF)
835
+ supplier = Conexa::Supplier.create(
836
+ name: 'Joao Fornecedor',
837
+ natural_person: {
838
+ cpf: '123.456.789-00',
839
+ rg: '123456789',
840
+ issuing_authority: 'SSP/MG'
841
+ }
842
+ )
843
+
844
+ # Find supplier
845
+ supplier = Conexa::Supplier.find(50)
846
+
847
+ # List suppliers
848
+ suppliers = Conexa::Supplier.all(company_id: [3])
849
+
850
+ # Update
851
+ supplier.name = 'Novo Nome'
852
+ supplier.save
853
+
854
+ # Delete
855
+ supplier.destroy
856
+ ```
857
+
858
+ **API endpoints:** `POST /supplier`, `GET /supplier/:id`, `PATCH /supplier/:id`, `DELETE /supplier/:id`, `GET /supplier` (list)
859
+
860
+ **Create attributes:**
861
+ - `name` - Supplier name (required)
862
+ - `field_of_activity` - Field of activity
863
+ - `notes` - Notes
864
+ - `cell_number` - Cell phone
865
+ - `phones` - Array of phones
866
+ - `emails` - Array of emails
867
+ - `website` - Website URL
868
+ - `contact_person_names` - Array of contact names
869
+ - `legal_person` - PJ data: `legal_name`, `cnpj`, `state_inscription`, `municipal_inscription`
870
+ - `natural_person` - PF data: `cpf`, `rg`, `issuing_authority`
871
+ - `address` - Address: `zip_code`, `state`, `city`, `street`, `number`, `neighborhood`, `additional_details`
872
+
873
+ ---
874
+
875
+ ### CreditCard
876
+
877
+ Manages customer credit cards.
878
+
879
+ ```ruby
880
+ # Create credit card
881
+ card = Conexa::CreditCard.create(
882
+ customer_id: 127,
883
+ number: '4111111111111111',
884
+ name: 'JOAO DA SILVA',
885
+ expiration_date: '12/2026',
886
+ cvc: '123',
887
+ brand: 'visa',
888
+ default: true,
889
+ enable_recurring: true
890
+ )
891
+
892
+ # Find credit card
893
+ card = Conexa::CreditCard.find(99)
894
+
895
+ # Update
896
+ card.default = true
897
+ card.save
898
+
899
+ # Delete
900
+ card.destroy
901
+ ```
902
+
903
+ **API endpoints:** `POST /creditCard`, `GET /creditCard/:id`, `PATCH /creditCard/:id`, `DELETE /creditCard/:id`
904
+
905
+ **Note:** `all` (listing) is not available for credit cards.
906
+
907
+ **Create attributes:**
908
+ - `customer_id` - Customer ID (required)
909
+ - `number` - Card number
910
+ - `name` - Cardholder name
911
+ - `expiration_date` - Expiration date (MM/YYYY)
912
+ - `cvc` - CVC code
913
+ - `brand` - Card brand
914
+ - `default` - Set as default (boolean)
915
+ - `enable_recurring` - Enable for recurring charges (boolean)
916
+
917
+ ---
918
+
919
+ ### Person
920
+
921
+ Manages requesters (solicitantes) linked to a customer. The API supports full CRUD.
922
+
923
+ ```ruby
924
+ # Create person for customer
925
+ person = Conexa::Person.create(
926
+ customer_id: 127,
927
+ name: 'Maria Solicitante',
928
+ nationality: 'Brasileira',
929
+ place_of_birth: 'Belo Horizonte',
930
+ marital_status: 'single',
931
+ is_foreign: false,
932
+ is_company_partner: true,
933
+ is_guarantor: false,
934
+ cpf: '123.456.789-00',
935
+ rg: '123456789',
936
+ issuing_authority: 'SSP/MG',
937
+ birth_date: '1990-05-15',
938
+ cell_number: '31999998888',
939
+ phones: ['3133334444'],
940
+ emails: ['maria@empresa.com'],
941
+ sex: 'female',
942
+ job_title: 'Gerente',
943
+ profession: 'Administradora',
944
+ resume: 'Profissional experiente',
945
+ notes: 'Contato principal',
946
+ address: {
947
+ zip_code: '30130000',
948
+ state: 'MG',
949
+ city: 'Belo Horizonte',
950
+ street: 'Av. Afonso Pena',
951
+ number: '1000',
952
+ neighborhood: 'Centro',
953
+ additional_details: 'Sala 501'
954
+ },
955
+ devices: [
956
+ { nickname: 'Notebook', mac_address: 'AA:BB:CC:DD:EE:FF' }
957
+ ],
958
+ has_login_access: true,
959
+ login: 'maria@empresa.com',
960
+ password: 'SecurePass123',
961
+ permissions: ['finance', 'orders', 'rooms'],
962
+ can_receive_mail: true,
963
+ color: '#FF5733',
964
+ url_linkedin: 'https://linkedin.com/in/maria',
965
+ url_instagram: '@maria',
966
+ url_facebook: 'maria.fb',
967
+ url_twitter: '@maria'
968
+ )
969
+
970
+ # Update
971
+ person.name = 'Maria Silva'
972
+ person.save
973
+
974
+ # Delete
975
+ person.destroy
976
+ ```
977
+
978
+ **API endpoints:** `POST /person`, `GET /person/:id`, `PATCH /person/:id`, `DELETE /person/:id`, `GET /persons`
979
+
980
+ **Note:** In the gem, `find`, `all`, and `find_by` raise `NoMethodError`. Use `Conexa::Request` directly if you need to retrieve or list persons.
981
+
982
+ **Create/update attributes:**
983
+ - `customer_id` - Customer ID (required)
984
+ - `name` - Full name (required)
985
+ - `nationality` - Nationality
986
+ - `place_of_birth` - Place of birth
987
+ - `marital_status` - Marital status
988
+ - `is_foreign` - Is foreigner (boolean)
989
+ - `foreign_data` - Foreign data object
990
+ - `is_company_partner` - Is company partner (boolean)
991
+ - `is_guarantor` - Is guarantor (boolean)
992
+ - `cpf` - CPF number
993
+ - `rg` - RG number
994
+ - `issuing_authority` - RG issuing authority
995
+ - `birth_date` - Birth date
996
+ - `cell_number` - Cell phone
997
+ - `phones` - Array of phones
998
+ - `emails` - Array of emails
999
+ - `sex` - Gender (male, female)
1000
+ - `job_title` - Job title
1001
+ - `profession` - Profession
1002
+ - `resume` - Resume/bio
1003
+ - `notes` - Notes
1004
+ - `address` - Address object
1005
+ - `devices` - Array of: `nickname`, `mac_address`
1006
+ - `has_login_access` - Has portal access (boolean)
1007
+ - `login` - Login email
1008
+ - `password` - Login password
1009
+ - `permissions` - Array of: finance, orders, rooms, shared_spaces, assistance, correspondences, printing
1010
+ - `access_id` - Access ID
1011
+ - `can_receive_mail` - Can receive mail (boolean)
1012
+ - `color` - Color code
1013
+ - `print_fee_id` - Print fee ID
1014
+ - `extension_numbers` - Extension numbers
1015
+ - `url_linkedin`, `url_instagram`, `url_facebook`, `url_twitter` - Social URLs
1016
+
1017
+ ---
1018
+
1019
+ ## Common Patterns
1020
+
1021
+ ### Pagination
1022
+
1023
+ All `#all` and `#find_by` methods support pagination:
1024
+
1025
+ ```ruby
1026
+ # Page 1, 50 items per page
1027
+ result = Conexa::Customer.all(page: 1, size: 50)
1028
+
1029
+ result.data # => Array of customers
1030
+ result.pagination # => { "page" => 1, "size" => 50, "total" => 150 }
1031
+ result.empty? # => false
1032
+
1033
+ # Iterate all pages
1034
+ page = 1
1035
+ loop do
1036
+ result = Conexa::Customer.all(page: page, size: 100)
1037
+ break if result.empty?
1038
+
1039
+ result.each { |customer| process(customer) }
1040
+ page += 1
1041
+ end
1042
+ ```
1043
+
1044
+ ### Result Object
1045
+
1046
+ API calls return `Conexa::Result` objects:
1047
+
1048
+ ```ruby
1049
+ result = Conexa::Customer.all
1050
+ result.data # Array of objects
1051
+ result.pagination # Pagination info
1052
+ result.empty? # Check if no results
1053
+ result.each { } # Iterate (delegates to data)
1054
+ result.first # First item
1055
+ result.count # Number of items
1056
+ ```
1057
+
1058
+ ### Error Handling
1059
+
1060
+ ```ruby
1061
+ begin
1062
+ customer = Conexa::Customer.find(999999)
1063
+ rescue Conexa::NotFound => e
1064
+ puts "Customer not found: #{e.message}"
1065
+ rescue Conexa::ValidationError => e
1066
+ puts "Validation failed: #{e.errors}"
1067
+ rescue Conexa::ConnectionError => e
1068
+ puts "Connection failed: #{e.message}"
1069
+ rescue Conexa::ResponseError => e
1070
+ puts "API error: #{e.message}"
1071
+ end
1072
+ ```
1073
+
1074
+ **Error classes:**
1075
+ - `Conexa::NotFound` - Resource not found (404)
1076
+ - `Conexa::ValidationError` - Validation failed (response without `message` key)
1077
+ - `Conexa::ResponseError` - API error with `message` key (400, 401, 422, 500)
1078
+ - `Conexa::ConnectionError` - Network error
1079
+ - `Conexa::RequestError` - Invalid request parameters (e.g., nil ID)
1080
+ - `Conexa::MissingCredentialsError` - Token not configured
1081
+
1082
+ ### Filters
1083
+
1084
+ Common filter parameters across resources:
1085
+
1086
+ ```ruby
1087
+ # Date ranges
1088
+ Conexa::Charge.all(
1089
+ due_date_from: '2024-01-01',
1090
+ due_date_to: '2024-01-31'
1091
+ )
1092
+
1093
+ # Multiple IDs
1094
+ Conexa::Customer.all(company_id: [1, 2, 3])
1095
+
1096
+ # Status filters
1097
+ Conexa::Contract.all(status: 'active')
1098
+ Conexa::Sale.all(status: 'notBilled')
1099
+
1100
+ # Combined
1101
+ Conexa::Charge.all(
1102
+ customer_id: [127],
1103
+ status: 'pending',
1104
+ due_date_from: '2024-01-01',
1105
+ page: 1,
1106
+ size: 100
1107
+ )
1108
+ ```
1109
+
1110
+ ### Rate Limiting
1111
+
1112
+ The API enforces a limit of 100 requests per minute. Response headers:
1113
+ - `X-Rate-Limit-Limit` - Maximum requests per minute
1114
+ - `X-Rate-Limit-Remaining` - Remaining requests
1115
+ - `X-Rate-Limit-Reset` - Seconds until limit resets
1116
+
1117
+ ---
1118
+
1119
+ ## Model Methods Summary
1120
+
1121
+ | Resource | find | all | create | save | destroy | Special Methods |
1122
+ |----------|------|-----|--------|------|---------|-----------------|
1123
+ | Auth | - | - | - | - | - | login, authenticate |
1124
+ | Customer | yes | yes | yes | yes | yes | persons, contracts, charges |
1125
+ | Contract | yes | yes | yes | yes | yes | end_contract, create_with_products |
1126
+ | Charge | yes | yes | yes | - | - | settle, pix, cancel, send_email |
1127
+ | Sale | yes | yes | yes | yes | yes | billed?, paid?, editable? |
1128
+ | RecurringSale | yes | yes | yes | yes | yes | end_recurring_sale |
1129
+ | Plan | yes | yes | yes | - | yes | - |
1130
+ | Product | yes | yes | - | - | - | - |
1131
+ | InvoicingMethod | yes | yes | yes | yes | yes | - |
1132
+ | Bill | yes | yes | yes | - | - | - |
1133
+ | Company | yes | yes | yes | yes | yes | - |
1134
+ | Supplier | yes | yes | yes | yes | yes | - |
1135
+ | CreditCard | yes | - | yes | yes | yes | - |
1136
+ | Person | - | - | yes | yes | yes | - |
1137
+
1138
+ **Legend:** `yes` = available, `-` = not available (raises NoMethodError or not implemented)
1139
+
1140
+ ---
1141
+
1142
+ ## Version History
1143
+
1144
+ - **v0.0.7** - Add Auth resource, fix Auth.login, add test suite with VCR
1145
+ - **v0.0.6** - Fix nil guard in camelize_hash, fix Result#empty? delegation
1146
+ - **v0.0.5** - Initial public release
1147
+
1148
+ ## Links
1149
+
1150
+ - **RubyGems:** https://rubygems.org/gems/conexa
1151
+ - **GitHub:** https://github.com/guilhermegazzinelli/conexa-ruby
1152
+ - **Conexa API Docs:** https://documenter.getpostman.com/view/25182821/2s93RZMpcB