Pedido Sugerido por IA
Última atualização: 23 de setembro de 2025
Visão Geral
Sistema inteligente de Pedido Sugerido por IA que utiliza algoritmos para analisar padrões de compra, histórico de vendas e perfil do cliente para sugerir produtos relevantes. A solução oferece uma experiência completa de assistente virtual com análise de alertas, configuração de parâmetros personalizados e filtragem inteligente de produtos.
Interface do Assistente Virtual
O sistema apresenta uma interface moderna e intuitiva, com a assistente virtual Sofia que guia o usuário através do processo:
| Tela Inicial - Apresentação da Sofia | Sistema de Alertas |
|---|---|
![]() | ![]() |
| Apresentação da assistente virtual Sofia | Exibição de alertas e validações |
| Configuração de Parâmetros | Filtros por Categoria |
|---|---|
![]() | ![]() |
| Interface de configuração de parâmetros | Sistema de filtros por categoria |
Funcionalidades Principais
| Funcionalidade | Descrição |
|---|---|
| Sugestão Inteligente de Produtos | Algoritmo que analisa histórico e perfil do cliente |
| Sistema de Alertas | Validações automáticas de estoque, preços e descontos |
| Parâmetros Configuráveis | Ajuste de critérios de diferença de preço e desconto |
| Filtros por Categoria | Organização de produtos por tipo (promoções, comprados, etc.) |
| Modo Preload | Análise de produtos já selecionados no carrinho |
Estrutura do Componente
Arquivos Principais
| Arquivo | Responsabilidade |
|---|---|
SuggestedSaleModal.tsx | Componente principal do modal |
SuggestedSaleModalController.ts | Lógica de negócio e integração com API |
SuggestedSalePreloadModalController.ts | Controller para modo preload (produtos existentes) |
components/SuggestedSaleModalLoading.tsx | Tela de carregamento com animações |
components/SuggestedSaleModalAlert.tsx | Exibição e gerenciamento de alertas |
components/SuggestedSaleModalParameters.tsx | Configuração de parâmetros de sugestão |
components/SuggestedSaleModalFilters.tsx | Filtros de categorização de produtos |
components/AlertDetailsModal.tsx | Modal detalhado para visualizar alertas |
index.ts | Arquivo de exportação dos componentes |
Fluxo de Navegação
Fluxograma do Modal
Navegação entre Abas
| Etapa | Aba | Funcionalidade |
|---|---|---|
| Loading | - | Carregamento de dados da API |
| Alertas | Alertas | Exibição de validações e inconsistências |
| Parâmetros | Parâmetros | Configuração de critérios de sugestão |
| Filtros | Filtros | Seleção de categorias de produtos |
Integração com API
Endpoint de Sugestão
POST /mobile/suggested-sale
// Request Body
{
"CODPARC": number,
"CODVEND": number,
"ITEMS": ApiItemData[]
}
// Response
{
"success": boolean,
"data": {
"TPEMISNFE": number,
"CODPARC": number,
"ITEMS": ApiItemData[]
// ... outros campos do pedido
}
}
Estrutura de Dados da API
ApiItemData Interface
interface ApiItemData {
VLRSUBST: number; // Valor de substituição
QTDPENDENTE: number; // Quantidade pendente
VLRDESC: number; // Valor do desconto
VLRTOTWITHIMP: number; // Valor total com impostos
VLRTOT: number; // Valor total
VLRUNITWITHIMP: number; // Valor unitário com impostos
VLRUNIT: number; // Valor unitário
CODLOCALORIG: number; // Código do local origem
CODVOL: string; // Código do volume
VLRDESCTOT: number; // Valor total do desconto
CODPROD: number; // Código do produto
PRECOBASE: number; // Preço base
PERCDESC: number; // Percentual de desconto
QTDCORTE: number; // Quantidade de corte
PESOBRUTO: number; // Peso bruto
QTDFAT: number; // Quantidade faturada
PESOLIQ: number; // Peso líquido
AD_CODVOLEMB: string; // Código volume embalagem
SEQUENCIA: number; // Sequência
PERCDESCTGFDES: number; // Percentual desconto TGF
QTDENTREGUE: number; // Quantidade entregue
VLRIPI: number; // Valor IPI
DESCRLOCAL: string; // Descrição do local
CONTROLE: string; // Controle
QTDNEG: number; // Quantidade negociada
DESCRPROD: string; // Descrição do produto
NUTAB: number; // Número da tabela
}
Sistema de Alertas
Tipos de Alertas Suportados
| Tipo | Nível | Descrição | Ação |
|---|---|---|---|
| Produtos com Desconto | warning | Produtos que possuem desconto aplicado | Revisar descontos aplicados |
| Produtos sem Estoque | error | Produtos indisponíveis em estoque | Remover ou substituir produtos |
| Desconto Excedido | error | Desconto acima do limite permitido | Ajustar percentual de desconto |
| Produtos Inativos | warning | Produtos marcados como inativos | Verificar status dos produtos |
Algoritmo de Validação de Alertas
// Exemplo: Verificação de estoque
const validateStock = async (items: ApiItemData[]) => {
const itemsWithoutStock = [];
for (const item of items) {
const productStock = realm
.objects("ProductStock")
.filtered("CODPROD == $0", item.CODPROD);
if (productStock.isEmpty() || productStock[0].ESTOQUE <= 0) {
itemsWithoutStock.push(item);
}
}
return itemsWithoutStock;
};
Sistema de Parâmetros
Configurações Disponíveis
Seção 1: Diferença de Preço
| Opção | Descrição | Comportamento |
|---|---|---|
| Preço da Tabela | Manter preço original da tabela | Usa VLRUNIT original |
| Preço do Cliente | Aplicar preço específico do cliente | Busca preço na tabela do cliente |
Sistema de Filtros
Categorias de Produtos
| Categoria | Cor | Fonte de Dados | Descrição |
|---|---|---|---|
| Promoções | #FF6B35 | ProductData.PROMOCOES | Produtos em promoção ativa |
| Último Pedido | #4CAF50 | ClientLastProductsPurchased | Produtos do último pedido do cliente |
| Comprados | #2196F3 | ClientProductsPurchased | Histórico de produtos comprados |
| Destaques | #FFC107 | AsyncStorage: featuredProducts | Produtos em destaque da empresa |
| Perfil | #9C27B0 | AsyncStorage: profileProducts | Produtos do perfil do tipo de cliente |
| Sugerido | #607D8B | Produtos não categorizados | Sugestões baseadas em IA |
Algoritmo de Categorização
const categorizeProducts = async (items: ApiItemData[]) => {
const categories = await getFilters();
const filters: FilterItem[] = [];
// Promoções
const promotionsProducts = items
.filter((item) => categories.promotions.includes(item.CODPROD))
.map((item) => item.CODPROD);
if (promotionsProducts.length > 0) {
filters.push({
id: "promotions",
label: "Promoções",
count: promotionsProducts.length,
isSelected: true,
color: "#FF6B35",
type: "promotions",
products: promotionsProducts,
});
}
// ... outras categorias
return filters;
};
Modo Preload
Funcionalidade
O modo preload permite analisar produtos já selecionados no carrinho de compras, oferecendo validações e categorizações sem fazer nova chamada à API de sugestão.
Transformação de Dados
const transformProductToApiItemData = (product: Product): ApiItemData => {
return {
VLRSUBST: 0,
QTDPENDENTE: 0,
VLRDESC: parseFloat(product.VLRDESC || "0"),
VLRTOTWITHIMP: parseFloat(product.VLRTOT || "0"),
VLRTOT: parseFloat(product.VLRTOT || "0"),
VLRUNITWITHIMP: parseFloat(product.VLRUNIT || "0"),
VLRUNIT: parseFloat(product.VLRUNIT || "0"),
CODLOCALORIG: parseInt(product.CODLOCALORIG || "0", 10),
CODVOL: product.CODVOL || product.selectedCodVol || "",
// ... outros campos
CODPROD: product.CODPROD,
QTDNEG: parseFloat(product.QTDNEG || "0"),
DESCRPROD: product.DESCRPROD || "",
};
};
Seletor Redux para Preload
export const getItensPreloadFromProducts = (ids: string[]) =>
createSelector(
(state: RootState) => state.orderCreate?.products.entities,
(products) => {
const selectedProducts = ids
.map((id) => products?.[id])
.filter((product): product is Product => product !== undefined);
return transformProductsToApiItemData(selectedProducts);
}
);
Interfaces TypeScript
Props Principais
export interface SuggestedSaleModalProps {
visible: boolean;
onClose: () => void;
codParc: number | string;
itensPreload?: string[];
}
export interface AlertItem {
id: string;
type: "warning" | "error" | "info";
title: string;
description: string;
count?: number;
products?: any[];
}
export interface FilterItem {
id: string;
label: string;
count: number;
isSelected: boolean;
color: string;
type:
| "promotions"
| "ultimasVendas"
| "comprados"
| "destaques"
| "perfil"
| "ruptura"
| "sugerido"
| "corte"
| "notCategorized";
products: number[];
}
export interface ParameterSection {
id: string;
title: string;
description: string;
options: ParameterOption[];
}
export interface ParameterOption {
id: string;
label: string;
isSelected: boolean;
}
Hooks Personalizados
useSuggestedSaleModalController
Responsabilidade: Gerenciar estado e lógica para modo API completo
export const useSuggestedSaleModalController = (
codParc: number | string,
props: SuggestedSaleModalProps
) => {
// Estados
const [isLoading, setIsLoading] = useState(false);
const [alerts, setAlerts] = useState<AlertItem[]>([]);
const [parameters, setParameters] = useState<ParameterSection[]>([]);
const [filters, setFilters] = useState<FilterItem[]>([]);
// Funções
const fetchSuggestedSale = async () => {
/* ... */
};
const handleParameterChange = useCallback(() => {
/* ... */
}, []);
const handleFilterToggle = useCallback(() => {
/* ... */
}, []);
const handleConfirm = async () => {
/* ... */
};
return {
isLoading,
isDataReady,
alerts,
parameters,
filters,
error,
fetchSuggestedSale,
handleParameterChange,
handleFilterToggle,
handleConfirm,
resetState,
loadingSend,
};
};
useSuggestedSalePreloadModalController
Responsabilidade: Gerenciar estado para modo preload (produtos existentes)
export const useSuggestedSalePreloadModalController = (
codParc: number | string,
props: SuggestedSalePreloadModalProps,
ids: string[]
) => {
// Estados simplificados para modo preload
const [alerts, setAlerts] = useState<AlertItem[]>([]);
const [filters, setFilters] = useState<FilterItem[]>([]);
// Processamento automático de dados preload
const processPreloadedData = async () => {
/* ... */
};
return {
alerts,
filters,
filteredProducts,
isDataReady,
loadingSend,
handleFilterToggle,
handleConfirm,
resetState,
};
};
Integração com Redux
Actions e Selectors Utilizados
// Store Actions
import {
editOrderWithDynamicHeader,
resetOrderEditStatus,
} from "../../store/order-create.slice";
// Selectors
const orderEditStatus = useSelector(
(state: RootState) => state.orderCreate.orderEditStatus
);
const client = useSelector(
(state: RootState) => state?.clients?.entities?.[codParc]
);
const user = useSelector((state: RootState) => state.auth.user);
Fluxo de Confirmação do Pedido
const handleConfirm = async () => {
try {
setLoadingSend(true);
// Filtrar produtos selecionados
const selectedProducts = filters
.filter((f) => f.isSelected)
.map((f) => f.products)
.flat();
// Formatar dados para criação/edição do pedido
const orderData = formatterOrderContent(suggestedSaleData);
// Disparar ação Redux
dispatch(editOrderWithDynamicHeader(orderData));
} catch (error) {
console.error("Erro ao confirmar pedido:", error);
} finally {
setLoadingSend(false);
}
};
Animações e UX
Componente de Loading
// SuggestedSaleModalLoading.tsx
const SuggestedSaleModalLoading = ({ onClose }) => {
// Animações sofisticadas
const fadeInAnim = useRef(new Animated.Value(0)).current;
const slideUpAnim = useRef(new Animated.Value(50)).current;
const pulseAnim = useRef(new Animated.Value(1)).current;
const shimmerAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
// Animação de entrada (fade in + slide up)
Animated.parallel([
Animated.timing(fadeInAnim, {
toValue: 1,
duration: 800,
useNativeDriver: true,
}),
Animated.timing(slideUpAnim, {
toValue: 0,
duration: 800,
useNativeDriver: true,
}),
]).start();
}, []);
// ... renderização com animações
};
Estados de Loading
| Estado | Duração | Animação |
|---|---|---|
| Fade In | 800ms | Opacidade 0 → 1 |
| Slide Up | 800ms | Translate Y: 50px → 0 |
| Pulse | 2000ms | Scale: 1 → 1.05 → 1 (loop) |
| Shimmer | 1500ms | Translate X: -100% → 100% |
Configuração e Habilitação
Setting de Controle
// Verificação de habilitação do recurso
const settings = useSelector((state: RootState) => state?.settings);
{
settings?.VENDASUGERIDAIA === "S" && (
<AiActionsButton onPress={() => setSuggestedSaleModal(true)} />
);
}
Configuração no Sankhya
| Campo | Tabela | Descrição |
|---|---|---|
VENDASUGERIDAIA | TSIPAR | Habilita/desabilita o recurso ('S'/'N') |
Pontos de Integração
Telas que Utilizam o Componente
| Tela | Localização | Modo de Uso |
|---|---|---|
| Ações Rápidas (Home) | cs-home-quick-actions.tsx | API Mode (novo pedido) |
| Cabeçalho do Carrinho | selected-product-header.tsx | Preload Mode (produtos existentes) |
| Card do Cliente | cs-client.tsx | API Mode (novo pedido) |
Exemplo de Uso - Ações Rápidas
// cs-home-quick-actions.tsx
const [suggestedSaleModalVisible, setSuggestedSaleModalVisible] =
useState(false);
const [suggestedSaleModalCodParc, setSuggestedSaleModalCodParc] = useState<
number | null
>(null);
{
!!suggestedSaleModalVisible && !!suggestedSaleModalCodParc && (
<SuggestedSaleModal
visible={suggestedSaleModalVisible}
codParc={suggestedSaleModalCodParc?.toString?.()}
onClose={() => setSuggestedSaleModalVisible(false)}
/>
);
}
Exemplo de Uso - Carrinho Existente
// selected-product-header.tsx
const [suggestedSaleModal, setSuggestedSaleModal] = useState(false);
{
suggestedSaleModal && (
<SuggestedSaleModal
codParc={codParc}
visible={true}
onClose={() => setSuggestedSaleModal(false)}
itensPreload={props?.productIDs ?? []}
/>
);
}
Tratamento de Erros
Cenários de Erro Tratados
| Erro | Tratamento | Experiência do Usuário |
|---|---|---|
| Falha na API | Alert com mensagem de erro + fechamento | Modal fecha automaticamente |
| Dados Inválidos | Log de erro + fallback | Exibe tela vazia ou mensagem |
| Timeout de Requisição | Retry automático ou manual | Botão de tentar novamente |
| Produtos sem Estoque | Alert específico na tela de alertas | Orientação para remover produtos |
Implementação de Error Handling
useEffect(() => {
if (
visible &&
!isLoading &&
currentStep === "loading" &&
!props?.itensPreload
) {
if (error) {
console.error("API Error:", error);
Alert.alert("Erro", error, [
{
text: "OK",
onPress: handleClose,
},
]);
} else if (isDataReady) {
setCurrentStep("alert");
}
}
}, [visible, isLoading, isDataReady, currentStep, error, handleClose]);
Performance e Otimizações
Estratégias de Performance
| Estratégia | Implementação | Benefício |
|---|---|---|
| Lazy Loading | Carregamento sob demanda de componentes | Reduz bundle inicial |
| useCallback | Memoização de funções de callback | Evita re-renders desnecessários |
| useState Batch Updates | Agrupamento de atualizações de estado | Melhora responsividade |
| Selective Re-renders | useEffect com dependências específicas | Otimiza ciclo de renderização |
Memoização de Funções
const handleParameterChange = useCallback(
(sectionId: string, optionId: string) => {
setParameters((prev) =>
prev.map((section) =>
section.id === sectionId
? {
...section,
options: section.options.map((option) => ({
...option,
isSelected: option.id === optionId,
})),
}
: section
)
);
},
[]
);
const handleFilterToggle = useCallback(
(filterId: string) => {
setFilters((prev) =>
prev.map((filter) =>
filter.id === filterId
? { ...filter, isSelected: !filter.isSelected }
: filter
)
);
},
[filters, suggestedSaleData]
);
Dependências
Bibliotecas Principais
{
"react": "^18.0.0",
"react-native": "^0.72.0",
"react-native-vector-icons": "^10.0.0",
"react-native-responsive-fontsize": "^0.8.1",
"@reduxjs/toolkit": "^1.9.0",
"react-redux": "^8.0.0",
"axios": "^1.4.0",
"dayjs": "^1.11.0",
"@react-native-async-storage/async-storage": "^1.19.0",
"react-native-reanimated": "^3.0.0"
}
Recursos Externos
- Realm Database - Armazenamento local de dados
- API Sankhya - Integração com ERP
- Redux Store - Gerenciamento de estado global
- React Navigation - Navegação entre telas



