7.8 KiB
Arquitetura — SARAndroid
Resumo Executivo
SAR Android (Sistema de Atendimento ao Representante) é um app Android legado em Java para representantes comerciais. Permite gerenciar clientes, produtos e pedidos completamente offline, sincronizando periodicamente com um servidor PostgreSQL via JDBC direto. O app opera em modo offline-first: toda interação do usuário usa SQLite local; a sincronização com o servidor é explicitamente iniciada pelo usuário.
Stack de Tecnologia
| Camada | Tecnologia | Versão |
|---|---|---|
| Linguagem | Java | (sem Kotlin) |
| Plataforma | Android | Min SDK 19 / Target SDK 35 |
| Build | Eclipse ADT | pré-Gradle |
| Banco local | SQLite via SQLiteOpenHelper | schema v40 |
| Banco remoto | PostgreSQL via JDBC direto | 8.x |
| Driver JDBC | postgresql-8.2-512.jdbc3.jar | 8.2 JDBC3 (fixo) |
| Data/Hora | Joda-Time | 2.5 |
| FTP | Apache Commons Net | 3.3 |
| UI compat | android.support.v4 | (support library legada) |
Padrão Arquitetural: Offline-First com Dual-Database
┌─────────────────────────────────────────────┐
│ DISPOSITIVO │
│ │
│ Activities / Fragments │
│ ↕ (UI Thread) │
│ Value Objects (vo/) │
│ ↕ │
│ DAOs SQLite (*DB.java / *BD.java) │
│ ↕ │
│ SQLite Local (jcsinformatica.sar v40) │
│ │
└──────────────────────┬──────────────────────┘
│ ComunicaActivity
│ (sync explícita, background thread)
↓
┌─────────────────────────────────────────────┐
│ SERVIDOR │
│ │
│ DAOs PostgreSQL (*PGSQL.java) │
│ ↕ │
│ ConnectionManager (JDBC, timeout 20s) │
│ ↕ │
│ PostgreSQL (ERP central) │
│ │
└─────────────────────────────────────────────┘
Fluxo de Entrada
SplashScreen
└→ LoginActivity (valida senha local, carrega Empresa no Global)
├→ ComunicaActivity (se última sync > 7 dias → força sync total)
└→ MainActivity (menu expandable)
├→ Pedidos
│ ├→ UpdatePedidoActivity (criar/editar pedido)
│ │ ├── FlexPedidoFragment
│ │ ├── ItensPedidoFragment
│ │ │ └→ UpdatePedItemActivity (editar item)
│ │ └── TotalPedidoFragment
│ └→ BrowsePedido (listar pedidos)
├→ Produtos
│ ├→ BrowseProduto
│ ├→ UpdateProduto
│ └→ FotosProduto
├→ Clientes
│ ├→ BrowseCliente
│ ├→ UpdateCliente
│ └→ BrowseCTR (contas a receber)
├→ Consulta
│ ├→ BrowsePedidoConsulta
│ └→ ConsultaVendasActivity
└→ Comunicação
└→ ComunicaActivity (sync manual)
Classes Centrais
Global (singleton estático)
Mantém o estado de runtime da sessão:
Global.empresa—Empresaativa (incluiRepresentante, configs de conexão)Global.pedido—Pedidoem edição (null fora do contexto de pedido)Global.pedItem—ItemPedidoem edição
⚠️
Global.getEmpresa()lançaWarningExceptionseempresa == null. Sempre use try/catch.
GlobalActivity (base Activity pós-login)
Lê Global.getEmpresa() no onCreate() e configura o título da tela.
Toda Activity pós-login deve estender GlobalActivity.
GlobalActivityFragment (base para Activities com Fragments)
Estende FragmentActivity da support library v4. Usada por UpdatePedidoActivity.
DatabaseHelper (SQLiteOpenHelper)
- DB name:
jcsinformatica.sar - Versão atual: 40
PRAGMA foreign_keys = ONativado emonOpen()onUpgrade()com guardsif (oldVersion < N)para cada versão
ConnectionManager (JDBC)
getConnection(Context, Config)— cria conexão PostgreSQL com timeout 20scloseAll(conn, stmt, rs)— sempre usar emfinally
ComunicaActivity (orquestrador de sync)
- Único ponto de sincronização com o servidor
- Roda em
Threadbackground (AtualizaDados) - Usa
postUserFeedback()para atualizar progresso na UI - Mantém
WakeLockdurante a sync
Padrão DAO Dual
Cada entidade sincronizável tem dois DAOs:
| Classe | Banco | Responsabilidade |
|---|---|---|
*DB.java / *BD.java |
SQLite | CRUD local + detecção de mudanças por MD5 |
*PGSQL.java |
PostgreSQL | Leitura/escrita remota durante sync |
Padrão de detecção de mudanças:
// No DAO SQLite (ex: ClienteDB)
SparseArray<String> arrayMd5 = selectIdMd5(db); // carrega id_erp → md5
for (Cliente c : clientesDoServidor) {
String md5Local = arrayMd5.get(c.getIdErp());
if (md5Local == null) insert(c, db); // novo
else if (md5Local.equals(c.getMd5())) continue; // sem mudança
else update(c, db); // atualizado
}
Lógica de Negócio em Pedido.java
A classe Pedido contém lógica de negócio significativa (não apenas VO):
| Método | Descrição |
|---|---|
calcTotal(context) |
Recalcula descontos, comissões e totais de todos os itens |
calcComissao(context, item) |
Calcula comissão por item considerando desconto de rateio |
calcIpi(context, item) |
Calcula IPI por item |
calcIcmsSt(context, item) |
Calcula ICMS-ST por item (modal 5=pauta, MVA) |
calcDescPauta(context) |
Aplica descontos de pauta por faixa de peso/volume |
getValidaPedidoMinimo(context) |
Valida preço mínimo por produto |
getTotalDesconto() |
Calcula e atribui vl_liquido em cada item |
⚠️
calcTotal()tem efeito colateral: modifica osItemPedidointernos. Deve ser chamado antes de salvar o pedido.
Padrão de UI
- ListView +
SimpleArrayAdapter*customizado para todas as listas - ViewPager + Fragments para tela de pedido (
UpdatePedidoActivity) - ExpandableListView para menu principal (
MainActivity) - Buscas em
Threaddedicada (ThreadBusca*.java) - Erros exibidos via
Util.sendError(activity, exception)→ AlertDialog
Geração de PDF
Util contém gerarPdf() que usa android.graphics.pdf.PdfDocument (API 19+) para gerar PDF do pedido e compartilhar via FileProvider.
Permissões
| Permissão | Uso |
|---|---|
| INTERNET | JDBC PostgreSQL + FTP |
| ACCESS_NETWORK_STATE | Verificar conectividade antes da sync |
| WRITE/READ_EXTERNAL_STORAGE | Fotos de produtos e PDF |
| WAKE_LOCK | Manter dispositivo ativo durante sync longa |
| VIBRATE | Feedback ao concluir sync |
Sem Testes Automatizados
O projeto não tem infraestrutura de testes. Não há test/ ou androidTest/. Validação é exclusivamente manual via dispositivo/emulador.