Objetivo desta tela
Exibir ao produtor um resumo dos dados cadastrados no ERP, o franqueado responsável pelo atendimento e as áreas cadastradas para aplicação, representadas por geocercas no mapa.
O app deve renderizar a geocerca usando os pontos do array poligono. Não deve receber
uma imagem pronta do mapa. O mapa deve ser montado dinamicamente no aplicativo.
Como os dados são atualizados?
A tela Seus Dados deve usar os dados salvos no banco offline local do app. A atualização desses dados acontece pela rotina de sincronização.
- O app chama
action=apiG10bio_produtoref=sincronizacao_offline. - O ERP identifica o produtor pelo
X-DEVICE-TOKEN. - A resposta retorna os dados atuais do produtor, vínculo com o franqueado e geocercas.
- O app salva ou atualiza esses dados no SQLite.
- A tela Seus Dados exibe sempre os dados locais.
Endpoint
POST
https://agroecologia.grupoekos.com.br/router/action.php
Headers obrigatórios
X-APP-KEY: <APP_KEY_FIXA_DO_APP>
X-DEVICE-TOKEN: <DEVICE_TOKEN_DO_APARELHO>
Body
action=apiG10bio_produtor
f=sincronizacao_offline
app_version=1.0.0
last_sync_at=YYYY-MM-DD HH:MM:SS
O app não deve enviar id_cliente nem id_franqueado. O ERP identifica o
produtor e o vínculo ativo pelo X-DEVICE-TOKEN.
Resposta esperada da API
{
"success": true,
"msg": "Sincronização realizada com sucesso.",
"data": {
"server_time": "2026-07-24 08:30:00",
"device_id": 123,
"cliente_id": 550,
"account_type": "produtor",
"id_franquia": 1,
"id_franqueado": 10,
"id_vinculo_produtor": 85,
"vinculo_atual": {
"situacao": "ATIVO",
"data_inicio": "2026-07-01",
"data_fim": null
},
"dados_produtor": {
"nome": "José da Silva",
"razao_social": "Sítio Modelo",
"documento": "000.000.000-00",
"telefone": "(18) 99999-9999",
"email": "produtor@exemplo.com",
"cidade": "Presidente Prudente",
"uf": "SP"
},
"dados_franqueado": {
"id_franqueado": 10,
"nome_franqueado": "G10 Bio Presidente Prudente",
"telefone": "(18) 99999-0000",
"email": "franqueado@exemplo.com",
"cidade_base": "Presidente Prudente",
"uf_base": "SP"
},
"geocercas": [
{
"id_geocerca": 5,
"descricao": "Talhão principal",
"total_pontos": 4,
"centro": {
"latitude": -21.853900,
"longitude": -47.479800
},
"poligono": [
{ "latitude": -21.853901, "longitude": -47.479801, "ordem": 1 },
{ "latitude": -21.853950, "longitude": -47.479700, "ordem": 2 },
{ "latitude": -21.854020, "longitude": -47.479750, "ordem": 3 },
{ "latitude": -21.854000, "longitude": -47.479850, "ordem": 4 }
],
"criado_em": "2026-07-20 10:00:00",
"atualizado_em": "2026-07-23 11:30:00"
}
]
}
}
Campos retornados — Dispositivo e vínculo
| Campo | Descrição |
|---|---|
device_id |
ID do aparelho registrado no ERP. |
device_name |
Nome/apelido do aparelho, informado no pareamento. |
account_type |
Tipo da conta autenticada. Neste aplicativo deve ser produtor.
|
Campos retornados — Produtor
| Campo | Descrição |
|---|---|
cliente_id |
ID do produtor cadastrado em clientes. |
dados_produtor.nome |
Nome do produtor. |
dados_produtor.razao_social |
Razão social ou nome da propriedade, quando disponível. |
dados_produtor.documento |
CPF ou CNPJ cadastrado no ERP. |
dados_produtor.telefone |
Telefone atualizado do produtor. |
dados_produtor.email |
E-mail cadastrado. |
dados_produtor.cidade / uf |
Localização principal do produtor. |
Campos retornados — Franqueado
| Campo | Descrição |
|---|---|
id_franquia |
ID da franquia responsável. |
id_franqueado |
ID do franqueado atualmente vinculado ao produtor. |
id_vinculo_produtor |
ID do vínculo ativo em franquias_franqueados_produtores. |
dados_franqueado.nome_franqueado |
Nome do franqueado responsável. |
dados_franqueado.telefone |
Telefone de contato do franqueado. |
dados_franqueado.email |
E-mail de contato. |
dados_franqueado.cidade_base / uf_base |
Cidade-base de atendimento do franqueado. |
Campos retornados — Geocercas
| Campo | Descrição |
|---|---|
geocercas[].id_geocerca |
ID da geocerca no ERP. |
geocercas[].descricao |
Descrição da área, exemplo: Área destinada para aplicação. |
geocercas[].total_pontos |
Total de pontos que formam o polígono. |
geocercas[].centro.latitude |
Latitude sugerida para centralizar o mapa. |
geocercas[].centro.longitude |
Longitude sugerida para centralizar o mapa. |
geocercas[].poligono |
Lista ordenada de pontos da geocerca. |
geocercas[].poligono[].latitude |
Latitude do ponto do polígono. |
geocercas[].poligono[].longitude |
Longitude do ponto do polígono. |
geocercas[].poligono[].ordem |
Ordem do ponto no desenho da geocerca. |
geocercas[].atualizado_em |
Data/hora da última alteração da geocerca no ERP. |
Como salvar no app
O app deve salvar o retorno no banco offline local. A tela Seus Dados deve ler primeiro os dados locais, para funcionar mesmo sem internet.
Estrutura sugerida no app
produtor_local
- id_cliente
- nome
- razao_social
- documento
- telefone
- email
- cidade
- uf
- updated_at
franqueado_local
- id_franquia
- id_franqueado
- id_vinculo_produtor
- nome_franqueado
- telefone
- email
- cidade_base
- uf_base
- situacao
- data_inicio
- data_fim
- updated_at
geocercas_local
- id_geocerca
- descricao
- total_pontos
- centro_latitude
- centro_longitude
- criado_em
- atualizado_em
geocerca_pontos_local
- id_geocerca
- latitude
- longitude
- ordem
Exibição da geocerca no React Native
Em React Native, o app normalmente deve usar um componente de mapa nativo, como
react-native-maps. O app deve transformar o array poligono em coordenadas
e passar para o componente Polygon.
Instalação recomendada
npm install react-native-maps
Tipos TypeScript sugeridos
type DeviceData = {
device_id: number;
device_name: string;
account_type: 'produtor' | 'produtor_funcionario' | 'colaborador';
};
type ClienteData = {
id_cliente: number;
razao_social: string;
cidade: string;
uf: string;
segmento: string;
variedade_planta: string;
porta_enxerto: string;
idade_pomar: string;
qtde_planta: string;
capacidade_tanque: string;
qtde_tanque_aplic: string;
qtde_planta_tanque: string;
area_irrigada: string;
plano_manejo: string;
};
type G10Data = {
id_g10: number;
id_fase: number;
fase: string;
status: number;
};
type GeoPoint = {
latitude: number;
longitude: number;
ordem: number;
};
type GeocercaData = {
id_geocerca: number;
descricao: string;
total_pontos: number;
centro: {
latitude: number;
longitude: number;
} | null;
poligono: GeoPoint[];
criado_em: string;
atualizado_em: string;
};
type MeusDadosGeocercaResponse = {
success: boolean;
msg: string;
data: {
device: DeviceData;
cliente: ClienteData;
g10: G10Data;
geocercas: GeocercaData[];
};
};
Exemplo de consumo da API
type ApiConfig = {
appKey: string;
deviceToken: string;
};
export async function sincronizarDadosProdutor(config: ApiConfig) {
const body = new URLSearchParams();
body.append('action', 'apiG10bio_produtor');
body.append('f', 'sincronizacao_offline');
body.append('app_version', '1.0.0');
const response = await fetch('https://agroecologia.grupoekos.com.br/router/action.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-APP-KEY': config.appKey,
'X-DEVICE-TOKEN': config.deviceToken,
},
body: body.toString(),
});
const json = await response.json() as MeusDadosGeocercaResponse;
if (!response.ok || !json.success) {
throw new Error(json.msg || 'Erro ao sincronizar dados do produtor.');
}
return json.data;
}
Exemplo de componente React Native
import React from 'react';
import { View, Text } from 'react-native';
import MapView, { Polygon, PROVIDER_GOOGLE } from 'react-native-maps';
type GeoPoint = {
latitude: number;
longitude: number;
ordem: number;
};
type GeocercaData = {
id_geocerca: number;
descricao: string;
total_pontos: number;
centro: {
latitude: number;
longitude: number;
} | null;
poligono: GeoPoint[];
};
type Props = {
geocerca: GeocercaData;
};
export function MapaGeocerca({ geocerca }: Props) {
if (!geocerca || !geocerca.centro || geocerca.poligono.length < 3) {
return (
<View>
<Text>Área de aplicação ainda não cadastrada.</Text>
</View>
);
}
const coordinates = [...geocerca.poligono]
.sort((a, b) => a.ordem - b.ordem)
.map((p) => ({
latitude: Number(p.latitude),
longitude: Number(p.longitude),
}));
return (
<MapView
provider={PROVIDER_GOOGLE}
mapType="satellite"
style={{ width: '100%', height: 320 }}
initialRegion={{
latitude: geocerca.centro.latitude,
longitude: geocerca.centro.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
>
<Polygon
coordinates={coordinates}
strokeColor="red"
fillColor="rgba(255, 255, 0, 0.35)"
strokeWidth={2}
/>
</MapView>
);
}
ordem antes de renderizar o polígono.
Observação sobre Google Maps
Em páginas web, o Google Maps é carregado com a tag abaixo:
<script src="https://maps.googleapis.com/maps/api/js?key=<AQUI>&v=weekly&language=pt-BR&libraries=drawing"></script>
Em React Native, essa tag <script> normalmente não é usada diretamente.
O app deve usar o SDK nativo do Google Maps através de uma biblioteca como
react-native-maps. A chave do Google Maps deve ser configurada no projeto Android/iOS,
conforme a biblioteca usada pelo desenvolvedor.
A biblioteca drawing é usada no ERP/web para desenhar e editar polígonos.
No app mobile, a necessidade principal é apenas exibir a geocerca já cadastrada.
Exemplo de chamada cURL
curl -i -X POST "https://agroecologia.grupoekos.com.br/router/action.php" \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
-H "X-APP-KEY: APP_KEY_FIXA_DO_APP" \
-H "X-DEVICE-TOKEN: dev_xxxxxxxxxxxxxxxxx" \
--data "action=apiG10bio_produtor&f=sincronizacao_offline&app_version=1.0.0"
Erros comuns
missing_app_key: o headerX-APP-KEYnão foi enviado.invalid_api_key: a chave do app é inválida.missing_device_token: o headerX-DEVICE-TOKENnão foi enviado.device_token_not_found: o aparelho não está pareado ou o token é inválido.vinculo_inativo: o produtor não possui vínculo ativo com um franqueado.produtor_not_found: o produtor vinculado ao aparelho não foi localizado.