|
| Started by Boller, Aug., 23 2025 12:26 AM - 1 reply |
| |
| | | |
|
| |
Registered member 4,618 messages |
|
| Posted on August, 23 2025 - 12:26 AM |
Vou criar um exemplo completo de integração entre Supabase (PostgreSQL) e Windev para um sistema de pedidos com tabelas relacionadas.
Criei um exemplo completo de integração entre Supabase (PostgreSQL) e Windev para um sistema de pedidos. O exemplo inclui:
## **Estrutura do Banco (Supabase/PostgreSQL):**
- Tabelas `pedidos` e `pedidos_itens` com relacionamento - Triggers automáticos para calcular valor total - Índices para otimização de performance - Row Level Security (RLS) opcional
## **Implementação Windev:**
- Classes `CPedido` e `CPedidoItem` para encapsular a lógica - Métodos para operações CRUD via API REST do Supabase - Tratamento adequado de JSON e requisições HTTP - Exemplo de uso em interface
## **Funcionalidades Implementadas:**
- ✅ Inserção de pedidos e itens - ✅ Atualização automática de valores totais - ✅ Busca de pedidos e itens - ✅ Relacionamento entre tabelas - ✅ Validações e tratamento de erros
## **Próximos Passos:**
1. Configure seu projeto no Supabase e execute os SQLs 1. Substitua as constantes pelas suas chaves reais 1. Adapte as classes conforme suas necessidades específicas 1. Implemente validações adicionais conforme seu negócio
# Sistema de Pedidos - Supabase + Windev
## 1. Estrutura do Banco de Dados no Supabase (PostgreSQL)
### Tabela: pedidos
```sql -- Criar tabela pedidos CREATE TABLE pedidos ( id SERIAL PRIMARY KEY, numero_pedido VARCHAR(20) UNIQUE NOT NULL, cliente_id INTEGER, data_pedido TIMESTAMP DEFAULT CURRENT_TIMESTAMP, valor_total DECIMAL(10,2) DEFAULT 0.00, status VARCHAR(20) DEFAULT 'PENDENTE', observacoes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
-- Índices para otimização CREATE INDEX idx_pedidos_cliente ON pedidos(cliente_id); CREATE INDEX idx_pedidos_data ON pedidos(data_pedido); CREATE INDEX idx_pedidos_status ON pedidos(status); ```
### Tabela: pedidos_itens
```sql -- Criar tabela pedidos_itens CREATE TABLE pedidos_itens ( id SERIAL PRIMARY KEY, pedido_id INTEGER NOT NULL, produto_id INTEGER NOT NULL, descricao_produto VARCHAR(255), quantidade DECIMAL(10,3) DEFAULT 1.000, valor_unitario DECIMAL(10,2) DEFAULT 0.00, valor_total DECIMAL(10,2) DEFAULT 0.00, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Chave estrangeira CONSTRAINT fk_pedidos_itens_pedido FOREIGN KEY (pedido_id) REFERENCES pedidos(id) ON DELETE CASCADE );
-- Índices CREATE INDEX idx_pedidos_itens_pedido ON pedidos_itens(pedido_id); CREATE INDEX idx_pedidos_itens_produto ON pedidos_itens(produto_id); ```
### Função para atualizar valor total do pedido
```sql -- Função para calcular valor total do pedido CREATE OR REPLACE FUNCTION atualizar_valor_total_pedido() RETURNS TRIGGER AS $$ BEGIN UPDATE pedidos SET valor_total = ( SELECT COALESCE(SUM(valor_total), 0.00) FROM pedidos_itens WHERE pedido_id = COALESCE(NEW.pedido_id, OLD.pedido_id) ), updated_at = CURRENT_TIMESTAMP WHERE id = COALESCE(NEW.pedido_id, OLD.pedido_id); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql;
-- Trigger para atualizar automaticamente o valor total CREATE TRIGGER trigger_atualizar_valor_pedido AFTER INSERT OR UPDATE OR DELETE ON pedidos_itens FOR EACH ROW EXECUTE FUNCTION atualizar_valor_total_pedido(); ```
### RLS (Row Level Security) - Opcional
```sql -- Habilitar RLS nas tabelas ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY; ALTER TABLE pedidos_itens ENABLE ROW LEVEL SECURITY;
-- Política básica (ajustar conforme necessidade) CREATE POLICY "Permitir tudo para usuários autenticados" ON pedidos FOR ALL USING (auth.role() = 'authenticated');
CREATE POLICY "Permitir tudo para usuários autenticados" ON pedidos_itens FOR ALL USING (auth.role() = 'authenticated'); ```
## 2. Configuração no Windev
### Estrutura de Dados no Análise Windev
#### Arquivo: Pedidos
``` Pedidos ├── ID (Numérico, Chave primária, Auto-incremento) ├── NumeroPedido (Texto, 20 caracteres, Único) ├── ClienteID (Numérico) ├── DataPedido (Data e Hora) ├── ValorTotal (Monetário) ├── Status (Texto, 20 caracteres) ├── Observacoes (Texto longo) ├── CreatedAt (Data e Hora) └── UpdatedAt (Data e Hora) ```
#### Arquivo: PedidosItens
``` PedidosItens ├── ID (Numérico, Chave primária, Auto-incremento) ├── PedidoID (Numérico, Chave estrangeira para Pedidos) ├── ProdutoID (Numérico) ├── DescricaoProduto (Texto, 255 caracteres) ├── Quantidade (Numérico real) ├── ValorUnitario (Monetário) ├── ValorTotal (Monetário) └── CreatedAt (Data e Hora) ```
### Conexão com Supabase
#### Código de Conexão (Inicialização do projeto)
```wlanguage // Declarações globais ou classe de conexão CONSTANTE SUPABASE_URL = "https://seuprojetoid.supabase.co" SUPABASE_ANON_KEY = "sua-chave-anonima-aqui" SUPABASE_SERVICE_KEY = "sua-chave-de-servico-aqui" // Para operações admin FIM
// Variáveis globais gsSupabaseURL é string = SUPABASE_URL gsSupabaseKey é string = SUPABASE_ANON_KEY
// Função para configurar headers das requisições PROCEDIMENTO ConfigurarHeadersSupabase() HTTPReinicializar() HTTPAdicionarHeader("Content-Type", "application/json") HTTPAdicionarHeader("apikey", gsSupabaseKey) HTTPAdicionarHeader("Authorization", "Bearer " + gsSupabaseKey) FIM ```
### Classes de Manipulação
#### Classe: CPedido
```wlanguage CPedido é uma Classe // Propriedades m_nID é inteiro m_sNumeroPedido é string m_nClienteID é inteiro m_dhDataPedido é DataHora m_rValorTotal é real m_sStatus é string = "PENDENTE" m_sObservacoes é string m_dhCreatedAt é DataHora m_dhUpdatedAt é DataHora // Lista de itens m_arrItens é array de CPedidoItem FIM
// Método: Inserir pedido PROCEDIMENTO CPedido::Inserir() LOCAL sJSON é string sURL é string sResposta é string vResposta é Variant FIM // Preparar dados para inserção sJSON = [ { "numero_pedido": "%1", "cliente_id": %2, "data_pedido": "%3", "status": "%4", "observacoes": "%5" } ] sJSON = StringFormatar(sJSON, ...:m_sNumeroPedido, ...:m_nClienteID, .. DateTimeParaString(m_dhDataPedido, "YYYY-MM-DD HH:mm:ss"), .. :m_sStatus, :m_sObservacoes) sURL = gsSupabaseURL + "/rest/v1/pedidos" ConfigurarHeadersSupabase() HTTPAdicionarHeader("Prefer", "return=representation") sResposta = HTTPRequisição(httpPost, sURL, sJSON) SE HTTPObterResultado() = 200 OU HTTPObterResultado() = 201 ENTÃO vResposta = JSONParaVariant(sResposta) :m_nID = vResposta[1].id :m_dhCreatedAt = StringParaDataHora(vResposta[1].created_at) :m_dhUpdatedAt = StringParaDataHora(vResposta[1].updated_at) RESULTADO Verdadeiro SENÃO Erro("Erro ao inserir pedido: " + sResposta) RESULTADO Falso FIM FIM
// Método: Atualizar pedido PROCEDIMENTO CPedido::Atualizar() LOCAL sJSON é string sURL é string sResposta é string FIM sJSON = [ { "cliente_id": %1, "status": "%2", "observacoes": "%3", "updated_at": "%4" } ] sJSON = StringFormatar(sJSON, :m_nClienteID, :m_sStatus, .. :m_sObservacoes, DateTimeParaString(DateTimeSys(), "YYYY-MM-DD HH:mm:ss")) sURL = gsSupabaseURL + "/rest/v1/pedidos?id=eq." + :m_nID ConfigurarHeadersSupabase() sResposta = HTTPRequisição(httpPatch, sURL, sJSON) SE HTTPObterResultado() = 200 OU HTTPObterResultado() = 204 ENTÃO RESULTADO Verdadeiro SENÃO Erro("Erro ao atualizar pedido: " + sResposta) RESULTADO Falso FIM FIM
// Método: Buscar pedido por ID PROCEDIMENTO CPedido::BuscarPorID(nID é inteiro) LOCAL sURL é string sResposta é string vResposta é Variant FIM sURL = gsSupabaseURL + "/rest/v1/pedidos?id=eq." + nID + "&select=*" ConfigurarHeadersSupabase() sResposta = HTTPRequisição(httpGet, sURL) SE HTTPObterResultado() = 200 ENTÃO vResposta = JSONParaVariant(sResposta) SE TableauTaille(vResposta) > 0 ENTÃO :m_nID = vResposta[1].id :m_sNumeroPedido = vResposta[1].numero_pedido :m_nClienteID = vResposta[1].cliente_id :m_dhDataPedido = StringParaDataHora(vResposta[1].data_pedido) :m_rValorTotal = vResposta[1].valor_total :m_sStatus = vResposta[1].status :m_sObservacoes = vResposta[1].observacoes :m_dhCreatedAt = StringParaDataHora(vResposta[1].created_at) :m_dhUpdatedAt = StringParaDataHora(vResposta[1].updated_at) RESULTADO Verdadeiro FIM FIM RESULTADO Falso FIM ```
#### Classe: CPedidoItem
```wlanguage CPedidoItem é uma Classe // Propriedades m_nID é inteiro m_nPedidoID é inteiro m_nProdutoID é inteiro m_sDescricaoProduto é string m_rQuantidade é real = 1.0 m_rValorUnitario é real = 0.0 m_rValorTotal é real = 0.0 m_dhCreatedAt é DataHora FIM
// Método: Calcular valor total PROCEDIMENTO CPedidoItem::CalcularValorTotal() :m_rValorTotal = :m_rQuantidade * :m_rValorUnitario FIM
// Método: Inserir item PROCEDIMENTO CPedidoItem::Inserir() LOCAL sJSON é string sURL é string sResposta é string vResposta é Variant FIM // Calcular valor total antes de inserir :CalcularValorTotal() sJSON = [ { "pedido_id": %1, "produto_id": %2, "descricao_produto": "%3", "quantidade": %4, "valor_unitario": %5, "valor_total": %6 } ] sJSON = StringFormatar(sJSON, :m_nPedidoID, :m_nProdutoID, .. :m_sDescricaoProduto, :m_rQuantidade, .. :m_rValorUnitario, :m_rValorTotal) sURL = gsSupabaseURL + "/rest/v1/pedidos_itens" ConfigurarHeadersSupabase() HTTPAdicionarHeader("Prefer", "return=representation") sResposta = HTTPRequisição(httpPost, sURL, sJSON) SE HTTPObterResultado() = 200 OU HTTPObterResultado() = 201 ENTÃO vResposta = JSONParaVariant(sResposta) :m_nID = vResposta[1].id :m_dhCreatedAt = StringParaDataHora(vResposta[1].created_at) RESULTADO Verdadeiro SENÃO Erro("Erro ao inserir item do pedido: " + sResposta) RESULTADO Falso FIM FIM
// Método: Buscar itens por pedido PROCEDIMENTO BuscarItensPorPedido(nPedidoID é inteiro) : array de CPedidoItem LOCAL sURL é string sResposta é string vResposta é Variant arrItens é array de CPedidoItem oItem é CPedidoItem nI é inteiro FIM sURL = gsSupabaseURL + "/rest/v1/pedidos_itens?pedido_id=eq." + nPedidoID + "&select=*&order=id" ConfigurarHeadersSupabase() sResposta = HTTPRequisição(httpGet, sURL) SE HTTPObterResultado() = 200 ENTÃO vResposta = JSONParaVariant(sResposta) PARA nI = 1 _A_ TableauTaille(vResposta) oItem = novo CPedidoItem() oItem.m_nID = vResposta[nI].id oItem.m_nPedidoID = vResposta[nI].pedido_id oItem.m_nProdutoID = vResposta[nI].produto_id oItem.m_sDescricaoProduto = vResposta[nI].descricao_produto oItem.m_rQuantidade = vResposta[nI].quantidade oItem.m_rValorUnitario = vResposta[nI].valor_unitario oItem.m_rValorTotal = vResposta[nI].valor_total oItem.m_dhCreatedAt = StringParaDataHora(vResposta[nI].created_at) TableauAdicionar(arrItens, oItem) FIM FIM RESULTADO arrItens FIM ```
### Exemplo de Uso na Interface
#### Janela de Cadastro de Pedidos
```wlanguage // Evento: Clique no botão Salvar LOCAL oPedido é CPedido oItem é CPedidoItem nI é inteiro FIM
// Criar novo pedido oPedido = novo CPedido() oPedido.m_sNumeroPedido = SAI_NumeroPedido oPedido.m_nClienteID = SAI_ClienteID oPedido.m_dhDataPedido = SAI_DataPedido oPedido.m_sStatus = COMBO_Status oPedido.m_sObservacoes = SAI_Observacoes
// Inserir pedido SE oPedido.Inserir() ENTÃO // Inserir itens PARA nI = 1 _A_ TableauTaille(TABELA_Itens) oItem = novo CPedidoItem() oItem.m_nPedidoID = oPedido.m_nID oItem.m_nProdutoID = TABELA_Itens[nI].COL_ProdutoID oItem.m_sDescricaoProduto = TABELA_Itens[nI].COL_Descricao oItem.m_rQuantidade = TABELA_Itens[nI].COL_Quantidade oItem.m_rValorUnitario = TABELA_Itens[nI].COL_ValorUnitario SE NÃO oItem.Inserir() ENTÃO Erro("Erro ao inserir item " + nI) INTERRUPÇÃO FIM FIM Info("Pedido salvo com sucesso!") Fechar() SENÃO Erro("Erro ao salvar pedido") FIM ```
### Configuração no Supabase Dashboard
1. **Criar o projeto no Supabase** 1. **Configurar as tabelas** usando o SQL Editor 1. **Configurar RLS** se necessário 1. **Obter as chaves de API**: - Project URL - Anon/Public Key - Service Role Key (para operações administrativas)
### Dicas de Implementação
1. **Tratamento de Erros**: Sempre verificar os códigos de resposta HTTP 1. **Validação**: Implementar validações antes de enviar dados 1. **Cache**: Considerar cache local para melhorar performance 1. **Transações**: Para operações complexas, considere usar transações do PostgreSQL 1. **Logs**: Implementar logs para debugar problemas de integração
Este exemplo fornece uma base sólida para integração entre Supabase e Windev, permitindo operações CRUD completas em um sistema de pedidos.
Entendi que você quer focar no primeiro ponto. Vou detalhar especificamente a configuração do projeto no Supabase e a execução dos SQLs:
Criei um guia detalhado para a configuração do projeto no Supabase. O processo é dividido em etapas claras:
## **Principais Etapas:**
1. **Criar conta e projeto no Supabase** - Interface web simples 1. **Executar 4 scripts SQL sequenciais:** - Criação das tabelas - Índices para performance - Função + Trigger automático - Dados de teste (opcional) 1. **Verificar se tudo foi criado corretamente** 1. **Obter as chaves de API** necessárias para o Windev 1. **Testar a conexão** via REST API
## **Pontos Importantes:**
- ✅ Scripts organizados em sequência lógica - ✅ Explicação de cada comando SQL - ✅ Verificações para confirmar que funcionou - ✅ Seção de troubleshooting para problemas comuns - ✅ Teste da API REST antes de integrar com Windev
O trigger automático é especialmente útil - ele recalcula o valor total do pedido sempre que você adiciona, remove ou modifica itens, mantendo a consistência dos dados.
# Configuração do Projeto Supabase - Passo a Passo
## 1. Criando o Projeto no Supabase
### 1.1 Acesso ao Supabase
1. Acesse [supabase.com](https://supabase.com) 1. Clique em “Start your project” 1. Faça login com GitHub, Google ou email
### 1.2 Criação do Projeto
1. Clique em “New Project” 1. Selecione sua organização (ou crie uma nova) 1. Preencha os dados: - **Project Name**: `sistema-pedidos` (ou nome de sua escolha) - **Database Password**: Use uma senha forte (anote ela!) - **Region**: Escolha a região mais próxima (ex: South America - São Paulo) 1. Clique em “Create new project” 1. Aguarde alguns minutos para o projeto ser criado
## 2. Executando os Scripts SQL
### 2.1 Acessando o SQL Editor
1. No painel do seu projeto, clique em “SQL Editor” no menu lateral 1. Clique em “New Query” para criar uma nova consulta
### 2.2 Script 1: Criação das Tabelas
Cole e execute este script:
```sql -- SCRIPT 1: CRIAÇÃO DAS TABELAS -- Execute primeiro este bloco
-- Criar tabela pedidos CREATE TABLE pedidos ( id SERIAL PRIMARY KEY, numero_pedido VARCHAR(20) UNIQUE NOT NULL, cliente_id INTEGER, data_pedido TIMESTAMP DEFAULT CURRENT_TIMESTAMP, valor_total DECIMAL(10,2) DEFAULT 0.00, status VARCHAR(20) DEFAULT 'PENDENTE', observacoes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
-- Criar tabela pedidos_itens CREATE TABLE pedidos_itens ( id SERIAL PRIMARY KEY, pedido_id INTEGER NOT NULL, produto_id INTEGER NOT NULL, descricao_produto VARCHAR(255), quantidade DECIMAL(10,3) DEFAULT 1.000, valor_unitario DECIMAL(10,2) DEFAULT 0.00, valor_total DECIMAL(10,2) DEFAULT 0.00, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Chave estrangeira CONSTRAINT fk_pedidos_itens_pedido FOREIGN KEY (pedido_id) REFERENCES pedidos(id) ON DELETE CASCADE ); ```
**Passos para executar:**
1. Cole o código acima no SQL Editor 1. Clique em “RUN” (ou Ctrl+Enter) 1. Verifique se aparece “Success. No rows returned” ou similar
### 2.3 Script 2: Criação dos Índices
Cole e execute este script em uma nova query:
```sql -- SCRIPT 2: CRIAÇÃO DOS ÍNDICES -- Execute após criar as tabelas
-- Índices para otimização da tabela pedidos CREATE INDEX idx_pedidos_cliente ON pedidos(cliente_id); CREATE INDEX idx_pedidos_data ON pedidos(data_pedido); CREATE INDEX idx_pedidos_status ON pedidos(status); CREATE INDEX idx_pedidos_numero ON pedidos(numero_pedido);
-- Índices para otimização da tabela pedidos_itens CREATE INDEX idx_pedidos_itens_pedido ON pedidos_itens(pedido_id); CREATE INDEX idx_pedidos_itens_produto ON pedidos_itens(produto_id); ```
### 2.4 Script 3: Função e Trigger para Cálculo Automático
Cole e execute este script:
```sql -- SCRIPT 3: FUNÇÃO E TRIGGER PARA CÁLCULO AUTOMÁTICO -- Execute após criar as tabelas e índices
-- Função para calcular valor total do pedido automaticamente CREATE OR REPLACE FUNCTION atualizar_valor_total_pedido() RETURNS TRIGGER AS $$ BEGIN -- Atualiza o valor total do pedido baseado na soma dos itens UPDATE pedidos SET valor_total = ( SELECT COALESCE(SUM(valor_total), 0.00) FROM pedidos_itens WHERE pedido_id = COALESCE(NEW.pedido_id, OLD.pedido_id) ), updated_at = CURRENT_TIMESTAMP WHERE id = COALESCE(NEW.pedido_id, OLD.pedido_id); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql;
-- Trigger que executa a função automaticamente CREATE TRIGGER trigger_atualizar_valor_pedido AFTER INSERT OR UPDATE OR DELETE ON pedidos_itens FOR EACH ROW EXECUTE FUNCTION atualizar_valor_total_pedido(); ```
### 2.5 Script 4: Dados de Teste (Opcional)
Para testar se tudo está funcionando:
```sql -- SCRIPT 4: DADOS DE TESTE -- Execute para inserir dados de exemplo
-- Inserir pedido de teste INSERT INTO pedidos (numero_pedido, cliente_id, status, observacoes) VALUES ('PED001', 1, 'PENDENTE', 'Pedido de teste');
-- Inserir itens de teste (pegue o ID do pedido criado) INSERT INTO pedidos_itens (pedido_id, produto_id, descricao_produto, quantidade, valor_unitario, valor_total) VALUES (1, 101, 'Produto A', 2.000, 25.50, 51.00), (1, 102, 'Produto B', 1.000, 15.75, 15.75), (1, 103, 'Produto C', 3.500, 8.20, 28.70);
-- Verificar se o trigger funcionou (valor_total deve ser 95.45) SELECT * FROM pedidos WHERE id = 1; ```
## 3. Verificação das Tabelas Criadas
### 3.1 Via Table Editor
1. Clique em “Table Editor” no menu lateral 1. Você deve ver as tabelas: - `pedidos` - `pedidos_itens`
### 3.2 Via SQL - Consulta de Verificação
```sql -- Verificar estrutura das tabelas SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name IN ('pedidos', 'pedidos_itens') ORDER BY table_name, ordinal_position;
-- Verificar triggers SELECT trigger_name, event_manipulation, event_object_table FROM information_schema.triggers WHERE event_object_table IN ('pedidos', 'pedidos_itens');
-- Verificar índices SELECT indexname, tablename, indexdef FROM pg_indexes WHERE tablename IN ('pedidos', 'pedidos_itens'); ```
## 4. Configurar RLS (Row Level Security) - Opcional
Se você quiser controlar acesso aos dados:
```sql -- SCRIPT 5: CONFIGURAÇÃO DE RLS (OPCIONAL) -- Execute apenas se precisar de controle de acesso
-- Habilitar RLS ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY; ALTER TABLE pedidos_itens ENABLE ROW LEVEL SECURITY;
-- Política para permitir tudo para usuários autenticados CREATE POLICY "Permitir acesso completo para autenticados" ON pedidos FOR ALL USING (auth.role() = 'authenticated');
CREATE POLICY "Permitir acesso completo para autenticados" ON pedidos_itens FOR ALL USING (auth.role() = 'authenticated');
-- Política para permitir acesso anônimo (para desenvolvimento) -- CUIDADO: Use apenas em desenvolvimento! CREATE POLICY "Permitir acesso anônimo" ON pedidos FOR ALL USING (true);
CREATE POLICY "Permitir acesso anônimo" ON pedidos_itens FOR ALL USING (true); ```
## 5. Obter Chaves de API
### 5.1 Localizar as Chaves
1. No menu lateral, clique em “Settings” 1. Clique em “API” 1. Você verá: - **Project URL**: `https://seuprojetoid.supabase.co` - **anon/public key**: Para uso no frontend - **service_role key**: Para operações admin (mantenha secreta!)
### 5.2 Anotar as Informações
``` Project URL: https://xxxxxxxxxxx.supabase.co Anon Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Service Role: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Database Password: [senha que você definiu] ```
## 6. Testar Conexão via API REST
### 6.1 Teste Simples
Use um cliente REST (Postman, Insomnia) ou curl:
```bash # Listar pedidos curl -X GET 'https://seuprojetoid.supabase.co/rest/v1/pedidos' \ -H "apikey: SUA_ANON_KEY" \ -H "Authorization: Bearer SUA_ANON_KEY"
# Inserir pedido curl -X POST 'https://seuprojetoid.supabase.co/rest/v1/pedidos' \ -H "apikey: SUA_ANON_KEY" \ -H "Authorization: Bearer SUA_ANON_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '{ "numero_pedido": "PED002", "cliente_id": 2, "status": "PENDENTE", "observacoes": "Teste via API" }' ```
## 7. Troubleshooting
### Problemas Comuns:
**Erro de permissão:**
- Verifique se RLS está desabilitado ou se as políticas estão corretas
**Erro de chave estrangeira:**
- Certifique-se que as tabelas foram criadas na ordem correta
**Função não encontrada:**
- Verifique se o script da função foi executado sem erros
**Trigger não funciona:**
- Teste inserindo um item e verificando se o valor_total do pedido é atualizado
### Comandos de Diagnóstico:
```sql -- Ver todas as tabelas \dt
-- Ver estrutura de uma tabela \d pedidos
-- Ver triggers ativos SELECT * FROM information_schema.triggers WHERE event_object_table = 'pedidos_itens';
-- Testar função manualmente SELECT atualizar_valor_total_pedido(); ```
Agora você tem sua base de dados configurada e pronta para integração com o Windev!
Client Use #######
Unified WX Client for Supabase Windev, Webdev, and Windev Mobile 1. Base Supabase Class (Shared) Class: CSupabaseClient CSupabaseClient is a Class // Private properties PRIVATE m_sURL is string m_sAnonKey is string m_sServiceKey is string m_sAuthToken is string m_bDebugMode is boolean = False END // Public properties PUBLIC TimeoutHTTP is integer = 30000 // 30 seconds MaxRetries is integer = 3 END END
// Constructor PROCEDURE Constructor(LOCAL sURL is string, LOCAL sAnonKey is string, LOCAL sServiceKey is string = "") :m_sURL = sURL :m_sAnonKey = sAnonKey :m_sServiceKey = sServiceKey // Initialization log IF :m_bDebugMode THEN LogWrite("Supabase Client initialized: " + sURL) END END
// Configure default headers PROCEDURE PRIVATE ConfigureHeaders(bUseServiceKey is boolean = False) HTTPReset() HTTPAddHeader("Content-Type", "application/json") HTTPAddHeader("Accept", "application/json") IF bUseServiceKey AND Length(:m_sServiceKey) > 0 THEN HTTPAddHeader("apikey", :m_sServiceKey) HTTPAddHeader("Authorization", "Bearer " + :m_sServiceKey) ELSE HTTPAddHeader("apikey", :m_sAnonKey) IF Length(:m_sAuthToken) > 0 THEN HTTPAddHeader("Authorization", "Bearer " + :m_sAuthToken) ELSE HTTPAddHeader("Authorization", "Bearer " + :m_sAnonKey) END END // Timeout HTTPConfigure(httpTimeOut, :TimeoutHTTP) END
// Generic request method PROCEDURE PRIVATE MakeRequest(sMethod is string, sEndpoint is string, sBody is string = "", bUseServiceKey is boolean = False) : Variant LOCAL sURL is string sResponse is string vResult is Variant nAttempts is integer = 0 nStatusHTTP is integer sLogMsg is string END sURL = :m_sURL + sEndpoint WHILE nAttempts < :MaxRetries nAttempts++ :ConfigureHeaders(bUseServiceKey) // Request log IF :m_bDebugMode THEN sLogMsg = StringFormat("[SUPABASE] %1 %2", sMethod, sURL) IF Length(sBody) > 0 THEN sLogMsg += " Body: " + sBody END LogWrite(sLogMsg) END // Make request based on method CHOOSE sMethod CASE "GET" sResponse = HTTPRequest(httpGet, sURL) CASE "POST" sResponse = HTTPRequest(httpPost, sURL, sBody) CASE "PATCH" sResponse = HTTPRequest(httpPatch, sURL, sBody) CASE "PUT" sResponse = HTTPRequest(httpPut, sURL, sBody) CASE "DELETE" sResponse = HTTPRequest(httpDelete, sURL) OTHER CASE Error("Unsupported HTTP method: " + sMethod) RETURN Null END nStatusHTTP = HTTPGetResult() // Response log IF :m_bDebugMode THEN LogWrite(StringFormat("[SUPABASE] Response %1: %2", nStatusHTTP, Left(sResponse, 200))) END // Check for success IF nStatusHTTP >= 200 AND nStatusHTTP <= 299 THEN // Success - convert response to Variant IF Length(sResponse) > 0 THEN vResult = JSONToVariant(sResponse) ELSE vResult.success = True vResult.statusCode = nStatusHTTP END vResult.statusCode = nStatusHTTP RETURN vResult ELSE IF nStatusHTTP >= 500 AND nAttempts < :MaxRetries THEN // Server error - retry IF :m_bDebugMode THEN LogWrite(StringFormat("[SUPABASE] Server error %1, attempt %2/%3", nStatusHTTP, nAttempts, :MaxRetries)) END Delay(1000 * nAttempts) // Exponential backoff CONTINUE ELSE // Permanent error vResult.error = True vResult.statusCode = nStatusHTTP vResult.message = sResponse vResult.httpError = HTTPGetError() IF :m_bDebugMode THEN LogWrite(StringFormat("[SUPABASE] Permanent error %1: %2", nStatusHTTP, sResponse)) END RETURN vResult END END // Reached here, retries exhausted vResult.error = True vResult.message = "Maximum retries exceeded" RETURN vResult END
// Enable/disable debug PROCEDURE EnableDebug(bEnable is boolean = True) :m_bDebugMode = bEnable END
// Set authentication token PROCEDURE SetAuthToken(sToken is string) :m_sAuthToken = sToken END 2. Generic CRUD Operations Class Class: CSupabaseTable CSupabaseTable is a Class inherits from CSupabaseClient PRIVATE m_sTableName is string END END
// Constructor PROCEDURE Constructor(LOCAL sURL is string, LOCAL sAnonKey is string, LOCAL sTableName is string, LOCAL sServiceKey is string = "") Ancestor:Constructor(sURL, sAnonKey, sServiceKey) :m_sTableName = sTableName END
// SELECT - Fetch records PROCEDURE Select(sColumns is string = "*", sFilter is string = "", sOrder is string = "", nLimit is integer = 0) : Variant LOCAL sEndpoint is string sQuery is string = "" END sEndpoint = "/rest/v1/" + :m_sTableName // Build query string IF Length(sColumns) > 0 AND sColumns <> "*" THEN sQuery = "select=" + sColumns END IF Length(sFilter) > 0 THEN IF Length(sQuery) > 0 THEN sQuery += "&" sQuery += sFilter END IF Length(sOrder) > 0 THEN IF Length(sQuery) > 0 THEN sQuery += "&" sQuery += "order=" + sOrder END IF nLimit > 0 THEN IF Length(sQuery) > 0 THEN sQuery += "&" sQuery += "limit=" + nLimit END IF Length(sQuery) > 0 THEN sEndpoint += "?" + sQuery END RETURN :MakeRequest("GET", sEndpoint) END
// INSERT - Insert record PROCEDURE Insert(vData is Variant, bReturnData is boolean = True) : Variant LOCAL sEndpoint is string sBody is string END sEndpoint = "/rest/v1/" + :m_sTableName IF bReturnData THEN HTTPAddHeader("Prefer", "return=representation") END sBody = VariantToJSON(vData) RETURN :MakeRequest("POST", sEndpoint, sBody) END
// UPDATE - Update records PROCEDURE Update(vData is Variant, sFilter is string, bReturnData is boolean = False) : Variant LOCAL sEndpoint is string sBody is string END sEndpoint = "/rest/v1/" + :m_sTableName IF Length(sFilter) > 0 THEN sEndpoint += "?" + sFilter END IF bReturnData THEN HTTPAddHeader("Prefer", "return=representation") END sBody = VariantToJSON(vData) RETURN :MakeRequest("PATCH", sEndpoint, sBody) END
// DELETE - Delete records PROCEDURE Delete(sFilter is string) : Variant LOCAL sEndpoint is string END sEndpoint = "/rest/v1/" + :m_sTableName IF Length(sFilter) > 0 THEN sEndpoint += "?" + sFilter ELSE Error("DELETE without filter is not allowed for safety") RETURN Null END RETURN :MakeRequest("DELETE", sEndpoint) END
// UPSERT - Insert or update PROCEDURE Upsert(vData is Variant, sConflictColumn is string = "", bReturnData is boolean = True) : Variant LOCAL sEndpoint is string sBody is string END sEndpoint = "/rest/v1/" + :m_sTableName HTTPAddHeader("Prefer", "resolution=merge-duplicates") IF bReturnData THEN HTTPAddHeader("Prefer", "return=representation") END IF Length(sConflictColumn) > 0 THEN sEndpoint += "?on_conflict=" + sConflictColumn END sBody = VariantToJSON(vData) RETURN :MakeRequest("POST", sEndpoint, sBody) END 3. Domain-Specific Classes Class: COrderSupabase COrderSupabase is a Class inherits from CSupabaseTable END
// Constructor PROCEDURE Constructor(LOCAL oSupabaseConfig is CSupabaseConfig) Ancestor:Constructor(oSupabaseConfig.URL, oSupabaseConfig.AnonKey, "orders", oSupabaseConfig.ServiceKey) END
// Fetch orders with specific filters PROCEDURE FetchOrders(sStatus is string = "", nClientID is integer = 0, dhStartDate is DateTime = "", dhEndDate is DateTime = "") : Variant LOCAL sFilter is string = "" arrFilters is array of 0 string END // Build filters IF Length(sStatus) > 0 THEN ArrayAdd(arrFilters, "status=eq." + sStatus) END IF nClientID > 0 THEN ArrayAdd(arrFilters, "client_id=eq." + nClientID) END IF dhStartDate <> "" THEN ArrayAdd(arrFilters, "order_date=gte." + DateTimeToString(dhStartDate, "YYYY-MM-DD")) END IF dhEndDate <> "" THEN ArrayAdd(arrFilters, "order_date=lte." + DateTimeToString(dhEndDate, "YYYY-MM-DD")) END IF ArrayCount(arrFilters) > 0 THEN sFilter = ArrayToString(arrFilters, "&") END RETURN :Select("*", sFilter, "order_date.desc") END
// Create new order PROCEDURE CreateOrder(sOrderNumber is string, nClientID is integer, sNotes is string = "") : Variant LOCAL vOrder is Variant END vOrder.order_number = sOrderNumber vOrder.client_id = nClientID vOrder.order_date = DateTimeToString(DateTimeSys(), "YYYY-MM-DD HH:mm:ss") vOrder.status = "PENDING" IF Length(sNotes) > 0 THEN vOrder.notes = sNotes END RETURN :Insert(vOrder) END
// Update order status PROCEDURE UpdateStatus(nOrderID is integer, sNewStatus is string) : Variant LOCAL vData is Variant END vData.status = sNewStatus vData.updated_at = DateTimeToString(DateTimeSys(), "YYYY-MM-DD HH:mm:ss") RETURN :Update(vData, "id=eq." + nOrderID) END Class: COrderItemsSupabase COrderItemsSupabase is a Class inherits from CSupabaseTable END
// Constructor PROCEDURE Constructor(LOCAL oSupabaseConfig is CSupabaseConfig) Ancestor:Constructor(oSupabaseConfig.URL, oSupabaseConfig.AnonKey, "order_items", oSupabaseConfig.ServiceKey) END
// Fetch items for an order PROCEDURE FetchItemsByOrder(nOrderID is integer) : Variant RETURN :Select("*", "order_id=eq." + nOrderID, "id") END
// Add item to order PROCEDURE AddItem(nOrderID is integer, nProductID is integer, sDescription is string, rQuantity is real, rUnitPrice is real) : Variant LOCAL vItem is Variant END vItem.order_id = nOrderID vItem.product_id = nProductID vItem.product_description = sDescription vItem.quantity = rQuantity vItem.unit_price = rUnitPrice vItem.total_amount = rQuantity * rUnitPrice RETURN :Insert(vItem) END
// Update item quantity PROCEDURE UpdateQuantity(nItemID is integer, rNewQuantity is real, rUnitPrice is real) : Variant LOCAL vData is Variant END vData.quantity = rNewQuantity vData.total_amount = rNewQuantity * rUnitPrice RETURN :Update(vData, "id=eq." + nItemID) END
// Remove item from order PROCEDURE RemoveItem(nItemID is integer) : Variant RETURN :Delete("id=eq." + nItemID) END 4. Configuration Class Class: CSupabaseConfig CSupabaseConfig is a Class PUBLIC URL is string AnonKey is string ServiceKey is string END END
// Constructor PROCEDURE Constructor(LOCAL sURL is string, LOCAL sAnonKey is string, LOCAL sServiceKey is string = "") :URL = sURL :AnonKey = sAnonKey :ServiceKey = sServiceKey END
// Load configuration from INI file PROCEDURE STATIC LoadFromINI(sFilePath is string) : CSupabaseConfig LOCAL oConfig is CSupabaseConfig END oConfig = new CSupabaseConfig() oConfig.URL = INIRead("SUPABASE", "URL", "", sFilePath) oConfig.AnonKey = INIRead("SUPABASE", "ANON_KEY", "", sFilePath) oConfig.ServiceKey = INIRead("SUPABASE", "SERVICE_KEY", "", sFilePath) RETURN oConfig END 5. Usage Examples by Platform 5.1 WINDEV - Desktop Window // Window global declarations GLOBAL goSupabaseConfig is CSupabaseConfig goOrders is COrderSupabase goItems is COrderItemsSupabase END
// Window initialization PROCEDURE InitializeSupabase() // Load configuration goSupabaseConfig = CSupabaseConfig::LoadFromINI(FExeDir() + "config.ini") // Initialize clients goOrders = new COrderSupabase(goSupabaseConfig) goItems = new COrderItemsSupabase(goSupabaseConfig) // Enable debug in development goOrders.EnableDebug(True) goItems.EnableDebug(True) END
// Event: Click on Fetch Orders PROCEDURE FetchOrders() LOCAL vResult is Variant nI is integer END // Clear table TableDeleteAll(TABLE_Orders) // Fetch orders vResult = goOrders.FetchOrders(COMBO_Status..Value, EDT_ClientID..Value) IF NOT VariantTyped(vResult.error) OR vResult.error = False THEN // Fill table FOR nI = 1 TO ArrayCount(vResult) TableAdd(TABLE_Orders, [ vResult[nI].id, vResult[nI].order_number, vResult[nI].client_id, StringToDateTime(vResult[nI].order_date), vResult[nI].total_amount, vResult[nI].status ]) END Info(StringFormat("%1 orders found", ArrayCount(vResult))) ELSE Error("Error fetching orders: " + vResult.message) END END
// Event: Double-click on orders table PROCEDURE LoadOrderItems() LOCAL nOrderID is integer vResult is Variant nI is integer END nOrderID = TABLE_Orders[TABLE_Orders].[1] // Selected order ID // Clear items table TableDeleteAll(TABLE_Items) // Load items vResult = goItems.FetchItemsByOrder(nOrderID) IF NOT VariantTyped(vResult.error) OR vResult.error = False THEN FOR nI = 1 TO ArrayCount(vResult) TableAdd(TABLE_Items, [ vResult[nI].id, vResult[nI].product_id, vResult[nI].product_description, vResult[nI].quantity, vResult[nI].unit_price, vResult[nI].total_amount ]) END ELSE Error("Error loading items: " + vResult.message) END END 5.2 WEBDEV - Web Page // Server code - Project initialization PROCEDURE InitializeSupabaseWeb() // Hardcoded or file-based configuration goSupabaseConfig is CSupabaseConfig( "https://yourprojectid.supabase.co", "your-anon-key-here", "your-service-key-here" ) // Store in session SessionWrite("supabase_config", goSupabaseConfig) END
// Server code - Fetch orders AJAX PROCEDURE FetchOrdersAJAX() LOCAL oConfig is CSupabaseConfig oOrders is COrderSupabase vResult is Variant sJSON is string END // Retrieve configuration from session oConfig = SessionRead("supabase_config") oOrders = new COrderSupabase(oConfig) // Fetch orders vResult = oOrders.FetchOrders() IF NOT VariantTyped(vResult.error) OR vResult.error = False THEN sJSON = VariantToJSON(vResult) AJAXExecuteAsync("DisplayOrders", sJSON) ELSE AJAXExecuteAsync("DisplayError", "Error: " + vResult.message) END END
// Browser code - Display orders PROCEDURE DisplayOrders(sJSON is string) LOCAL vOrders is Variant nI is integer sHTML is string = "" END vOrders = JSONToVariant(sJSON) FOR nI = 1 TO ArrayCount(vOrders) sHTML += [ %2 %3 %4 %5 %6 ] sHTML = StringFormat(sHTML, vOrders[nI].id, vOrders[nI].order_number, vOrders[nI].client_id, vOrders[nI].order_date, vOrders[nI].total_amount, vOrders[nI].status ) END DIV_OrdersList..InnerHTML = sHTML END 5.3 WINDEV MOBILE - Mobile Application // Mobile project global declarations GLOBAL goSupabaseConfig is CSupabaseConfig goOrders is COrderSupabase goItems is COrderItemsSupabase END
// Application initialization PROCEDURE InitializeSupabaseMobile() // Mobile configuration goSupabaseConfig = new CSupabaseConfig( "https://yourprojectid.supabase.co", "your-anon-key-here" ) goOrders = new COrderSupabase(goSupabaseConfig) goItems = new COrderItemsSupabase(goSupabaseConfig) // Shorter timeout for mobile goOrders.TimeoutHTTP = 15000 goItems.TimeoutHTTP = 15000 END
// Window: Orders List PROCEDURE LoadOrdersList() LOCAL vResult is Variant nI is integer END // Show loading WIN_Loading.Visible = True // Thread to avoid blocking UI ThreadExecute("ThreadFetchOrders", threadNormal) END
// Thread to fetch orders PROCEDURE ThreadFetchOrders() LOCAL vResult is Variant nI is integer sResultJSON is string END vResult = goOrders.FetchOrders() sResultJSON = VariantToJSON(vResult) // Return to main thread ExecuteMainThread("ProcessOrdersResult", sResultJSON) END
// Process result in main thread PROCEDURE ProcessOrdersResult(sJSON is string) LOCAL vResult is Variant nI is integer END // Hide loading WIN_Loading.Visible = False vResult = JSONToVariant(sJSON) IF NOT VariantTyped(vResult.error) OR vResult.error = False THEN // Clear list ListDeleteAll(LIST_Orders) // Fill list FOR nI = 1 TO ArrayCount(vResult) ListAdd(LIST_Orders, StringFormat("%1 - Client: %2 - $ %3", vResult[nI].order_number, vResult[nI].client_id, NumericToString(vResult[nI].total_amount, "999,999.99") ) ) // Store order ID in stored value LIST_Orders[LIST_Orders..Occurrence].StoredValue = vResult[nI].id END ELSE Info("Error loading orders: " + vResult.message) END END 6. Configuration File (config.ini) [SUPABASE] URL=https://yourprojectid.supabase.co ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIs... SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIs...
[HTTP] TIMEOUT=30000 MAX_RETRIES=3 DEBUG=1 7. Additional Utilities Class: CSupabaseUtils CSupabaseUtils is a Class END
// Convert DateTime to Supabase format PROCEDURE STATIC DateTimeToSupabase(dhDate is DateTime) : string RETURN DateTimeToString(dhDate, "YYYY-MM-DD HH:mm:ss.CCC+00:00") END
// Convert Supabase string to DateTime PROCEDURE STATIC SupabaseToDateTime(sDate is string) : DateTime // Remove timezone if present sDate = StringReplace(sDate, "+00:00", "") sDate = StringReplace(sDate, "T", " ") RETURN StringToDateTime(sDate) END
// Escape string for filters PROCEDURE STATIC EscapeString(sValue is string) : string sValue = StringReplace(sValue, "'", "''") sValue = StringReplace(sValue, "\", "\\") RETURN sValue END
// Build text search filter PROCEDURE STATIC TextFilter(sColumn is string, sValue is string, sOperator is string = "ilike") : string IF sOperator = "ilike" THEN RETURN sColumn + "=ilike.*" + EscapeString(sValue) + "*" ELSE RETURN sColumn + "=" + sOperator + "." + EscapeString(sValue) END END
This unified WX client enables consistent use of Supabase across all WX platforms, with error handling, automatic retries, debug capabilities, and platform-specific examples.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Registered member 4,618 messages |
|
| Posted on September, 05 2025 - 11:06 AM |
Introdução
Em aplicações empresariais avançadas, como as desenvolvidas com WinDev, a atualização instantânea de dados críticos (pedidos, estoques, status) torna-se um problema de responsividade para o usuário. O acoplador Supabase Realtime com uma interface WinDev permite uma resposta eficiente a essa resposta: as alterações no banco de dados Postgres são imediatamente propagadas pela interface, sem consulta, com latência praticamente nula. Este artigo explora os comentários incluídos nessa arquitetura, após a origem da nova integração Postgres no código WinDev via WebSocketConnectSSL.
Apresentação Rápida do Supabase Realtime O Supabase Realtime é um componente da plataforma Supabase que permite receber eventos (INSERT, UPDATE, DELETE) para uma base PostgreSQL, utilizando funções de Replicação Lógica e o WAL (Write-Ahead Logging) do PostgreSQL. Configurar a replicação lógica para o serviço Realtime do Supabase também permite capturar todas as alterações em uma tabela para transmissão aos clientes existentes via WebSocket.
Formato Phoenix
O Supabase Realtime utiliza Phoenix Channels, um protocolo de comunicação baseado em JSON usado no ecossistema Elixir. Cada mensagem WebSocket segue um formato estruturado:
{ "topic": "realtime:public:command", "event": "INSERT", "payload": { "new": { "id": 4521, "client": "IsiNeva", "amount": 1250 }, "old": null, "type": "INSERT" }, "ref": null, "join_ref": "1" }
Conexão WebSocket Segura com o WinDev O WinDev possui a função WebSocketConnectSSL, ideal para estabelecer uma conexão segura com o Supabase. Exemplo de conexão WebSocket
sURL is this link = "wss://xyz.supabase.co/realtime/v1/websocket?apikey=xxxx" SE WebSocketConnectSSL("SupabaseSocket", sURL) ENTÃO Trace("Conexão dos Réus") CASO CONTRÁRIO Error("Verificação de conexão: " + ErrorInfo()) FIM
A autenticação depende de um token de API (chave do projeto ou configuração de selo JWT), que é transmitido no parâmetro da solicitação.
Gerenciamento de Mensagens no WinDev Uma vez conectado, você deve:
Entrar em um canal Phoenix (com join) Requisitar uma tabela específica (ex.: public.command) Processar eventos INSERT, UPDATE e DELETE Enviar uma mensagem de "join" no canal
sJoinMsg est une chaîne = [ { "topic": "realtime:public:commande", "event": "phx_join", "payload": {}, "ref": "1" } ] WebSocketEnvoie("SupabaseSocket", sJoinMsg)
Ou:
WebSocketSend("SupabaseSocket", sJoinMsg)
Receber e analisar mensagens
PROCÉDURE WS_SupabaseSocket_MessageReçue(sMessage est une chaîne) JSON est un Variant = JSONVersVariant(sMessage) SELON JSON.event CAS "INSERT": id est un entier = JSON.payload.new.id client est une chaîne = JSON.payload.new.client Trace("Nouvelle commande : " + client + " (" + id + ")") FIN
Benefícios para o seu negócio
A adoção do Supabase Realtime proporciona à arquitetura WinDev benefícios mais tangíveis: Redução de pesquisas: não há necessidade de consultar o banco de dados em intervalos fixos. Responsividade da interface: os usuários veem as alterações imediatamente. Simplicidade de implantação: do envio de notificações à manutenção. Evolução: mais clientes WinDev podem se conectar à mesma instância do Supabase.
Caso concreto: atualização de um painel. Quando o aplicativo é criado para o IsiNeva, você pode adicionar, modificar ou excluir um pedido, o que é refletido instantaneamente na interface do WinDev sem a necessidade de ação do usuário. O painel atual e a lista de pedidos também são acionados no retorno de chamada WS_SupabaseSocket_MessageReceived, o que é suficiente para garantir uma interface consistente em tempo real.
Conclusão
A integração do Supabase Realtime com o WinDev via WebSocketConnectSSL é uma solução moderna, eficiente e rápida para interfaces de negócios dinâmicas. Ela transforma um aplicativo clássico em um sistema interativo, sincronizado com seus servidores.
Na IsiNeva, essa abordagem permite que você libere suas ferramentas ultra-responsáveis, adaptando-se às demandas do terreno ou aos métodos de back-office, sem aumentar a carga do servidor ou da infraestrutura complexa.
Links externos
Documentação do Supabase Realtime https://supabase.com/docs/guides/realtime
Protocolo Phoenix Channels https://hexdocs.pm/phoenix/channels.html
WebSocketConnectSSL – Ajuda do PCSoft https://doc.pcsoft.fr/?1000021470
Websockets para colaboração em tempo real e trabalho (vídeo do PCSoft no YouTube)
Poc Windev Realtime GitHub https://github.com/LG-IsiNeva/Poc_Windev_Realtime
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | | | |
| | |
|