bfocus 0.2.0 → 0.2.2
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 +4 -4
- data/README.md +125 -5
- data/lib/bfocus/errors.rb +8 -1
- data/lib/bfocus/resources/people.rb +44 -8
- data/lib/bfocus/transport.rb +6 -0
- data/lib/bfocus/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cde8597ee9adbdde78b052fc3527eb19ed6a5045f73829424d835f696dcd95c9
|
|
4
|
+
data.tar.gz: 867ddf4b2644cbe30252ac5ca6d482e6a4a5216049a4469e6f5f2d5ed4f5757b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a5abc2452240b5f164b164e72bb4e2d895cd70e4ff6479f64d7ba39fa34ecbf0af48e32d75c3c5fe97642f566edf0007e23ab3ec0c8ea921140bbc62614ecda
|
|
7
|
+
data.tar.gz: 91a342991a7a3859e6664b94bd73282fe1d3892226de023372243325bb3a2f738c580575126e0fda360298b310b8b36eed00118165540331f24c9591450add7f
|
data/README.md
CHANGED
|
@@ -154,6 +154,22 @@ client.people.identifiers.add("app-77", "crm-p5")
|
|
|
154
154
|
client.people.identifiers.remove("app-77", "crm-p5")
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
### Ler os identificadores da pessoa (para reconciliar)
|
|
158
|
+
|
|
159
|
+
`client.people.list(...)` mostra só o identificador **principal** de cada pessoa. Quando dois
|
|
160
|
+
cadastros seus eram a mesma pessoa, um dos ids virou **extra** — e some da listagem sem ter sumido
|
|
161
|
+
do cadastro. É isso que faz a sua conferência fechar "633 de 636" sem explicar os 3.
|
|
162
|
+
|
|
163
|
+
`people.identifiers.list` é a fonte de verdade dessa conferência, e é **leitura**: antes dela era
|
|
164
|
+
preciso ESCREVER (tentar um `add`) para descobrir o que tinha acontecido. Aceita no caminho o id
|
|
165
|
+
principal **ou qualquer um dos extras**.
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
ids = client.people.identifiers.list("crm-p5") # o id extra que "sumiu" da listagem
|
|
169
|
+
ids["external_id"] # => "app-77" — o principal do cadastro
|
|
170
|
+
ids["identifiers"].each { |i| puts "#{i['external_id']} #{i['label']} #{i['source']}" }
|
|
171
|
+
```
|
|
172
|
+
|
|
157
173
|
## Pessoas
|
|
158
174
|
|
|
159
175
|
As pessoas (usuários do seu sistema) de cada cliente, em `client.people`. O `external_id` da pessoa
|
|
@@ -174,10 +190,111 @@ client.people.upsert("erp-1042", "app-77", access: true) # devolve o acesso
|
|
|
174
190
|
|
|
175
191
|
- **Nunca duplica.** O e-mail (ou o telefone) acha a pessoa que já chegou por e-mail, pelo widget
|
|
176
192
|
ou por outro sistema, e ela é **adotada** (ganha o seu `external_id`).
|
|
177
|
-
- A mesma pessoa enviada com **outro cliente** é
|
|
193
|
+
- A mesma pessoa enviada com **outro cliente** NÃO é transferida: fica **ligada** também a ele
|
|
194
|
+
(`"linked" => true` na resposta). O cadastro é único e a mesma pessoa circula por vários clientes.
|
|
195
|
+
- **O acesso é do vínculo.** `delete` (e `access: false`) tira o acesso dela NESTE cliente, não nos
|
|
196
|
+
outros: `"unlinked" => true` na resposta quer dizer que ela segue ativa em algum outro.
|
|
178
197
|
- Como no resto da SDK, só o que você passa muda; `nil` limpa (`phone: nil`).
|
|
179
198
|
- Campos: `name`, `email`, `phone`, `role`, `access` (pode usar o atendimento), `is_primary`
|
|
180
|
-
(contato principal), `extra_emails`, `extra_phones`.
|
|
199
|
+
(contato principal), `extra_emails`, `extra_phones`, `custom_fields`, `clear`.
|
|
200
|
+
- Erros comuns (`code`): `CUSTOMER_NOT_FOUND`, `NAME_REQUIRED` (ao criar), `PERSON_EMAIL_TAKEN`,
|
|
201
|
+
`PERSON_PHONE_TAKEN`, `PERSON_CONTACT_OTHER_CUSTOMER`, `PERSON_EMAIL_STAFF`,
|
|
202
|
+
`PERSON_CLEAR_FIELD_INVALID`, `PERSON_CLEAR_NOT_OWN_RECORD`.
|
|
203
|
+
|
|
204
|
+
### Campos personalizados da pessoa
|
|
205
|
+
|
|
206
|
+
`custom_fields` leva o que só existe no seu sistema (matrícula, centro de custo, filial). É a
|
|
207
|
+
**exceção** ao "só o que vier muda": a lista enviada **substitui a lista inteira** — campo que
|
|
208
|
+
ficar de fora é **removido**. Mande sempre a lista que o seu sistema tem hoje; omitir o argumento não mexe
|
|
209
|
+
em nada, como em qualquer outro campo.
|
|
210
|
+
|
|
211
|
+
A `visibility` é decidida no bFocus e **preservada entre sincronizações** — por isso ela não vai
|
|
212
|
+
no envio, só volta na resposta: o seu ERP não rebaixa nem promove a exposição de um dado sem
|
|
213
|
+
querer.
|
|
214
|
+
|
|
215
|
+
Vale no upsert de pessoa, no lote de pessoas e na listagem de pessoas do cliente.
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
pessoa = client.people.upsert(
|
|
219
|
+
"erp-1042", "app-77",
|
|
220
|
+
custom_fields: [ # a lista INTEIRA do seu sistema
|
|
221
|
+
{ key: "matricula", label: "Matrícula", value: "4471" },
|
|
222
|
+
{ key: "filial", label: "Filial", value: "Centro" }
|
|
223
|
+
]
|
|
224
|
+
)
|
|
225
|
+
pessoa["custom_fields"].each do |campo|
|
|
226
|
+
puts "#{campo['key']} #{campo['value']} #{campo['visibility']}" # visibility vem do bFocus
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Apagar o e-mail ou o telefone da pessoa
|
|
231
|
+
|
|
232
|
+
Um contato gravado errado ficava preso para sempre: enquanto a ficha errada segurasse o telefone,
|
|
233
|
+
nenhum reenvio o soltava. `clear` apaga.
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
client.people.upsert("erp-1042", "app-77", clear: ["phone"]) # some o telefone
|
|
237
|
+
client.people.upsert("erp-1042", "app-77", clear: %w[email phone]) # somem os dois
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Três regras que parecem contraintuitivas e são de propósito:
|
|
241
|
+
|
|
242
|
+
- **Apagar é explícito.** `phone: nil`, `clear: []` e não passar o argumento continuam significando
|
|
243
|
+
**"não mexe"** — a SDK não traduz `nil` em `clear`. Fazer o `nil` apagar teria apagado, em
|
|
244
|
+
silêncio e na primeira carga seguinte, o dado de todo sistema que manda `nil` para "não tenho
|
|
245
|
+
esse valor".
|
|
246
|
+
- **Campo fora da lista é recusado, não ignorado**: hoje só `"email"` e `"phone"`; qualquer outro
|
|
247
|
+
devolve 422 `PERSON_CLEAR_FIELD_INVALID` (`Bfocus::ValidationError`).
|
|
248
|
+
- **Só se limpa a própria ficha.** Se você alcançou a pessoa por um identificador **extra**, a API
|
|
249
|
+
recusa com 409 `PERSON_CLEAR_NOT_OWN_RECORD` (`Bfocus::ConflictError`): apagar o contato de uma
|
|
250
|
+
ficha alcançada por apelido seria apagar dado de outro sistema. Para saber se o id que você tem em
|
|
251
|
+
mãos é o principal ou um extra, use `client.people.identifiers.list(...)`.
|
|
252
|
+
|
|
253
|
+
Vale no `people.upsert` e no `people.batch` (`"clear" => ["phone"]` no item).
|
|
254
|
+
|
|
255
|
+
### Contato já usado: um 409 que você consegue resolver
|
|
256
|
+
|
|
257
|
+
`PERSON_EMAIL_TAKEN` e `PERSON_PHONE_TAKEN` (409) não são "tente de novo": o e-mail (ou o
|
|
258
|
+
telefone) já é de outra pessoa da conta. O erro diz **de quem**, em `error.data` (a API repete o mesmo
|
|
259
|
+
detalhe em `error.validation`, por compatibilidade):
|
|
260
|
+
|
|
261
|
+
| campo | o que é |
|
|
262
|
+
| --- | --- |
|
|
263
|
+
| `field` | `email` ou `phone` — qual contato está tomado |
|
|
264
|
+
| `owner_external_id` | o identificador da pessoa que já usa esse contato |
|
|
265
|
+
| `owner_name` | o nome dela |
|
|
266
|
+
| `owner_customer_external_id` | o cliente a que ela pertence |
|
|
267
|
+
|
|
268
|
+
**É o `owner_customer_external_id` que decide a ação**, e os dois casos pedem coisas opostas:
|
|
269
|
+
|
|
270
|
+
- **mesmo cliente que você enviou** → é quase sempre a MESMA pessoa em dois sistemas. Uma pessoa
|
|
271
|
+
tem **N identificadores**: registre o seu como **extra** dela. A partir daí o seu id encontra
|
|
272
|
+
essa pessoa.
|
|
273
|
+
- **outro cliente** → ninguém decide sozinho a quem a pessoa pertence. Não force: registre o caso
|
|
274
|
+
e leve para quem conhece o cadastro. Unificar dois clientes é decisão de gente, não de um
|
|
275
|
+
casamento por e-mail.
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
begin
|
|
279
|
+
client.people.upsert("erp-1042", "app-77", name: "Paula Reis", email: "paula@padaria.example")
|
|
280
|
+
rescue Bfocus::ConflictError => e
|
|
281
|
+
raise unless %w[PERSON_EMAIL_TAKEN PERSON_PHONE_TAKEN].include?(e.code)
|
|
282
|
+
|
|
283
|
+
dono = e.data
|
|
284
|
+
if dono["owner_customer_external_id"] == "erp-1042"
|
|
285
|
+
# A mesma pessoa, com dois ids: o seu vira mais um identificador dela.
|
|
286
|
+
client.people.identifiers.add(dono["owner_external_id"], "app-77", label: "ERP")
|
|
287
|
+
else
|
|
288
|
+
# Dono em OUTRO cliente: não decida sozinho — registre e leve para o cadastro.
|
|
289
|
+
avisar_cadastro(e.code, dono)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`PERSON_CONTACT_OTHER_CUSTOMER` (409) é o mesmo assunto pelo outro lado, e é **recusa
|
|
295
|
+
definitiva**: a API não move mais uma pessoa de um cliente para outro só porque o e-mail (ou o
|
|
296
|
+
telefone) casou. Repetir a chamada não resolve — trate como caso para o cadastro, nunca como
|
|
297
|
+
falha temporária.
|
|
181
298
|
|
|
182
299
|
## Lotes — `customers.batch` e `people.batch`
|
|
183
300
|
|
|
@@ -410,10 +527,10 @@ Todos são `Hash` com chaves string (campos novos podem aparecer a qualquer mome
|
|
|
410
527
|
| Contato (`customers.contacts.*`) | `id`, `external_id`, `name`, `role`, `email`, `phone`, `notes`, `is_primary`, `created_at`, `updated_at` |
|
|
411
528
|
| Produto vinculado (`customers.products.*`) | `id`, `slug`, `name`, `is_active` |
|
|
412
529
|
| Interação (`customers.interactions.*`) | `id`, `content`, `is_internal`, `author_kind`, `author_name`, `created_at` |
|
|
413
|
-
| Pessoa (`people.list`, `delete`) | `external_id` (pode ser `nil`), `name`, `email`, `phone`, `role`, `access`, `is_primary`, `customer_external_id` |
|
|
530
|
+
| Pessoa (`people.list`, `delete`) | `external_id` (pode ser `nil`), `name`, `email`, `phone`, `role`, `access`, `is_primary`, `customer_external_id`, `custom_fields` (lista de `{key, label, value, visibility}`) |
|
|
414
531
|
| Pessoa gravada (`people.upsert`) | a pessoa + `status` (`created`/`updated`/`unchanged`) |
|
|
415
532
|
| Cliente com identificadores (`customers.identifiers.add`, `remove`) | o cliente + `identifiers` (lista de `{external_id, label, source}`) |
|
|
416
|
-
| Identificadores da pessoa (`people.identifiers.add`, `remove`) | `external_id`, `identifiers` (lista de `{external_id, label, source}`) |
|
|
533
|
+
| Identificadores da pessoa (`people.identifiers.list`, `add`, `remove`) | `external_id`, `identifiers` (lista de `{external_id, label, source}`) |
|
|
417
534
|
| Lote (`customers.batch`, `people.batch`) | `results` (lista de `{index, status, external_id, merged_into, error, code}`; `status` ∈ `created`/`updated`/`unchanged`/`error`), `summary` (`{created, updated, unchanged, error}`) |
|
|
418
535
|
| Produto (`products.*`) | `id`, `slug`, `name`, `description`, `color`, `icon`, `is_active`, `sort_order`, `current_version`, `ai_level`, `created_at`, `updated_at` |
|
|
419
536
|
| Release note (`release_notes.*`) | `id`, `product`, `version`, `title`, `description_html`, `audience`, `is_published`, `require_ack_internal`, `require_ack_external`, `published_at`, `created_at`, `updated_at` |
|
|
@@ -440,7 +557,10 @@ Qualquer resposta fora de 2xx levanta `Bfocus::Error` (ou uma subclasse):
|
|
|
440
557
|
| `Bfocus::ServerError` | 5xx |
|
|
441
558
|
| `Bfocus::NetworkError` | conexão/timeout — `status == 0`, `code == "NETWORK_ERROR"` |
|
|
442
559
|
|
|
443
|
-
Todas têm `code`, `status`, `request_id`, `validation`, `retry_after`, `required_scope` e
|
|
560
|
+
Todas têm `code`, `status`, `request_id`, `validation`, `data`, `retry_after`, `required_scope` e
|
|
561
|
+
`body`. O `data` é o `data` do corpo: o detalhe estruturado que alguns erros trazem (`{}` quando
|
|
562
|
+
não há) — é por ele que um 409 de contato tomado diz de **quem** é o contato (veja
|
|
563
|
+
[Pessoas](#pessoas)).
|
|
444
564
|
**Decida pelo `code`** — ele é estável (`CUSTOMER_NOT_FOUND`, `INTEGRATION_SCOPE_MISSING`,
|
|
445
565
|
`MODULE_NOT_CONTRACTED`, `VALIDATION_ERROR`…). O `message` é texto para humanos e pode mudar. Ao
|
|
446
566
|
falar com o suporte, informe o `request_id`: ele vem do corpo da resposta, senão do header
|
data/lib/bfocus/errors.rb
CHANGED
|
@@ -21,6 +21,12 @@ module Bfocus
|
|
|
21
21
|
attr_reader :request_id
|
|
22
22
|
# @return [Hash{String=>String}] motivos por campo (erros de validação); `{}` quando não há.
|
|
23
23
|
attr_reader :validation
|
|
24
|
+
# @return [Hash] o `data` do corpo do erro: o detalhe estruturado que alguns erros trazem
|
|
25
|
+
# (`{}` quando não há). É onde vem, por exemplo, de quem é o contato já usado num 409
|
|
26
|
+
# `PERSON_EMAIL_TAKEN`/`PERSON_PHONE_TAKEN` (`field`, `owner_external_id`, `owner_name`,
|
|
27
|
+
# `owner_customer_external_id`) e o `owner` de um `IDENTIFIER_IN_USE`. A API repete esse
|
|
28
|
+
# detalhe em {#validation}, por compatibilidade com as SDKs que ainda não expunham `data`.
|
|
29
|
+
attr_reader :data
|
|
24
30
|
# @return [Integer, Float, nil] segundos do header `Retry-After` (só em 429).
|
|
25
31
|
attr_reader :retry_after
|
|
26
32
|
# @return [String, nil] escopo que faltou na chave (header `X-Required-Scope`, só em 403).
|
|
@@ -29,11 +35,12 @@ module Bfocus
|
|
|
29
35
|
attr_reader :body
|
|
30
36
|
|
|
31
37
|
def initialize(message = nil, code: nil, status: 0, request_id: nil, validation: nil,
|
|
32
|
-
retry_after: nil, required_scope: nil, body: nil)
|
|
38
|
+
retry_after: nil, required_scope: nil, body: nil, data: nil)
|
|
33
39
|
@code = code
|
|
34
40
|
@status = status
|
|
35
41
|
@request_id = request_id
|
|
36
42
|
@validation = validation.is_a?(Hash) ? validation.dup : {}
|
|
43
|
+
@data = data.is_a?(Hash) ? data.dup : {}
|
|
37
44
|
@retry_after = retry_after
|
|
38
45
|
@required_scope = required_scope
|
|
39
46
|
@body = body
|
|
@@ -8,6 +8,21 @@ module Bfocus
|
|
|
8
8
|
# Retorno: `{"external_id", "identifiers" => [{"external_id", "label", "source"}, …]}`.
|
|
9
9
|
# Id que já pertence a outro cadastro: `ConflictError` com `code == "IDENTIFIER_IN_USE"`.
|
|
10
10
|
class PersonIdentifiers < Base
|
|
11
|
+
# Todos os identificadores da pessoa: o principal (`"external_id"` do retorno) e os extras.
|
|
12
|
+
# `GET /people/{person_external_id}/identifiers` (escopo `customers:read`). Aceita no
|
|
13
|
+
# caminho o principal OU qualquer um dos extras.
|
|
14
|
+
#
|
|
15
|
+
# É a fonte de verdade para RECONCILIAR: {People#list} mostra só o identificador principal,
|
|
16
|
+
# então um id que virou extra some de lá sem ter sumido do cadastro — e, sem esta leitura,
|
|
17
|
+
# era preciso ESCREVER (tentar um {#add}) para descobrir o que tinha acontecido.
|
|
18
|
+
#
|
|
19
|
+
# @return [Hash]
|
|
20
|
+
# @raise [Bfocus::NotFoundError] `PERSON_NOT_FOUND`.
|
|
21
|
+
def list(person_external_id, timeout: nil)
|
|
22
|
+
call("GET", "/people/#{segment(person_external_id, 'person_external_id')}/identifiers",
|
|
23
|
+
timeout: timeout)
|
|
24
|
+
end
|
|
25
|
+
|
|
11
26
|
# Liga `extra_id` à pessoa (idempotente). `PUT /people/{person_external_id}/identifiers/{extra_id}`
|
|
12
27
|
#
|
|
13
28
|
# @param label [String, nil] rótulo livre. Não informado = sem corpo.
|
|
@@ -35,7 +50,8 @@ module Bfocus
|
|
|
35
50
|
#
|
|
36
51
|
# Pessoa: `"external_id"` (pode ser `nil` para quem chegou por e-mail/widget sem id),
|
|
37
52
|
# `"name"`, `"email"`, `"phone"`, `"role"`, `"access"`, `"is_primary"`,
|
|
38
|
-
# `"customer_external_id"
|
|
53
|
+
# `"customer_external_id"` e `"custom_fields"` (lista de `{"key", "label", "value",
|
|
54
|
+
# "visibility"}`). O `upsert` devolve também `"status"`
|
|
39
55
|
# (`"created"`/`"updated"`/`"unchanged"`).
|
|
40
56
|
#
|
|
41
57
|
# O `external_id` da pessoa é o mesmo `user_external_id` assinado no widget — por isso não
|
|
@@ -53,22 +69,38 @@ module Bfocus
|
|
|
53
69
|
# `PUT /customers/{customer_external_id}/people/{person_external_id}`
|
|
54
70
|
#
|
|
55
71
|
# Só o que vier muda; `nil` limpa. O e-mail (ou telefone) acha a pessoa que já chegou por
|
|
56
|
-
# outro caminho e ela é adotada, nunca duplicada
|
|
57
|
-
# transferida
|
|
72
|
+
# outro caminho e ela é adotada, nunca duplicada. Se ela já existia em OUTRO cliente, NÃO é
|
|
73
|
+
# transferida: fica ligada também a este (cadastro único, `"linked" => true` na resposta).
|
|
74
|
+
# `access: true` devolve o acesso retirado por {#delete}.
|
|
58
75
|
#
|
|
59
76
|
# @param access [Boolean] pode abrir chamados/usar o widget.
|
|
60
77
|
# @param is_primary [Boolean] contato principal do cliente.
|
|
61
78
|
# @param extra_emails [Array<String>] e-mails adicionais.
|
|
62
79
|
# @param extra_phones [Array<String>] telefones adicionais.
|
|
63
|
-
# @
|
|
80
|
+
# @param custom_fields [Array<Hash>] campos personalizados (`{"key", "label", "value"}`).
|
|
81
|
+
# Ao contrário de `extra_emails`/`extra_phones`, a lista SUBSTITUI a lista inteira: mande
|
|
82
|
+
# o que o seu sistema tem hoje, porque campo que ficar de fora é REMOVIDO. Não passar o
|
|
83
|
+
# argumento não mexe em nada. A visibilidade é decidida no bFocus e preservada entre
|
|
84
|
+
# sincronizações.
|
|
85
|
+
# @param clear [Array<String>] campos a APAGAR nesta pessoa: `["email"]`, `["phone"]` ou os
|
|
86
|
+
# dois. Apagar é EXPLÍCITO: `phone: nil`, `clear: []` e não passar o argumento continuam
|
|
87
|
+
# significando "não mexe" — a SDK não traduz `nil` em `clear`. Campo fora da lista aceita
|
|
88
|
+
# é RECUSADO pela API (422 `PERSON_CLEAR_FIELD_INVALID`), não ignorado; e só se limpa a
|
|
89
|
+
# PRÓPRIA ficha: alcançando a pessoa por um identificador EXTRA, a API recusa (409
|
|
90
|
+
# `PERSON_CLEAR_NOT_OWN_RECORD`) — apagar contato de ficha alcançada por apelido seria
|
|
91
|
+
# apagar dado de outro sistema.
|
|
92
|
+
# @return [Hash] a pessoa + `"status"`, `"linked"` (já existia em outro cliente e agora está
|
|
93
|
+
# ligada a este também) e `"merged_into"` (o id que você mandou era um apelido; este é o
|
|
94
|
+
# principal do cadastro).
|
|
64
95
|
def upsert(customer_external_id, person_external_id, name: UNSET, email: UNSET, phone: UNSET,
|
|
65
96
|
role: UNSET, access: UNSET, is_primary: UNSET, extra_emails: UNSET, extra_phones: UNSET,
|
|
66
|
-
idempotency_key: nil, timeout: nil)
|
|
97
|
+
custom_fields: UNSET, clear: UNSET, idempotency_key: nil, timeout: nil)
|
|
67
98
|
cid = segment(customer_external_id, "customer_external_id")
|
|
68
99
|
pid = segment(person_external_id, "person_external_id")
|
|
69
100
|
person = compact(
|
|
70
101
|
"name" => name, "email" => email, "phone" => phone, "role" => role, "access" => access,
|
|
71
|
-
"is_primary" => is_primary, "extra_emails" => extra_emails, "extra_phones" => extra_phones
|
|
102
|
+
"is_primary" => is_primary, "extra_emails" => extra_emails, "extra_phones" => extra_phones,
|
|
103
|
+
"custom_fields" => custom_fields, "clear" => clear
|
|
72
104
|
)
|
|
73
105
|
call("PUT", "/customers/#{cid}/people/#{pid}",
|
|
74
106
|
body: { "person" => person }, idempotency_key: idempotency_key, timeout: timeout)
|
|
@@ -81,9 +113,13 @@ module Bfocus
|
|
|
81
113
|
timeout: timeout)
|
|
82
114
|
end
|
|
83
115
|
|
|
84
|
-
# Retira o acesso da pessoa (ela continua no histórico).
|
|
116
|
+
# Retira o acesso da pessoa NESTE cliente (ela continua no histórico).
|
|
85
117
|
# `DELETE /customers/{customer_external_id}/people/{person_external_id}`
|
|
86
|
-
#
|
|
118
|
+
#
|
|
119
|
+
# O acesso é DO VÍNCULO: a mesma pessoa circula por vários clientes e tirar o acesso aqui
|
|
120
|
+
# não tira o dela nos outros.
|
|
121
|
+
# @return [Hash] a pessoa, com `"access" => false` e `"unlinked"` (`true` = ela segue ativa
|
|
122
|
+
# em outros clientes; `false` = era só deste e foi desligada).
|
|
87
123
|
def delete(customer_external_id, person_external_id, idempotency_key: nil, timeout: nil)
|
|
88
124
|
cid = segment(customer_external_id, "customer_external_id")
|
|
89
125
|
pid = segment(person_external_id, "person_external_id")
|
data/lib/bfocus/transport.rb
CHANGED
|
@@ -211,6 +211,7 @@ module Bfocus
|
|
|
211
211
|
human = nil
|
|
212
212
|
request_id = nil
|
|
213
213
|
validation = {}
|
|
214
|
+
data = {}
|
|
214
215
|
|
|
215
216
|
if payload.is_a?(Hash)
|
|
216
217
|
err = payload["error"]
|
|
@@ -222,6 +223,10 @@ module Bfocus
|
|
|
222
223
|
end
|
|
223
224
|
human = msg if msg.is_a?(String) && !msg.empty? && msg != code
|
|
224
225
|
validation = payload["validation"] if payload["validation"].is_a?(Hash)
|
|
226
|
+
# `data` é o detalhe estruturado do erro (de quem é o contato já usado, o dono de um
|
|
227
|
+
# identificador…). A API também o repete em `validation`, mas quem lê o erro precisa
|
|
228
|
+
# alcançá-lo sem depender dessa duplicação.
|
|
229
|
+
data = payload["data"] if payload["data"].is_a?(Hash)
|
|
225
230
|
rid = payload["request_id"]
|
|
226
231
|
request_id = rid if rid.is_a?(String) && !rid.empty?
|
|
227
232
|
elsif !text.strip.empty?
|
|
@@ -247,6 +252,7 @@ module Bfocus
|
|
|
247
252
|
status: status,
|
|
248
253
|
request_id: request_id,
|
|
249
254
|
validation: validation,
|
|
255
|
+
data: data,
|
|
250
256
|
retry_after: retry_after,
|
|
251
257
|
required_scope: required_scope,
|
|
252
258
|
body: payload.nil? ? present(text) : payload
|
data/lib/bfocus/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bfocus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Berni Software
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Clientes, produtos, release notes, base de conhecimento e agentes de
|
|
14
14
|
IA do bFocus. Zero dependências de runtime, novas tentativas e idempotência automáticas.
|