O xMova interpreta o modelo de dados para criar as telas do fluxo. No modelo define-se todas as entidades e seus campos. Em cada entidade e campo pode-se definir atributos e eventos.
O modelo é definido da seguinte forma:
Apontamento sync=in|out|beforeInstall
id inc
idSeq long uuid
data Now
campo1 int
campo2 String
events
beforeInsert
log Evento que ocorre antes de inserir um registro.
beforeSync
log Não sincroniza se retornar 0.
fieldEvents
onValidate
campo1
//Permite prosseguir apenas se o valor inserido for maior que zero.
return campo1 > 0
crudActions
MostrarResumo
show TelaResumoApontamento
Exemplo:
Entidade1 recursive
id int
name Str
idParent Entidade1
Exemplo de uso da máscara:
Boletim sync=out cleanupDays=20 notSavedMessage
id inc
idSeqBoletim uuid
mascaraCPF Long mask="999.999.999-99"
mascaraNumeral Long mask="9.9.9.9.9.9"
placaVeiculo Str mask="AAA-9999"
mascaraTelefoneCelular Str mask="(12)\99999-9999"
mascaraFicticia Str mask="99A*\?"
O xMova Server quando acessar a entidade do sistema de terceiro ou banco de dados, usará o próprio nome da entidade no JSON e SQL.
Note
Note a saída do JSON. O nome da entidade Serviço não foi alterado. Já o nome da entidade Frente, aparece no JSON como FrenteAG.
Exemplo de Entidade:
Servico
id inc
nome String
Frente
id inc
nome String
server name FrenteAG
JSON que será gerado para o sistema de terceiro durante o sync:
{"deviceUuid":"357517051399154",
"appCode":"E",
"intVersion":12,
"version":"1.0.11",
"auth":{"obra":76,"operador":177749,"celular":10495},
"types":[
{"version":0,"name":"Servico"},
{"version":0,"name":"FrenteAG"}
]
}
Para obter a quantidade de itens selecionados e não selecionados numa lista multi-select, o xmova usa um padrão de nomes de campos para definir os valores.
No campo que termina com Selecteds será definida a quantidade de itens selecionados.
No campo que termina com Unselecteds será definida a quantidade de itens não selecionados, ou seja, o total de itens na lista menos a quantidade de itens selecionados.
Exemplo:
Apontamento
id inc
funcionarios List<Funcionario>
funcionariosSelecteds int notFill
funcionariosUnselecteds int notFill
Num campo de checklist são armazenados os ids dos registros que foram selecionados. Para se obter os ids dos que não foram selecionados declara-se um outro campo com nome terminando com ‘UnselectedRecords’.
Exemplo:
Apontamento
id inc
funcionarios List<Funcionario>
funcionariosUnselectedsRecords List<Funcionario> notFill
Permite adicionar ações customizadas na tela de opções do crud
Exemplo:
Boletim sync=out cleanupDays=20 notSavedMessage
id inc
idSeqBoletim uuid
numero int notFill
crudActions
myCrudAction
actionLabel
return id
fill numero
Permite adicionar ações customizadas no menu de opções que pode ser exibido na tela de opções do crudo e nas telas de lista de entidades.
Exemplo:
Boletim sync=out cleanupDays=20 notSavedMessage
id inc
idSeqBoletim uuid
numero int notFill
options
myOption
actionLabel
return id
fill numero
Em cada entidade pode-se definir o tipo de sincronismo. Quando o app faz um sync com o servidor cada entidade será analisada para verificar se os dados dela serão enviados ou recebidos do servidor, ou se a entidade será ignorada no sync.
sync=in: O servidor retornará os dados desta entidade em todos os sync Foreground.
sync=out: Em todos os Sync, Foreground ou Background, os dados dessa entidade serão enviados ao servidor.
sync=in|out: O servidor retornará os dados desta entidade em todos os sync Foreground, e enviará os dados inseridos nela em todos os sync.
sync=backgroundIn: O servidor retornará os dados desta entidade em todos os sync, independente de ser Foreground ou Background.
sync=beforeInstall: O servidor retornará os dados desta entidade apenas no primeiro sync ANTES da instalação do app. Serve para fluxos em que seja necessário exibir lista de opções durante o processo de configuração inicial do fluxo.
No modelo de dados, pode-se definir um campo como tipo ‘now’ para que nesse campo seja armazenada a data e hora corrente quando cria-se um apontamento.
Quando o xmova armazena a data e hora corrente em um registro de apontamento, ela pode ser de 4 tipos. A letra do tipo é que será enviado ao sistema externo.
No fluxo pode-se definir que a data e hora não seja solicitada ao usuário, mas que seja pega a data e hora corrente do device. Quando isso ocorrer, o xmova não enviará para o servidor o tipo de data M Manual, mas sim enviará o tipo de data D Device.
Exemplo:
App appCode=R offlineDate=Device
No modelo, no campo que guarda a data e hora do tipow ‘now’, pode-se definir o nome de campo no sistema externo onde será informado o tipo de data que foi utilizada. Para isso use o atributo timeTypeField. Dessa forma, quando enviar o JSON para o sistema externo, o xMova Server enviará 2 campos, o campo do tipo now contendo a data e hora, que o desenvolvedor criou no modelo, e um segundo campo para informar o tipo de data e hora utilizada, que não faz parte da entidade no mobile, mas foi especificado no campo do tipo now.
Exemplo:
Apontamento
boletim Boletim
data now timeTypeField=tipoData
servico Servico
Exemplo de envio da data no JSON:
{"boletim":100,
"data":"11/07/2014 09:10:32",
"tipoData":"G",
"servico":200}
Para que as buscas de uma determinada entidade seja feita com performance máxima, basta ativar o gerenciamento da entidade em memória. O xMova manterá todos os registros da entidade em memória deixando a leitura dos dados muito rápida, pois não terá que lê-la do banco de dados.
Na primeira vez que uma entidade de memória for usada o xMova carregará os dados dela do banco em memória, e isso poderá demorar um pouco dependendo da quantidade de dados. Enquanto estiver lendo os dados do banco e colocando na memória, aparecerá uma tela bloqueando a tela informando ao usuário que aguarde o carregamento dos dados.
Para ativar o gerenciamento em memória de uma entidade, basta adicionar o atributo memory na entidade.
Exemplo de Entidade em Memória:
Servico sync=in memory
id inc
nome String
Frente
id inc
nome String
No exemplo acima, a entidade Servico será mantida em memória.
Essa seção descreve os passos necessários para alterar os dados de configuração do aplicativo sem a necessidade de realizar o procedimento de limpeza.
Primeiro é preciso criar a opção Trocar Líder, que ficará no menu da tela inicial.
No App:
Main main entity=AuthInput fields=descricaoObra,descricaoLider,descricaoCelular
actions
Boletim Ok
crud Boletim
TrocarLider
Integer numApontBoletim = rowCount table=Boletim_SyncOut
Integer numApontApontamento = rowCount table=ApontamentoFuncionario_SyncOut
if numApontBoletim + numApontApontamento + numApontFuncionarios + numApontLocation == 0
crud ChangeLider
else
toast Sincronize os Dados!
A opção TrocarLider deverá ser adicionada no actions do Main.
Qualquer alteração dos dados de configuração somente poderá ser feita se não houver dados offline.
Portanto é necessário realizar uma validação em todas as Entidades que realizam ´´sync=out´´ do aplicativo, confirmando se há algum registro aguardando para ser enviado.
Isso é realizado obtendo a quantidade de registros de todas as entidades do tipo _SyncOut do aplicativo. Deve-se então atribuir a um campo Integer o resultado do rowCount de cada Entidade. Por exemplo, para a Entidade Boletim foi criado o Integer numApontBoletim, que recebe o valor do rowCount da Entidade Boletim_SyncOut.
Após obter o rowCount de todas as entidades, é realizada uma validação, somando todos os valores.
Se o resultado for maior que zero, significa que existem dados gravados e a alteração dos dados não poderá ser realizada. Nesse caso uma mensagem será mostrada, pedindo para sincronizar os dados.
Se o valor for igual a zero, significa que não há dados gravados, e alteração poderá ser realizada. Nesse caso, um CRUD será executado. Esse CRUD é relacionado a uma Entidade que deverá ser definida no modelo, conforme exemplo abaixo.
No Model:
Auth
obra int
lider int
celular int
AuthInput notSavedMessage
obra obra
lider int
celular int
ChangeLider notSavedMessage
lider int
events
afterInsert
AuthInput authInput = selectFirst from AuthInput
authInput.lider = lider
save authInput
Auth auth = selectFirst from Auth
auth.lider = lider
save auth
dropTable AppEntityVer
dropTable Funcionario
exit
No exemplo acima, temos as entidades envolvidas nessa alteração: Auth, AuthInput e ChangeLider.
O Auth é a Entidade que contém as informações que serão enviadas no JSON de todos os apontamentos.
O AuthInput é a Entidade preenchida na primeira vez que o aplicativo é instalado.
O ChangeLider é a Entidade que será criada para alterar algum valor do Auth e AuthInput. Nesse exemplo, será alterado o líder.
Quando executado, o ChangeLider irá pedir o campo líder. Após o preenchimento desse campo, o evento afterInsert será executado.
O AuthInput é carregado e o valor de seu campo líder é alterado para o valor informado. O Auth é carregado e seu campo líder é alterado para o valor informado.
Se houver a necessidade de obter alguma Entidade após essa alteração, é preciso apagar os valores que estão no celular e obtê-los novamente.
Nesse exemplo, a lista de Funcionários vinculados a líder será apagada e obtida novamente. Portanto deve-se apagar a entidade de versões e a de Funcionários. Para isso utiliza-se o comando dropTable + o nome da entidade.
Por fim é executado o comando exit, que fecha o aplicativo. Isso é necessário para os dados sejam obtidos assim que for iniciado.
Danger
No update essas entidades Auth e AuthInput são mantidas para não ter que digitar os dados de instalação novamente a cada update. Se alterar os campos dessas entidades, visto que elas não são apagadas, o modelo tem campos diferentes dos campos da entidade, corrompendo os dados. Caso seja necessário alterar os campos das entidades Auth e AuthInput, será necessário desisntalar e fazer a instalação do fluxo novamente.
O xMova pode fazer validação online dos valores inseridos nos campos de uma entidade. Basta adicionar ao campo o atributo onlineValidate=obra.
Exemplo:
Boletim
boletim Boletim
data now timeTypeField=tipoData
codigoOperador int onlineValidate=operador
idOperador int notFill
nomeOperador String notFill
Danger
O xMova Server enviará para o sistema de terceiro o registro inteiro para validação. O sistema deve retornar novamente o sistema inteiro, com os mesmos dados que foram enviados, adicionando ao registro dados adicionais que foram validados. No exemplo acima, deverá ser retornado para o xMova os valores nos campos idOperador e nomeOperador, bem como todos os dados que foram enviados.
O sistema de terceiro deve retornar os seguites status:
Quando a validação retornar um status 1 (erro), o xMova mobile pega a mensagem de erro na seguinte ordem:
Quando a validação retornar um status diferente, ou seja >= 2 (falha), o xmova pega a mensagem na seguinte ordem:
O xMova pode ler codigo de barras de diversos tipo por meio da câmera do celular.
Para ler as informações de um QRCODE, deve ser alterado o APP do fluxo, definindo no reader a propriedade camera, que irá definir o comportamento da leitura. Para ler com QRCODE deve ser adicionado á propriedade camera o atributo type e sizes, onde type define o tipo de leitura( ex: QRCODE,CODE93,CODE128) e o sizes define o numero de caracteres que o QRCODE/BARCODE representa. Quando é definido um valor ao sizes, será feita a busca por códigos de barras, ou barcode, que representam aquele numero de caracteres definido, não reconhcendo QRCODE/BARCODE que possuam mais ou menos dígitos e os entendendo como invalidos. Caso não seja definido o atributo sizes, ele irá ler quaisquer QRCODE/BARCODE que lhe for lhe for apresentado independente de seu tamanho. segue um exemplo de como ficará no APP No exemplo a seguir foi definido no app que a funcionalidade de leitura somente reconhcerá QRCODES e que representem caracteres de 9 digitos.
Exemplo:
App appCode=LIDER periodicSync
id 555
name ConstruMobil
reader
camera types=QRCODE sizes=9 minQuality=20
Caso haja a necessidade de aceitar outras quantidades de caracteres, é necessário informá-las no atributo sizes.
No exemplo a seguir, o aplicativo aceita QRCODES de tamanho 5 e de tamanho 9.
Exemplo:
App appCode=LIDER periodicSync
id 555
name ConstruMobil
reader
camera types=QRCODE sizes=5,9
Para que um campo possa ser lido a partir de um QRCODE/BARCODE, deve ser definido o campo como long e adicionado ao campo o atributo reader com o valor (reader=cam). No exemplo a seguir mostra a definição de um campo(code) de uma entidade(TesteQRCode) no model, que será lido de um QRCODE que será lido pela câmera do celular e mostrado uma mensagem com o valor do campo lido.
Exemplo:
TesteQRCode sync=out
code long reader=cam
fieldEvents
onValidate
code
return confirm lang=custom.msgQRCode vars=qrcode:code
O celular que envia dados de cadastro sync=in para outro é chamado de Master. O que recebe os dados é chamado de Slave. No sincronismo via Bluetooth ou Http off-line ocorre tanto o envio dos dados off-line para o master, quanto o recebimento dos dados de cadastro do master para o slave, numa mesma requisição Bluetooth ou HTTP.
Para tornar um fluxo master, defina a linha master no App. No atributo appCodes você define quais os appCodes que esse master vai atender. Se um fluxo Slave com appCode=LIDER tentar enviar dados para um master que não definiu esse appCode, ele apenas conseguirá fazer o envio dos dados offline, mas não o recebimento dos dados sync=in.
Veja um exemplo de como definir um fluxo como Master:
App appCode=LIDER
id 555
name ConstruMobil
Main main
master appCodes=A,B
A
Servico slaveName=Serv
onInit
Equipamento equipamento = auth.equipamento
Familia familia = selectLast from Familia where id == :equipamento.familia
onFilter
Integer i = 0
if id in familia.servicos && familia.flagPrincipal == auth.familiaPrincipal
return true
Familia
onFilter
log filtrando...
No modelo do fluxo do Master é necessário definir a entidade auth de todos os fluxos de slaves aceitáveis. A definição deve seguir o padrão de nome AuthSlave_ + AppCode do slave. Exemplo: AuthSlave_L. Os campos dessa entidade devem ser idênticos aos campos da entidade auth original do Slave.
Para cada appCode definido no master, pode-se definir o evento onFilter para cada entidade. Dessa forma, o Master pode obter os dados completos de uma entidade do servidor, mas enviar apenas uma parte desses dados que interessem ao Slave. Esse evento será chamado para cada registro da entidade em que ele foi definido. São padrão no evento onFilter as variáveis record, auth, e todas as variáveis locais definidas no evento onInit.
É o registro que está sendo filtrado no momento. É também o context do evento, podendo ser acessados os campos diretamente. Em vez de digitar if record.familia == 5 pode-se usar diretamente o nome do campo if familia == 5.
É o registro inteiro, com todos os dados do auth do fluxo Slave. Esse registro auth é enviado pelo Slave no início da requisição. Dessa forma, pode-se filtrar os dados de acordo com alguma informação do auth do fluxo do Slave, enviando apenas dados que interesse a ele.
O evento onInit pode ser definido em cada entidade do Slave. Ele é executado antes de começar a percorrer os registros da entidade que será enviada. Todas as variáveis definidas nesse evento poderão ser usadas no evento onFilter como se fossem variáveis locais definidas lá no onFilter, pois os valores delas são copiados do evento onInit para o evento onFilter. Normalmente usa-se esse evento para pré-carregar registros do banco que serão usados massivamente no evento onFilter.
Para implementar o GPS no fluxo xMova é necessário adicionar a entidade Location no Modelo e configurar o location no App.
No Modelo:
Location sync=out
lat double
long double
alt double
gpsTime long
No App:
App
location minTime=1m minDistance=100 minLastTime=10m
Se definir minTime como 1h e minDistance 100m, os dois valores serão tratado como AND, ou seja, só atualizará quando os dois casos forem verdadeiros. Após 1h e tendo deslocado pelo menos 100m. Se passou 2h, mas andou somente 50m, a posição não será atualizada.
Se o minTime for 0 e o minDistance for 100m, o GPS será atualizado sempre que ultrapassar 100m, independente de horário.
Se o minTime for 1h e o minDistance for 0m, o GPS será atualizado a cada hora, independente da distância.
Para enviar a informação do GPS em uma entidade basta adicionar um campo do tipo Location em tal entidade.
No Modelo:
Boletim sync=out
id inc
idSeqBoletim Long
location Location inlineData
Apontamento sync=out
id inc
idSeqApontamento Long
location Location inlineData
Note
Utilize inlineData para que o registro de Location não fique separado do apontamento.
Exemplo do JSON que será criado nos apontamentos acima descritos:
Boletim
{
"id":1,
"idSeqBoletim":"1023043202340",
"location":
{
"lat": -23.1935728
"long": -45.8929331,
"alt": 0.0,
"gpsTime": 1406721814242,
}
}
Apontamento
{
"id":1,
"idSeqApontamento":"1023234402340",
"location":
{
"lat": -23.1935728
"long": -45.8929331,
"alt": 0.0,
"gpsTime": 1406721814242,
}
}
Para Tracking deve-se adicionar o atributo tracking no location do app, e adicionar minDistance = 0m. O minTime` também deve ser configurado, e seu valor deve ser maior que zero.
Utilize o evento onInsert para adicionar informações adicionais no apontamento ou executar outras instruções antes de um Tracking ser inserido. No exemplo abaixo adicionamos a hora local ao Location no evento onInsert.
No App:
App
location minTime=1m minDistance=0 minLastTime=10m tracking
onInsert [opcional]
dataHora = currentDate
No Model:
Location sync=out
lat double
long double
alt double
gpsTime long
dataHora now
O xMova pode receber e enviar mensagens.
Para receber mensagens, precisamos ativar o sync periódico no xmova, de forma que ele faça sync mesmo que não tenha dados offline. O tempo de intervalo entre os sync será o mesmo tempo definido em syncInterval no app. Para ativar o sync periódico, basta adicionar o atributo periodicSync no App.
Nos sync offline normalmente o xMova não retorna qualquer entidade, apenas envia os dados offline. Como o objetivo do sync periódico é retornar as mensagens, precisamos definir na entidade que receberá as mensagens sync=BackgroundIn para que o xMova retorne essa entidade em todos os sync foreground ou background.
Exemplo:
App appCode=LIDER periodicSync
id 555
name ConstruMobil
afterSync
show MensagemScr
MainSrc …
...
MessageScr message title=Mensagem entity=Mensagem messageField=msg dateField=data fromField=from
actions
ok
INSERT INTO ResumoMensagem (texto,data,remetente,enviada) VALUES (:texto,:data,:remetente,false)
A funcionalidade de envio de mensagens é feita com cadastro normal do xmova, como se fosse um apontamento.
No App:
App syncInterval=1m periodicSync
...
#No events, adicionar:
afterSync
show MessageScr
Main ...
actions
#Adicionar a Action...
Mensagem
crud CentralMensagem
#Adicionar a Screen
MessageScr message title=Mensagem entity=MensagemRecebida messageField=texto dateField=data fromField=remetente
actions
ok
INSERT INTO ResumoMensagem (texto,data,remetente,enviada) VALUES (:texto,:data,:remetente,false)
#Adicionar a Screen
Adicionar em Constants
TipoMensagemEnviarSelecionar 1
TipoMensagemEnviarEscrever 2
TipoMensagensEnviadas 1
TipoMensagensRecebidas 2
TipoMensagemEnviar 3
#Adicionar no InstallData
InstallData
TipoMensagemEnviar
1 Selecionar
2 Escrever
TipoMensagens
1 Enviadas
2 Recebidas
3 Enviar
No Model:
MensagemCadastro sync=in EmptyVerify=false
id int
descricao Str
MensagemRecebida sync=backgroundIn EmptyVerify=false
id int
data Str
texto Str
remetente Str
MensagemEnviada sync=out cleanupDays=15 notSavedMessage
data Now
texto text
TipoMensagemEnviar
id int
descricao Str
TipoMensagens
id int
descricao Str
ResumoMensagem cleanupDays=15
texto Str
data Str
remetente Str
enviada boolean
CentralMensagem cleanupDays=5 notSavedMessage
id inc
tipoMensagens TipoMensagens
tipoEnviar TipoMensagemEnviar fillCondition="tipoMensagens == %TipoMensagemEnviar"
data Now
mensagemCadastro MensagemCadastro fillCondition="tipoEnviar == %TipoMensagemEnviarSelecionar"
mensagemTexto text fillCondition="tipoEnviar == %TipoMensagemEnviarEscrever"
enviadas ResumoMensagem fillCondition="tipoMensagens == %TipoMensagensEnviadas"
recebidas ResumoMensagem fillCondition="tipoMensagens == %TipoMensagensRecebidas"
fieldEvents
afterFill
mensagemCadastro
INSERT INTO MensagemEnviada (data ,texto ) VALUES (:data,:mensagemCadastro.descricao)
INSERT INTO ResumoMensagem (texto,data,enviada) VALUES (:mensagemCadastro.descricao,:data, true)
sync background
mensagemTexto
INSERT INTO MensagemEnviada (data ,texto ) VALUES (:data,:mensagemTexto)
INSERT INTO ResumoMensagem (texto,data,enviada) VALUES (:mensagemTexto,:data, true)
sync background
filterListRecord
enviadas
if (listRecord.enviada == false)
return false
recebidas
if (listRecord.enviada == true)
return false
#Onde desejar exibir a opção de Mensagens, adicionar na entidade pai:
Boletim
crudActions
Mensagens
fill centralMensagem
No Lang:
EntityLabel.CentralMensagem = Central Mensagem
EntityLabel.MensagemCadastro = Cadastro
EntityLabel.TipoMensagens = Central Mensagens
EntityLabel.TipoMensagemEnviar = Enviar
FieldLabel.CentralMensagem.enviadas = Enviadas
FieldLabel.CentralMensagem.recebidas = Recebidas
FieldLabel.CentralMensagem.mensagemTexto = Mensagem
EntityLabel.ResumoMensagem = Mensagens
FieldLabel.ResumoMensagem.texto= Texto
FieldLabel.ResumoMensagem.data= Data
FieldLabel.ResumoMensagem.remetente= Remetente
Com o atributo MessageScheduled e a adição de alguns campos obrigatórios é possível receber dados que serão usados para exibir uma mensagem em um determinado horário (todos os dias) ou então em uma determinada data uma única vez.
No App:
App syncInterval=1m periodicSync
No Model:
ScheduledMessage sync=backgroundIn messageScheduled
id int
title Str
text Str
hour Str
date Str
fixedPoint int
executed Date
scheduled Date
events
onShowScheduledMessage
log title
log scheduled
log executed
No Lang:
FixedKeyMsgScheduledScreenTitle = Mensagem (Valor padrão)
FixedKeyMsgScheduledHour = Horário (Valor padrão)
FixedKeyMsgScheduledTitle = Título (Valor padrão)
FixedKeyMsgScheduledMessage = Mensagem (Valor padrão)
FixedKeyMessageSourceScreenMessageConfirmOK = OK (Valor padrão)