Pular para o conteúdo principal

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_PATHDOWNAPKCOLETALEITE Tipo: Texto — caminho absoluto do arquivo .apk no servidor (DOM_VARCHAR120, nullable) — TDBMemo ReadOnly preenchido via TOpenDialog Valor padrão: vazio/NULL — o campo não é inicializado em tabPARMFATURAfterInsert (ver UdmMFA001.pas linhas 434-606) e o DDL não possui DEFAULT (ver docs/schema/tables/PARMFATUR.sql linha 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 / ComponenteCódigoPapel
Parâmetros de FaturamentoMFA001Tela produtora — grava o caminho absoluto do arquivo .apk
API REST Coleta Leite — DispositivoMovelDAOsource/Server/CaptacaoMobile/CaptacaoMobileASConsumidor — 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/CaptacaoMobileASConsumidor — 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​

CampoDescrição
PARMFATUR.DS_PATHDOWNAPKCOLETALEITEEste parâmetro. Caminho absoluto (no servidor onde o WildFly está publicado) do arquivo .apk da Coleta de Leite — DOM_VARCHAR120
PARMFATUR.DS_PATHAPKDOWCampo irmão, na mesma aba da MFA001 — caminho do APK do Pedido Mobile (outra API)
DISPOSITIVOMOVEL.IDEMPRESAFK 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 via DataField = 'DS_PATHDOWNAPKCOLETALEITE'.
  • btnDS_PATHDOWNAPKCOLETALEITE: TBitBtn — Caption "Selecione Caminho do APK Coleta Leite", Hint "Abrir Arquivo" — chama TOpenDialog e grava o FileName escolhido 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 TOpenDialog o campo é zerado (string vazia). Para preservar o caminho existente, o usuário deve evitar clicar no botão.

Simulação passo a passo​

  1. Copie o arquivo do APK da Coleta de Leite para o servidor onde a API CaptacaoMobileAS está publicada (mesmo host do WildFly), ex.: C:\Junsoft\Mobile\captacaomobile.apk.
  2. Abra MFA001 → aba Pedido Mobile.
  3. Clique em Selecione Caminho do APK Coleta Leite (btnDS_PATHDOWNAPKCOLETALEITE).
  4. No filtro "Aplicativos (*.apk)", selecione o arquivo → o caminho completo é gravado em mmDS_PATHDOWNAPKCOLETALEITE (somente-leitura, MaxLength=240).
  5. Salve (F6) — a PARMFATUR recebe UPDATE ... 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 JVM PathServidor.INSTANCE), aqui o path é resolvido por empresa a cada chamada, pois é carregado no objeto DispositivoMovel retornado pelo DAO. Não há cache global: cada dispositivo recebe o APK da própria empresa (DM.IDEMPRESA). O filtro DM.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:

  1. Extrai o idDispositivo do último segmento do URI (/download/<idDispositivo>).
  2. Chama DispositivoMovelNegocio.retornaDispositivoMovel(...) que internamente executa DispositivoMovelDAO.load(...) — e com isso pathDownload fica populado a partir de PARMFATUR.DS_PATHDOWNAPKCOLETALEITE.
  3. Se getPathDownload() == null: loga PATHDOWNLOAD_NAOCADASTRADO (com o nome da empresa) e lança ServletException (vira HTTP 500 para o cliente).
  4. Se preenchido: abre o arquivo em new File(filePath) e transmite os bytes ao cliente com Content-Disposition: attachment; filename="<nomeArquivo>".
  5. O MIME type é resolvido pelo ServletContext e faz fallback para application/octet-stream.

Simulação passo a passo (lado dispositivo)​

  1. Garanta que PARMFATUR.DS_PATHDOWNAPKCOLETALEITE aponta para um arquivo existente no servidor (ex.: C:\Junsoft\Mobile\captacaomobile.apk).
  2. No app Android de Coleta de Leite (ou via qualquer cliente HTTP), acione GET http://<servidor>:<porta>/CaptacaoMobile/download/<idDispositivoMovel>.
  3. A API carrega o DispositivoMovel (que resolve pathDownload no INNER JOIN PARMFATUR), abre o arquivo e transmite os bytes como attachment.

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​

RegraDetalhe
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 telaTDBMemo 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 é preenchidoClique em btnDS_PATHDOWNAPKCOLETALEITE → TOpenDialog (filtro "Aplicativos (.apk)
Cancelar o diálogoApaga o caminho (grava string vazia). Para preservar, o usuário deve não clicar no botão
Caminho absoluto vs. relativoO 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 esperadaFiltro 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/OnValidateNã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-empresaO 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
AmbienteO 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 dispositivoApenas 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.dfm linhas 12078 (aba pagPedidoMobile, Caption='Pedido Mobile'), 12137-12192 — grupo grpDS_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.pas linhas 528-530 — declaração de grpDS_PATHDOWNAPKCOLETALEITE, mmDS_PATHDOWNAPKCOLETALEITE, btnDS_PATHDOWNAPKCOLETALEITE
  • source/MFA001/UMFA001.pas linha 828 — assinatura procedure btnDS_PATHDOWNAPKCOLETALEITEClick(Sender: TObject);
  • source/MFA001/UMFA001.pas linhas 2499-2510 — btnDS_PATHDOWNAPKCOLETALEITEClick: aplica filtro 'Aplicativos (*.apk)|*.APK', título 'Selecione o APk da Coleta Leite', executa TOpenDialog e grava/limpa tabPARMFATURDS_PATHDOWNAPKCOLETALEITE.AsString
  • source/MFA001/UdmMFA001.pas linha 273 — tabPARMFATURDS_PATHDOWNAPKCOLETALEITE: TStringField
  • source/MFA001/UdmMFA001.pas linhas 434-606 — tabPARMFATURAfterInsert: o campo não é inicializado (valor padrão = vazio/NULL)
  • source/MFA001/UdmMFA001.dfm linhas 286 (SELECT da lista de campos), 1918-1922 (definição do TStringField com Size=120), 2440 (INSERT lista de colunas), 2612 (INSERT lista de parâmetros), 2866 (UPDATE SET 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.java linhas 22-31 — constante BUSCA_DISPOSITIVO com SELECT ... 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.java linha 67 — dispositivoMovel.setPathDownload(rs.getString("DS_PATHDOWNAPKCOLETALEITE"));
  • source/Server/CaptacaoMobile/CaptacaoMobileModelo/src/main/java/br/com/junsoft/modelo/DispositivoMovel.java linhas 24, 68-74 — atributo private String pathDownload; + getter/setter
  • source/Server/CaptacaoMobile/CaptacaoMobileAS/src/main/java/br/com/junsoft/services/DownloadResource.java linhas 30-129 — servlet @WebServlet("/download/*"), extrai idDispositivo do URI, chama dispositivoMovelNegocio.retornaDispositivoMovel(...), valida getPathDownload() == null (lança PATHDOWNLOAD_NAOCADASTRADO) e transmite new File(filePath) como attachment

DDL da tabela​

  • docs/schema/tables/PARMFATUR.sql linha 216 — DS_PATHDOWNAPKCOLETALEITE DOM_VARCHAR120, (nullable, sem DEFAULT)