ajuste no tabela de preços
Some checks failed
Some checks failed
This commit is contained in:
parent
2f78f273da
commit
968ae6a314
@ -1180,7 +1180,7 @@ DEPENDENCIES
|
|||||||
working_hours
|
working_hours
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.4.4p34
|
ruby 3.4.4p34
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.11
|
2.5.11
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
class Api::V1::Accounts::Captain::PricingsController < Api::V1::Accounts::BaseController
|
||||||
|
before_action :fetch_pricings, only: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: @pricings
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_pricings
|
||||||
|
@pricings = current_account.captain_pricings
|
||||||
|
|
||||||
|
return unless params[:query].present?
|
||||||
|
|
||||||
|
# Fuzzy search using ILIKE for case-insensitive matching
|
||||||
|
# We wrap the query in % for wildcard matching on both sides
|
||||||
|
@pricings = @pricings.where('suite_category ILIKE ?', "%#{params[:query]}%")
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1319,6 +1319,56 @@
|
|||||||
"TITLE": "No Connected Inboxes",
|
"TITLE": "No Connected Inboxes",
|
||||||
"SUBTITLE": "Connecting an inbox allows the assistant to handle initial questions from your customers before transferring them to you."
|
"SUBTITLE": "Connecting an inbox allows the assistant to handle initial questions from your customers before transferring them to you."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"PRICINGS": {
|
||||||
|
"HEADER": "Administrative Panel",
|
||||||
|
"TITLE": "Pricing Table",
|
||||||
|
"DESCRIPTION": "Configure pricing rules by inbox, brand, day, and category.",
|
||||||
|
"ADD_BUTTON": "New Rule",
|
||||||
|
"EMPTY_STATE": "No rules found with current filters.",
|
||||||
|
"FIELDS": {
|
||||||
|
"INBOX": "Inbox",
|
||||||
|
"BRAND": "Brand",
|
||||||
|
"DAY": "Day",
|
||||||
|
"DAYS": "Days",
|
||||||
|
"CATEGORY": "Category",
|
||||||
|
"DURATION": "Duration",
|
||||||
|
"PRICE": "Price",
|
||||||
|
"PRICE_DISPLAY": "$ {price}",
|
||||||
|
"ACTIONS": "Actions",
|
||||||
|
"MIN_PRICE": "Min price",
|
||||||
|
"MAX_PRICE": "Max price"
|
||||||
|
},
|
||||||
|
"FILTERS": {
|
||||||
|
"TITLE": "Filters",
|
||||||
|
"ALL": "All",
|
||||||
|
"ALL_DAYS": "All",
|
||||||
|
"CLEAR": "Clear filters"
|
||||||
|
},
|
||||||
|
"DELETE_CONFIRMATION": "Are you sure you want to delete this rule?",
|
||||||
|
"DELETE_SUCCESS": "Rule removed",
|
||||||
|
"DELETE_ERROR": "Error removing rule",
|
||||||
|
"FETCH_ERROR": "Error fetching data",
|
||||||
|
"MODAL": {
|
||||||
|
"ADD_TITLE": "New Pricing Rule",
|
||||||
|
"EDIT_TITLE": "Edit Rule",
|
||||||
|
"SELECT_DAYS_REQUIRED": "Select at least one day.",
|
||||||
|
"SELECT_BRAND_FIRST": "Select a brand first.",
|
||||||
|
"NO_CATEGORIES": "No categories registered for this brand.",
|
||||||
|
"NO_DURATIONS": "No durations registered for this brand.",
|
||||||
|
"SAVE_SUCCESS": "Price saved!",
|
||||||
|
"SAVE_ERROR": "Error saving price",
|
||||||
|
"CANCEL": "Cancel",
|
||||||
|
"SAVE": "Save",
|
||||||
|
"SELECT_CATEGORY": "Select a category",
|
||||||
|
"SELECT_DURATION": "Select a duration",
|
||||||
|
"PRICE_PLACEHOLDER": "0.00",
|
||||||
|
"REMOVE_INBOX": "Remove",
|
||||||
|
"CLOSE": "×",
|
||||||
|
"FIELDS": {
|
||||||
|
"DAYS_WEEK": "Days of the Week"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
app/models/captain/pricing.rb
Normal file
7
app/models/captain/pricing.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module Captain
|
||||||
|
class Pricing < ApplicationRecord
|
||||||
|
belongs_to :account
|
||||||
|
belongs_to :captain_brand, optional: true
|
||||||
|
belongs_to :inbox, optional: true
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -38,6 +38,9 @@ module Captain
|
|||||||
actual_params = resolve_params(args, params)
|
actual_params = resolve_params(args, params)
|
||||||
File.open(Rails.root.join('log/tool_debug.log'), 'a') do |f|
|
File.open(Rails.root.join('log/tool_debug.log'), 'a') do |f|
|
||||||
f.puts "[#{Time.now}] STARTING CheckAvailabilityTool with params: #{actual_params}"
|
f.puts "[#{Time.now}] STARTING CheckAvailabilityTool with params: #{actual_params}"
|
||||||
|
f.puts "[#{Time.now}] PRICING COUNT: #{Captain::Pricing.count}"
|
||||||
|
f.puts "[#{Time.now}] FIRST PRICING: #{Captain::Pricing.first.inspect}"
|
||||||
|
f.puts "[#{Time.now}] ALL PRICINGS: #{Captain::Pricing.all.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
suite_category = actual_params[:suite]
|
suite_category = actual_params[:suite]
|
||||||
@ -56,8 +59,9 @@ module Captain
|
|||||||
File.open(Rails.root.join('log/tool_debug.log'), 'a') { |f| f.puts "[#{Time.now}] RESOLVED DATE: #{target_date} | SUITE: #{suite_category}" }
|
File.open(Rails.root.join('log/tool_debug.log'), 'a') { |f| f.puts "[#{Time.now}] RESOLVED DATE: #{target_date} | SUITE: #{suite_category}" }
|
||||||
|
|
||||||
# Find pricing strategy
|
# Find pricing strategy
|
||||||
pricing_scope = Captain::Pricing.where(account_id: @conversation.account_id)
|
account_id = @conversation&.account_id || @assistant&.account_id
|
||||||
.where('LOWER(suite_category) = ?', suite_category.downcase)
|
pricing_scope = Captain::Pricing.where(account_id: account_id)
|
||||||
|
.where('suite_category ILIKE ?', "%#{suite_category}%")
|
||||||
|
|
||||||
pricing_scope = filter_pricings_by_day_range(pricing_scope, target_date) if target_date
|
pricing_scope = filter_pricings_by_day_range(pricing_scope, target_date) if target_date
|
||||||
|
|
||||||
@ -101,6 +105,11 @@ module Captain
|
|||||||
File.open(Rails.root.join('log/tool_debug.log'), 'a') { |f| f.puts "[#{Time.now}] FAILURE: #{msg}" }
|
File.open(Rails.root.join('log/tool_debug.log'), 'a') { |f| f.puts "[#{Time.now}] FAILURE: #{msg}" }
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
rescue StandardError => e
|
||||||
|
File.open(Rails.root.join('log/tool_debug.log'), 'a') do |f|
|
||||||
|
f.puts "[#{Time.now}] CRITICAL ERROR in CheckAvailabilityTool: #{e.message}\n#{e.backtrace.first(5).join("\n")}"
|
||||||
|
end
|
||||||
|
raise e
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@ -7,6 +7,13 @@ class Integrations::Captain::ProcessorService < Integrations::BotProcessorServic
|
|||||||
call_captain(message_content)
|
call_captain(message_content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Prevent bot from replying to itself or other agents
|
||||||
|
def should_run_processor?(message)
|
||||||
|
return false if message.outgoing? || message.sender_type != 'Contact'
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
def process_response(message, response)
|
def process_response(message, response)
|
||||||
if response == 'conversation_handoff'
|
if response == 'conversation_handoff'
|
||||||
message.conversation.bot_handoff!
|
message.conversation.bot_handoff!
|
||||||
|
|||||||
77
progresso/resolucao_erro_tarifas_captain.md
Normal file
77
progresso/resolucao_erro_tarifas_captain.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Resolução de Erro: Captain Pricing (CheckAvailabilityTool) - 22/01/2026
|
||||||
|
|
||||||
|
## 1. O Problema
|
||||||
|
|
||||||
|
O usuário recebia a mensagem "Estou com um erro no meu sistema" ou "Não encontrei tarifas" ao buscar preços (ex: "preço hidro") no Playground ou via WhatsApp.
|
||||||
|
|
||||||
|
## 2. Diagnóstico Técnica
|
||||||
|
|
||||||
|
Ao analisar os logs (`log/tool_debug.log`), identificamos um **CRITICAL ERROR**:
|
||||||
|
|
||||||
|
```
|
||||||
|
undefined method 'account_id' for nil:NilClass
|
||||||
|
```
|
||||||
|
|
||||||
|
Isso ocorria na linha:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pricing_scope = Captain::Pricing.where(account_id: @conversation.account_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Causa Raiz:**
|
||||||
|
A ferramenta `CheckAvailabilityTool` assumia que sempre existiria um objeto `@conversation`.
|
||||||
|
|
||||||
|
- No **WhatsApp** (produção), geralmente existe uma conversa persistida.
|
||||||
|
- No **Playground** ou testes diretos, a variável `@conversation` pode chegar `nil` (nula), pois o contexto é volátil.
|
||||||
|
- Ao tentar acessar `@conversation.account_id`, o sistema quebrava (Crash), retornando a mensagem genérica de erro.
|
||||||
|
|
||||||
|
## 3. A Solução Implementada
|
||||||
|
|
||||||
|
Alteramos o código para ser resiliente. Se não houver conversa, usamos o ID da conta do próprio Assistente (`@assistant`), que sempre existe.
|
||||||
|
|
||||||
|
**Arquivo:** `enterprise/app/services/captain/tools/check_availability_tool.rb`
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# ANTES (Quebrava se @conversation fosse nil)
|
||||||
|
account_id = @conversation.account_id
|
||||||
|
|
||||||
|
# DEPOIS (Correção Robusta)
|
||||||
|
account_id = @conversation&.account_id || @assistant&.account_id
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Requisitos de Configuração (Dados)
|
||||||
|
|
||||||
|
Para que a tarifa seja encontrada, ela precisa seguir estas regras no Banco de Dados (`Captain::Pricing`):
|
||||||
|
|
||||||
|
1. **Account ID:** Deve ser idêntico ao da conta do bot (ex: ID 1).
|
||||||
|
2. **Inbox ID (CRÍTICO):**
|
||||||
|
- Se `inbox_id` for `nil` (vazio): A tarifa é **GLOBAL** e funciona para qualquer canal (WhatsApp, Web, API, Playground). **Recomendado para testes.**
|
||||||
|
- Se `inbox_id` estiver preenchido: A tarifa só será encontrada se a conversa vier _exatamente_ daquele inbox.
|
||||||
|
3. **Nome da Suíte (Fuzzy Match):**
|
||||||
|
- O sistema usa busca aproximada (`ILIKE`).
|
||||||
|
- Exemplo: Se cadastrar "Spa-Hidromassagem", buscar por "hidro", "massagem" ou "spa" irá funcionar.
|
||||||
|
|
||||||
|
## 5. Como Identificar e Corrigir no Futuro
|
||||||
|
|
||||||
|
Se o erro voltar ("Estou com um erro no meu sistema" ou tarifa não encontrada):
|
||||||
|
|
||||||
|
1. **Verifique os Logs:**
|
||||||
|
Execute no terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tail -f log/tool_debug.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Procure por `CRITICAL ERROR` ou `PRICING COUNT`.
|
||||||
|
|
||||||
|
2. **Verifique se a Tarifa Existe:**
|
||||||
|
Se o log mostra `PRICING COUNT: 0`, a tarifa não está no banco para aquela conta.
|
||||||
|
Se o log mostra tarifas (ex: `PRICING COUNT: 30`) mas falha em achar a sua, verifique o nome (`suite_category`) e se o `inbox_id` não está restringindo o acesso.
|
||||||
|
|
||||||
|
3. **Reinicie o Serviço:**
|
||||||
|
Sempre que alterar código Ruby (como models ou services), reinicie os processos:
|
||||||
|
```bash
|
||||||
|
make run
|
||||||
|
# ou
|
||||||
|
systemctl restart chatwoot-web sidekiq
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue
Block a user