DS_PATHDOWNAPKCOLETALEITE — Caminho do APK da Coleta de Leite para Download
Tela de configuração: MFA001 — Parâmetros de Faturamento Localização: Parâmetros → aba Pedido Mobile → grupo Caminho do Apk Coleta Leite Tabela:
PARMFATUR.DS_PATHDOWNAPKCOLETALEITETipo: Texto — caminho absoluto do arquivo.apkno servidor (DOM_VARCHAR120, nullable) —TDBMemoReadOnly preenchido viaTOpenDialogValor padrão: vazio/NULL — o campo não é inicializado emtabPARMFATURAfterInsert(verUdmMFA001.paslinhas 434-606) e o DDL não possuiDEFAULT(verdocs/schema/tables/PARMFATUR.sqllinha 216)
Telas impactadas
O parâmetro é apenas configurado na MFA001. Quem o consome em runtime é a API REST da Coleta de Leite (projeto Java CaptacaoMobile), responsável por servir o APK aos dispositivos Android via HTTP — a tela ERP Desktop não lê este campo em nenhum outro módulo além da própria MFA001.
| Módulo / Componente | Código | Papel |
|---|---|---|
| Parâmetros de Faturamento | MFA001 | Tela produtora — grava o caminho absoluto do arquivo .apk |
API REST Coleta Leite — DispositivoMovelDAO | source/Server/CaptacaoMobile/CaptacaoMobileAS | Consumidor — lê PARMFATUR.DS_PATHDOWNAPKCOLETALEITE no SELECT que carrega o dispositivo móvel e injeta em DispositivoMovel.setPathDownload(...) |
API REST Coleta Leite — DownloadResource (servlet /download/*) | source/Server/CaptacaoMobile/CaptacaoMobileAS | Consumidor — abre new File(dispositivoMovel.getPathDownload()), transmite os bytes do APK no response HTTP (Content-Disposition: attachment) |
Observação — Homônimo: há um campo similar
PARMFATUR.DS_PATHAPKDOW(também editado na MFA001, na mesma aba), porém destinado ao APK do Pedido Mobile e consumido por outra API (ERPPedidoRestAS). Não confundir:DS_PATHDOWNAPKCOLETALEITEé exclusivamente o APK da Coleta de Leite (módulo CaptacaoMobile).
Procedures impactadas
Não há procedures/views/triggers que referenciem DS_PATHDOWNAPKCOLETALEITE. O campo é acessado apenas por SELECT direto no Java (DispositivoMovelDAO.BUSCA_DISPOSITIVO) e pelo CRUD da MFA001 (INSERT/UPDATE/FetchRow).
Campos envolvidos
| Campo | Descrição |
|---|---|
PARMFATUR.DS_PATHDOWNAPKCOLETALEITE | Este parâmetro. Caminho absoluto (no servidor onde o WildFly está publicado) do arquivo .apk da Coleta de Leite — DOM_VARCHAR120 |
PARMFATUR.DS_PATHAPKDOW | Campo irmão, na mesma aba da MFA001 — caminho do APK do Pedido Mobile (outra API) |
DISPOSITIVOMOVEL.IDEMPRESA | FK usada no INNER JOIN do DAO (PF.CD_EMPRESA = DM.IDEMPRESA) — o path retornado é o da empresa do dispositivo que se autenticou |
Comportamento / Uso
1. Configuração na MFA001 (aba Pedido Mobile)
A aba pagPedidoMobile (Caption=Pedido Mobile) possui um TGroupBox "Caminho do Apk Coleta Leite" com:
mmDS_PATHDOWNAPKCOLETALEITE: TDBMemo— somente-leitura (ReadOnly = True),MaxLength = 240, vinculado ao campo viaDataField = 'DS_PATHDOWNAPKCOLETALEITE'.btnDS_PATHDOWNAPKCOLETALEITE: TBitBtn— Caption "Selecione Caminho do APK Coleta Leite", Hint "Abrir Arquivo" — chamaTOpenDialoge grava oFileNameescolhido no campo.
Código responsável pelo clique no botão (source/MFA001/UMFA001.pas linhas 2499-2510):
procedure TfrmMFA001.btnDS_PATHDOWNAPKCOLETALEITEClick(Sender: TObject);
begin
AbrirArquivo.Filter := 'Aplicativos (*.apk)|*.APK';
AbrirArquivo.Title := 'Selecione o APk da Coleta Leite';
dmMFA001.tabPARMFATUR.Edit;
if (AbrirArquivo.Execute) then
dmMFA001.tabPARMFATURDS_PATHDOWNAPKCOLETALEITE.AsString := AbrirArquivo.FileName
else
dmMFA001.tabPARMFATURDS_PATHDOWNAPKCOLETALEITE.AsString := '';
end;
Atenção: ao cancelar o
TOpenDialogo campo é zerado (string vazia). Para preservar o caminho existente, o usuário deve evitar clicar no botão.
Simulação passo a passo
- Copie o arquivo do APK da Coleta de Leite para o servidor onde a API
CaptacaoMobileASestá publicada (mesmo host do WildFly), ex.:C:\Junsoft\Mobile\captacaomobile.apk. - Abra MFA001 → aba Pedido Mobile.
- Clique em Selecione Caminho do APK Coleta Leite (
btnDS_PATHDOWNAPKCOLETALEITE). - No filtro "Aplicativos (*.apk)", selecione o arquivo → o caminho completo é gravado em
mmDS_PATHDOWNAPKCOLETALEITE(somente-leitura,MaxLength=240). - Salve (F6) — a
PARMFATURrecebeUPDATE ... SET DS_PATHDOWNAPKCOLETALEITE = :DS_PATHDOWNAPKCOLETALEITE.
Resultado esperado: caminho absoluto armazenado em PARMFATUR.DS_PATHDOWNAPKCOLETALEITE. No próximo download solicitado pelo app Android de Coleta de Leite (servlet /download/<idDispositivo>), o APK passa a ser transmitido a partir desse caminho.
2. Consumo pela API Coleta Leite (DispositivoMovelDAO)
Ao autenticar/carregar o dispositivo móvel (DispositivoMovelDAO.load(idDispositivoMovel)), o DAO lê o campo diretamente no SELECT, fazendo INNER JOIN entre DISPOSITIVOMOVEL e PARMFATUR pela empresa do dispositivo, e guarda o caminho no objeto de modelo (DispositivoMovel.pathDownload):
source/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/DAO/DispositivoMovelDAO.java — linhas 22-31 e 67:
private static final String BUSCA_DISPOSITIVO
= "SELECT "
+ " DM.ID, DM.IDEMPRESA, U.CD_USUARIO, "
+ " DM.DTULTIMASINC, DM.IDMOTORISTA, "
+ " PF.DS_PATHDOWNAPKCOLETALEITE, DM.NRPAGESIZE, STOBRIGAGPSPEDIDO "
+ "FROM DISPOSITIVOMOVEL DM "
+ "INNER JOIN USUARIO U ON (U.ID = DM.IDUSUARIO) "
+ "INNER JOIN PARMFATUR PF ON(PF.CD_EMPRESA = DM.IDEMPRESA) "
+ "WHERE DM.ID = ? "
+ " AND DM.TPDISPOSITIVO = 'L' ";
...
dispositivoMovel.setPathDownload(rs.getString("DS_PATHDOWNAPKCOLETALEITE"));
Observação — multi-empresa: diferente do
DS_PATHAPKDOW(que fica em enum singleton JVMPathServidor.INSTANCE), aqui o path é resolvido por empresa a cada chamada, pois é carregado no objetoDispositivoMovelretornado pelo DAO. Não há cache global: cada dispositivo recebe o APK da própria empresa (DM.IDEMPRESA). O filtroDM.TPDISPOSITIVO = 'L'restringe a consulta a dispositivos do tipo "L" (Leite).
3. Download do APK (DownloadResource — servlet /download/*)
Quando o app Android de Coleta de Leite chama GET /download/<idDispositivoMovel>, o servlet DownloadResource busca o dispositivo via DispositivoMovelNegocio.retornaDispositivoMovel(...) e transmite o arquivo apontado pelo path.
source/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/services/DownloadResource.java — linhas 68-127:
String[] path = request.getRequestURI().split("/");
String idDispositivo = path[path.length - 1];
DispositivoMovel dispositivoMovel = dispositivoMovelNegocio
.retornaDispositivoMovel(Integer.valueOf(idDispositivo));
if (dispositivoMovel.getPathDownload() == null) {
String[] ocurrences = {String.valueOf(dispositivoMovel.getEmpresa().getNmEmpresa())};
Logger.getLogger(DispositivoMovelDAO.class.getName()).log(Level.SEVERE,
MensagemEnum.PATHDOWNLOAD_NAOCADASTRADO.replaceOcurrences(ocurrences),
MensagemEnum.PATHDOWNLOAD_NAOCADASTRADO.replaceOcurrences(ocurrences));
throw new ServletException(
MensagemEnum.DISPOSITIVO_NAOCADASTRADO.replaceOcurrences(ocurrences));
} else {
String filePath = dispositivoMovel.getPathDownload();
File downloadFile = new File(filePath);
FileInputStream inStream = new FileInputStream(downloadFile);
String mimeType = context.getMimeType(filePath);
if (mimeType == null) { mimeType = "application/octet-stream"; }
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
response.setHeader("Content-Disposition",
String.format("attachment; filename=\"%s\"", downloadFile.getName()));
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
}
Fluxo do servlet:
- Extrai o
idDispositivodo último segmento do URI (/download/<idDispositivo>). - Chama
DispositivoMovelNegocio.retornaDispositivoMovel(...)que internamente executaDispositivoMovelDAO.load(...)— e com issopathDownloadfica populado a partir dePARMFATUR.DS_PATHDOWNAPKCOLETALEITE. - Se
getPathDownload() == null: logaPATHDOWNLOAD_NAOCADASTRADO(com o nome da empresa) e lançaServletException(vira HTTP 500 para o cliente). - Se preenchido: abre o arquivo em
new File(filePath)e transmite os bytes ao cliente comContent-Disposition: attachment; filename="<nomeArquivo>". - O MIME type é resolvido pelo
ServletContexte faz fallback paraapplication/octet-stream.
Simulação passo a passo (lado dispositivo)
- Garanta que
PARMFATUR.DS_PATHDOWNAPKCOLETALEITEaponta para um arquivo existente no servidor (ex.:C:\Junsoft\Mobile\captacaomobile.apk). - No app Android de Coleta de Leite (ou via qualquer cliente HTTP), acione
GET http://<servidor>:<porta>/CaptacaoMobile/download/<idDispositivoMovel>. - A API carrega o
DispositivoMovel(que resolvepathDownloadnoINNER JOIN PARMFATUR), abre o arquivo e transmite os bytes comoattachment.
Resultado esperado: o APK é enviado ao cliente com Content-Disposition: attachment (nome = último segmento do path). Se DS_PATHDOWNAPKCOLETALEITE estiver em branco/NULL, o servlet retorna HTTP 500 com mensagem PATHDOWNLOAD_NAOCADASTRADO (nome da empresa no log). Se o caminho existir mas apontar para arquivo inexistente, o new File(...)/FileInputStream lança FileNotFoundException, que também sobe como HTTP 500.
Formato esperado
| Regra | Detalhe |
|---|---|
| Tipo de dado (DDL) | DS_PATHDOWNAPKCOLETALEITE DOM_VARCHAR120 — coluna nullable, sem DEFAULT (ver docs/schema/tables/PARMFATUR.sql linha 216). Limite de 120 caracteres no banco |
| Tipo de dado (FireDAC) | TStringField com Size = 120 em UdmMFA001.dfm linhas 1918-1922 |
| Componente na tela | TDBMemo ReadOnly com MaxLength = 240 — inconsistente com o Size=120 do banco: o componente permitiria digitar até 240 caracteres, mas o banco trunca/rejeita acima de 120 (na prática o campo é preenchido por TOpenDialog, então o usuário não digita) |
| Como é preenchido | Clique em btnDS_PATHDOWNAPKCOLETALEITE → TOpenDialog (filtro "Aplicativos (.apk) |
| Cancelar o diálogo | Apaga o caminho (grava string vazia). Para preservar, o usuário deve não clicar no botão |
| Caminho absoluto vs. relativo | O TOpenDialog retorna caminho absoluto. O servlet DownloadResource passa o valor direto para new File(filePath) — precisa ser um caminho válido no servidor do WildFly/API, não na estação do cliente que salvou |
| Extensão esperada | Filtro do diálogo aceita apenas *.apk (case-insensitive). Tecnicamente o campo é texto livre — qualquer caminho pode ser gravado; o servlet apenas tenta abrir como arquivo |
| Validação no AfterPost/OnValidate | Não há. O campo não possui evento OnValidate. Um caminho inválido só é detectado no download (HTTP 500 no servlet). Campo NULL dispara mensagem PATHDOWNLOAD_NAOCADASTRADO |
| Multi-empresa | O DAO faz INNER JOIN PARMFATUR ... ON (PF.CD_EMPRESA = DM.IDEMPRESA). Cada empresa configura seu próprio APK. Não há cache global — cada chamada resolve o path da empresa do dispositivo |
| Ambiente | O caminho deve ser resolvível pelo processo Java do WildFly. Caminhos UNC (\\SERVIDOR\Share\app.apk) funcionam se o serviço tiver permissão de rede; caminhos locais (C:\...) precisam existir na própria máquina do servidor |
| Filtro do dispositivo | Apenas dispositivos com DISPOSITIVOMOVEL.TPDISPOSITIVO = 'L' (Leite) são retornados pelo DAO. Dispositivos de outros tipos não acessam este path |
Referências no Código Fonte
Tela MFA001 (configuração)
source/MFA001/UMFA001.dfmlinhas 12078 (abapagPedidoMobile, Caption='Pedido Mobile'), 12137-12192 — grupogrpDS_PATHDOWNAPKCOLETALEITE(Caption=' Caminho do Apk Coleta Leite '),mmDS_PATHDOWNAPKCOLETALEITE: TDBMemo(ReadOnly, MaxLength=240, DataField='DS_PATHDOWNAPKCOLETALEITE'),btnDS_PATHDOWNAPKCOLETALEITE: TBitBtn(Caption='Selecione Caminho do APK Coleta Leite', Hint='Abrir Arquivo', OnClick=btnDS_PATHDOWNAPKCOLETALEITEClick)source/MFA001/UMFA001.paslinhas 528-530 — declaração degrpDS_PATHDOWNAPKCOLETALEITE,mmDS_PATHDOWNAPKCOLETALEITE,btnDS_PATHDOWNAPKCOLETALEITEsource/MFA001/UMFA001.paslinha 828 — assinaturaprocedure btnDS_PATHDOWNAPKCOLETALEITEClick(Sender: TObject);source/MFA001/UMFA001.paslinhas 2499-2510 —btnDS_PATHDOWNAPKCOLETALEITEClick: aplica filtro'Aplicativos (*.apk)|*.APK', título'Selecione o APk da Coleta Leite', executaTOpenDialoge grava/limpatabPARMFATURDS_PATHDOWNAPKCOLETALEITE.AsStringsource/MFA001/UdmMFA001.paslinha 273 —tabPARMFATURDS_PATHDOWNAPKCOLETALEITE: TStringFieldsource/MFA001/UdmMFA001.paslinhas 434-606 —tabPARMFATURAfterInsert: o campo não é inicializado (valor padrão = vazio/NULL)source/MFA001/UdmMFA001.dfmlinhas 286 (SELECT da lista de campos), 1918-1922 (definição doTStringFieldcomSize=120), 2440 (INSERT lista de colunas), 2612 (INSERT lista de parâmetros), 2866 (UPDATESET DS_PATHDOWNAPKCOLETALEITE = :DS_PATHDOWNAPKCOLETALEITE), 3166 (FetchRow) — CRUD do parâmetro
Consumidor — API REST Coleta Leite (Java / WildFly)
source/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/DAO/DispositivoMovelDAO.javalinhas 22-31 — constanteBUSCA_DISPOSITIVOcomSELECT ... PF.DS_PATHDOWNAPKCOLETALEITE ... FROM DISPOSITIVOMOVEL DM INNER JOIN USUARIO U ON (U.ID = DM.IDUSUARIO) INNER JOIN PARMFATUR PF ON(PF.CD_EMPRESA = DM.IDEMPRESA) WHERE DM.ID = ? AND DM.TPDISPOSITIVO = 'L'source/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/DAO/DispositivoMovelDAO.javalinha 67 —dispositivoMovel.setPathDownload(rs.getString("DS_PATHDOWNAPKCOLETALEITE"));source/Server/CaptacaoMobile/CaptacaoMobileModelo/src/main/java/br/com/junsoft/modelo/DispositivoMovel.javalinhas 24, 68-74 — atributoprivate String pathDownload;+ getter/settersource/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/services/DownloadResource.javalinhas 30-129 — servlet@WebServlet("/download/*"), extraiidDispositivodo URI, chamadispositivoMovelNegocio.retornaDispositivoMovel(...), validagetPathDownload() == null(lançaPATHDOWNLOAD_NAOCADASTRADO) e transmitenew File(filePath)comoattachment
DDL da tabela
docs/schema/tables/PARMFATUR.sqllinha 216 —DS_PATHDOWNAPKCOLETALEITE DOM_VARCHAR120,(nullable, semDEFAULT)