Difference between revisions of "Programação com Objectos/Projecto de Programação com Objectos/Enunciado do Projecto de 2022-2023"

From Wiki**3

< Programação com Objectos‎ | Projecto de Programação com Objectos
(Created page with "{{PRJPOAvisosEN20222023}} {{PRJPOMandatory20222023}} {{TOCright}} '''<font color="red">EM PREPARAÇÃO</font>''' '''<font color="red">ÉPOCA NORMAL</font>''' O objectivo do...")
 
(Iniciar comunicação interactiva)
 
(39 intermediate revisions by the same user not shown)
Line 2: Line 2:
 
{{PRJPOMandatory20222023}}
 
{{PRJPOMandatory20222023}}
 
{{TOCright}}
 
{{TOCright}}
'''<font color="red">EM PREPARAÇÃO</font>'''
+
<!--'''<font color="red">EM PREPARAÇÃO</font>'''
 
+
-->
 
'''<font color="red">ÉPOCA NORMAL</font>'''
 
'''<font color="red">ÉPOCA NORMAL</font>'''
  
O objectivo do projecto é desenvolver uma aplicação de gestão do inventário de um entreposto de recursos naturais e seus derivados (formados por um ou mais produtos ou seus derivados). O entreposto concede prémios de fidelização aos bons parceiros (que lhe compram e vendem produtos), baseando-se no volume de negócio (compras e vendas). A funcionalidade da aplicação inclui, entre outras acções, manipular dados de produtos para negociar, registar/manipular dados de parceiros, registar/manipular transacções de compra e venda e outras e fazer pesquisas várias sobre a informação armazenada.
+
O objectivo do projecto e desenvolver uma aplicação de gestão de uma rede de terminais de comunicação, denominada por '''prr'''. Genericamente, o programa permite o registo, gestão e consulta de clientes, terminais e comunicações.
  
Neste texto, o tipo '''negrito''' indica um literal (i.e., é exactamente como apresentado); o símbolo ␣ indica um espaço; e o tipo ''itálico'' indica uma parte variável.
+
= Clientes, terminais, comunicações, planos tarifários =
  
=  Entreposto, Produtos, Parceiros e Transacções =
+
Os clientes, terminais e comunicações possuem chaves únicas, cadeias de caracteres para os clientes e para os terminais e inteiros para as comunicações.
  
Os produtos e os parceiros possuem uma chave única (cadeia de caracteres, não havendo distinção entre maiúsculas e minúsculas). As transacções possuem uma chave única inteira gerida automaticamente.
+
A noção de saldo é definida como a diferença entre os valores dos pagamentos efectuados e das dívidas por pagar. O saldo pode ser calculado globalmente (considerando todos os clientes), por cliente (considerando todos os terminais do cliente) ou por terminal (considerando todas as comunicações do terminal, pagas e por pagar).
  
O saldo inicial do entreposto é 0 (zero).
+
== Propriedades e funcionalidade dos clientes ==
<!--
 
O entreposto tem a noção de sessão: um parceiro tem de iniciar essa sessão, indicando o seu identificador pessoal, antes da abertura do menu principal. Os parceiros podem ser carregados de ficheiros textuais (ver abaixo) ou a partir de sessões anteriores que foram preservadas via persistência Java (ver abaixo). Note-se que a própria sessão não é mantida de forma persistente.-->
 
  
== Propriedades e Funcionalidade dos Produtos ==
+
Cada cliente, para além da chave única, tem ainda o nome (cadeia de caracteres) e o número de identificação fiscal (inteiro). A cada cliente podem estar associados vários terminais.
  
Um produto pode ser simples ou derivado. Um produto derivado tem uma receita de fabrico. A receita corresponde à discriminação dos identificadores dos componentes (simples ou derivados) que formam o produto e respectivas quantidades. Não é possível a definição de receitas cujos componentes não sejam previamente conhecidos.
+
O cliente mantém informação sobre os pagamentos efectuados (sobre comunicações passadas) e valores em dívida (comunicações cujo valor ainda não foi pago).
  
Os produtos, tanto simples, como derivados, são comprados, vendidos, ou fabricados (no caso dos produtos derivados) em lotes. Cada lote tem um fornecedor (o parceiro a quem é comprado o produto), o número de unidades disponíveis do produto no lote e o preço de cada unidade. Podem existir vários lotes do mesmo produto, com o mesmo fornecedor, com preços iguais ou diferentes. O preço de um produto é sempre um número em vírgula flutuante não negativo. Quando um lote é esgotado, é removido do registo de dados.
+
Existem três tipos de clientes: '''Normal''' (situação inicial, após o registo -- no entanto, ver a situação da leitura de dados textuais), '''Gold''' e '''Platinum'''. O tipo de cliente influencia o custo das comunicações que efectua (ver [[#Planos tarifários|planos tarifários]]). O tipo do cliente evolui nas seguintes condições:
  
O preço dos produtos é definido pelo parceiro no acto da compra (pelo entreposto). Este processo é uniforme para todos os produtos, excepto nas operações indicadas (ver a seguir e abaixo).
+
{|
 +
| '''Antes'''
 +
| '''Depois'''
 +
| '''Condição'''
 +
|-
 +
| Normal
 +
| Gold
 +
| O saldo do cliente (após realizar um pagamento) é superior a 500 créditos.
 +
|-
 +
| Normal
 +
| Platinum
 +
| (não é possível)
 +
|-
 +
| Gold
 +
| Normal
 +
| O saldo do cliente (após realizar uma comunicação) é negativo.
 +
|-
 +
| Gold
 +
| Platinum
 +
| O cliente realizou 5 comunicações de vídeo consecutivas e não tem saldo negativo. A contabilização da 5ª comunicação ainda considera que o cliente é do tipo Gold.
 +
|-
 +
| Platinum
 +
| Gold
 +
| O cliente realizou 2 comunicações de texto consecutivas e não tem saldo negativo. A contabilização da 2ª comunicação ainda considera que o cliente é do tipo Platinum.
 +
|-
 +
| Platinum
 +
| Normal
 +
| O saldo do cliente (após realizar uma comunicação) é negativo.
 +
|}
  
O entreposto pode realizar operações de agregação e de desagregação. Uma operação de agregação consiste em criar um produto derivado a partir das existências dos seus componentes. O preço dos produtos derivados, resultantes de agregações, é calculado com base nos preços dos componentes, de acordo com a receita, tendo um agravamento multiplicativo (número positivo em vírgula flutuante), definido pela receita, relativo ao valor acrescentado associado à combinação. Por exemplo: a água é feita de hidrogénio (2x) e de oxigénio (1x). O seu preço é: <math>P_{H_2O} = (1 + \alpha) \times (2 \times P_H + P_O)</math> (onde <math>P_x</math> são preços e <math>\alpha</math> é o factor de agravamento, e.g. 0.1, correspondente a um aumento de 10% sobre o valor dos componentes). A reserva de recursos para construção de determinada quantidade de produto derivado contempla a totalidade da construção (e.g., não é possível respeitar um pedido de 10 unidades de água se apenas estiverem disponíveis 8 unidades de oxigénio). Em geral, operações de consumo de existências começam sempre pelas existências de menor preço, consumindo um ou mais lotes, total ou parcialmente, no processo.
+
== Propriedades e funcionalidade dos terminais ==
  
É possível desagregar um produto derivado e recuperar os seus componentes. Não há perdas de produtos nesta operação, mas pode haver perda de valor: o valor do produto derivado pode ser superior à soma dos valores dos preços dos seus componentes<!-- (o preço de um componente resultante de uma operação de desagregação é o preço mais alto conhecido para esse componente, considerando todos os lotes desse produto (passados e presentes)-->. O parceiro que pede a operação de desagregação paga o diferencial de valor, caso seja positivo, sendo registada uma transacção de desagregação (com o valor do diferencial, mesmo que negativo).
+
Cada terminal é identificado por uma cadeia de caracteres numérica (exactamente 6 dígitos) e está associado a um único cliente.  
  
== Propriedades e Funcionalidade dos Parceiros ==
+
Os terminais podem realizar três tipos de comunicação: texto, voz e vídeo. As comunicações realizadas pelo terminal são contabilizadas de acordo com o tarifário associado ao cliente. O terminal tem contabilidade própria, sendo sempre possível saber os valores dos pagamentos efectuados e dos valores devidos.
  
Cada parceiro tem um nome (cadeia de caracteres) e uma morada (cadeia de caracteres). Associada a cada parceiro existe ainda informação relativa às suas transacções.
+
Existem, pelo menos, dois tipos de terminal: básicos e sofisticados. Os terminais básicos só conseguem realizar comunicações de texto e de voz, não podendo nem iniciar nem receber comunicações de vídeo. Os terminais sofisticados podem realizar todos os tipos de comunicação.  
  
Um parceiro tem ainda um estatuto (e.g., '''Elite''', etc- -- ver abaixo), ao qual está associada contabilização de pontos. O estatuto tem impacto na relação do parceiro com o entreposto.
+
Cada terminal tem uma lista de amigos (inicialmente vazia). Um terminal não pode ser amigo de si próprio.
  
Dado um parceiro, é possível aceder ao historial das suas transacções (ver a seguir).
+
Um terminal recém-criado fica no estado de espera (''idle''); tem os valores de pagamentos e dívidas ambos a zero. Existem outros estados, definidos a seguir.
  
== Propriedades e Funcionalidade das Transacções ==
+
=== Estados dos terminais ===
 +
Cada terminal pode estar em espera, em silêncio, ocupado ou desligado.
 +
* Espera -- situação normal sem actividade (''idle'');
 +
* Silêncio -- tal como em Espera, pode iniciar-se qualquer tipo de comunicação suportada pelo terminal, mas só podem ser recebidas comunicações de texto;
 +
* Ocupado -- não podendo iniciar-se comunicações, mas podem ser recebidas mensagens de texto;
 +
* Desligado -- não podem ser iniciadas ou recebidas comunicações.
  
As transacções são identificadas por um número (inteiro), atribuído de forma automática pela aplicação. Este identificador começa em 0 (zero), sendo incrementado quando se regista uma nova transacção. A sequência de identificadores é partilhada por todos os tipos de transacções. Todas as transacções têm uma data de pagamento.
+
=== Transições entre estados de terminais ===
 +
Um terminal pode chegar aos vários estados nas seguintes condições (outras transições não são possíveis):
 +
* Espera -- de desligado (ao ligar em espera); de silêncio (ir para espera); de ocupado (final de comunicação);
 +
* Silêncio -- de desligado (ao ligar em silêncio); de espera (colocar em silêncio); de ocupado (final de comunicação);
 +
* Ocupado -- de espera ou de silêncio (início de comunicação);
 +
* Desligado -- de espera ou de silêncio (ao desligar).
  
O entreposto realiza com os parceiros transacções de compra (do entreposto aos parceiros), venda (do entreposto aos parceiros) e desagregações (pelo entreposto a pedido dos parceiros). As desagregações: podem ser vistas como compras e vendas combinadas, para efeitos de progressão do estatuto dos parceiros. Uma desagregação corresponde a uma venda do produto a desagregar e à compra dos componentes que resultam da desagregação.
+
=== Notificações ===
  
As compras são sempre pagas instantaneamente. As vendas podem ser pagas pelos parceiros mais tarde (excepto no contexto de uma desagregação: nesse caso, são pagas imediatamente).
+
Apenas são passíveis de notificação os clientes que tentaram comunicação com um terminal e a comunicação não foi possível nessa altura. Quando uma comunicação não se efectua, regista-se a tentativa de contacto, para que, assim que seja possível a realização do contacto pretendido, se enviarem notificações aos clientes associados aos terminais de origem. O registo da tentativa de contactos só tem lugar quando o cliente do terminal de origem tem activa a recepção de contactos falhados no instante em que se tentou efectuar a comunicação (por omissão, a recepção de notificações está activa).
  
=== Compras (do entreposto a parceiros) ===
+
São geradas notificações e é possível avisar um cliente nas seguintes circunstâncias:
Uma compra está associada a um parceiro e envolve uma ou mais unidades de um produto fornecido pelo parceiro em causa. O custo unitário do produto é definido pelo parceiro no momento da transacção. Quando se faz uma compra deve considerar-se que a compra é paga imediatamente e que as existências do entreposto são actualizadas considerando os produtos comprados. É mantida informação sobre cada produto, o parceiro que o forneceu e o preço que foi pago.
+
# Um terminal desligado é colocado em silêncio (''off-to-silent''): notifica-se disponibilidade para receber comunicações de texto.
 +
# Um terminal desligado ou em silêncio é colocado em espera (''off-to-idle'' ou ''silent-to-idle''): notifica-se disponibilidade para receber comunicações (qualquer suportada).
 +
# Um terminal deixa de estar ocupado (''busy-to-idle''): notifica-se disponibilidade para receber comunicações (qualquer suportada).
  
=== Vendas (do entreposto a parceiros) ===
+
== Propriedades e funcionalidade das comunicações ==
Uma venda está associada a um parceiro e envolve uma ou mais unidades de um único produto. As unidades podem ser provenientes de vários lotes e ter preços diferentes, dependendo dos parceiros que forneceram o produto. Cada venda tem uma data limite de pagamento: primeiro realiza-se a venda e só depois é que se procede ao seu pagamento.
 
  
Se o produto a adquirir for derivado e não existir em quantidade suficiente no entreposto, a quantidade em falta pode ser fabricada a partir de outros produtos, de acordo com a receita correspondente ao produto em venda. O preço a pagar é o definido pela reserva dos componentes individuais (com a selecção do preço mais baixo, etc.) e o correspondente factor de valor acrescentado (ver acima). Note-se que, na venda de múltiplas unidades de um produto derivado, o processo de construção deve ser repetido, pelo que cada uma das unidades do produto derivado pode ter custo diferente das anteriores. Caso falte algum componente da receita, então a operação de agregação deve ser abortada indicando o primeiro produto (pela ordem da receita) que viola a condição de disponibilidade.
+
Cada comunicação tem um identificador único (número inteiro, no contexto de todos os clientes). A primeira tem como identificador “1”, sendo os identificadores subsequentes obtidos por incremento unitário do mais recente identificador utilizado. A comunicação contém ainda informação sobre os terminais de origem e de destino e o estado da comunicação: em curso ou terminada.  
  
O preço a pagar por um parceiro (numa operação de venda por parte do entreposto) depende ainda do tempo que demora a realizar o pagamento. São considerados os seguintes períodos ('''N''' é 5 para produtos simples, 3 para produtos derivados):
+
As comunicações de texto têm ainda a mensagem enviada. As comunicações interactivas (vídeo e voz) possuem informação sobre a duração da comunicação. O custo de uma comunicação depende do comprimento da mensagem de texto ou da duração das comunicações interactivas. O custo depende ainda do [[#Planos tarifários|plano tarifário]] associado a cada cliente (calculado no final da comunicação). Todos os cálculos envolvendo os custos das comunicações devem ser realizados sem arredondamentos.
  
* '''P1''' - até '''N''' dias antes do limite de pagamento ('''data_limite_de_pagamento − data_actual ≥ N''').
+
Um terminal não pode estabelecer uma comunicação interactiva consigo próprio.
* '''P2''' - até à data limite ('''0 ≤ data_limite_de_pagamento − data_actual < N''').
 
* '''P3''' - até '''N''' dias depois da data limite ('''0 < data_actual − data_limite_de_pagamento ≤ N''').
 
* '''P4''' - após '''N''' dias depois da data limite ('''data_actual − data_limite_de_pagamento > N''').
 
  
Intervalos ao longo do tempo:
+
== Planos tarifários ==
<source lang="text">
 
  
              ──────────────┬──────────────┬──────────────┬──────────────
+
Cada plano tarifário define os custos para cada tipo de comunicação, baseado no nível do cliente, no tipo de comunicação, entre outras características. Os planos tarifários têm um nome único no contexto da rede de terminais a que estão associados. A rede de terminais pode oferecer vários planos tarifários mas em cada momento um cliente apenas tem um plano tarifário. A rede de terminais oferece pelo menos o plano tarifário designado como '''base'''. Este plano tarifário é o plano atribuído inicialmente a todos os clientes. O custos das comunicações é medido em créditos.
>>> tempo >>>      P1      │      P2      │      P3      │      P4      >>> tempo >>>
 
              ──────────────┴──────────────┴──────────────┴──────────────
 
                            ↑              ↑              ↑           
 
                        prazo - N        prazo        prazo + N
 
</source>
 
  
Note-se que estes períodos são arbitrários e não correspondem a qualquer conceito especial de tempo, podendo ser redefinidos ou mesmo eliminados em revisões do processo de venda.
+
O custo (medido em créditos) de uma comunicação de texto com '''N''' caracteres no plano tarifário base está representado na tabela seguinte:
  
=== Desagregações de produtos (pelo entreposto a pedido de parceiros) ===
+
{|
 
+
! style="width: 40%;" |
Uma desagregação é uma transacção associada a um parceiro e envolve um único produto. Um parceiro pode pedir uma desagregação para permitir outras operações sobre os componentes do produto a desagregar. Se o produto a desagregar for simples, a acção não tem qualquer efeito.
+
! style="width: 20%;" | '''Normal'''
 
+
! style="width: 20%;" | '''Gold'''
As desagregações são operações sobre o estado do entreposto por parte de um parceiro: a desagregação remove uma quantidade de produto derivado das existências do entreposto (como se fosse uma venda) e introduz nas existências do entreposto os seus componentes (como se fosse uma compra ao parceiro envolvido). O preço de inserção é o menor preço nos lotes disponíveis para esse produto. Se não existirem lotes disponíveis, o preço é o maior preço associado a esse produto no histórico de operações do entreposto.
+
! style="width: 20%;" | '''Platinum'''
O parceiro paga ao entreposto a diferença (positiva) entre os valores inicial (o valor da quantidade de produto não desagregado) e final (o valor das quantidades de componentes obtidas com base na desagregação).
+
|-
 
+
| '''N''' < 50 caracteres
== Notificações ==
+
| 10
 
+
| 10
Quando o entreposto recebe um novo produto, os parceiros devem ser colocados como entidades interessadas em receber notificações sobre eventos a ele associados. Em qualquer momento, um parceiro pode activar ou desactivar as notificações relativas a um produto. Os eventos a considerar são os seguintes: (i) quando o produto passa de stock 0 (zero) para outro valor (positivo); (ii) quando aparece um lote mais barato de um produto. As notificações são compostas pelo identificador do produto e pela descrição da notificação: '''NEW''', para novas existências de produtos, mas não quando se regista um novo produto; e '''BARGAIN''', para disponibilidade de preços mais baixos. As notificações são registadas nos parceiros que as recebem.
+
| 0
 
+
|-
A entrega de notificações deve ser flexível e prever vários meios de entrega, e.g., correio postal, SMS, email, entre outras. O meio de entrega por omissão corresponde a registar a notificação na aplicação.
+
| 50 caracteres <= '''N''' < 100 caracteres
 +
| 16
 +
| 10
 +
| 4
 +
|-
 +
| '''N''' >= 100 caracteres
 +
| 2 x '''N'''  
 +
| 2 x '''N'''  
 +
| 4
 +
|}
  
==  Contabilização de Pontos (Parceiros) ==
+
Quando é efectuada uma comunicação de voz ou de vídeo, o custo no plano tarifário base é proporcional ao tempo de conversação e, quando se comunica com um terminal amigo, é aplicado um desconto de 50%. O custo, em créditos por minuto, é o seguinte para terminais não amigos:
 
 
As multas e os descontos aplicam-se apenas no pagamento de transacções de venda.
 
 
 
Os valores pagos (instantaneamente) nas transacções de desagregação também dão direito à contabilização de pontos (apenas quando é positivo).
 
 
 
Existem três classificações distintas de parceiros: '''Nomal''', '''Selection''' e '''Elite'''. A classificação de um parceiro tem impacto nas multas e descontos a aplicar no pagamento de uma venda.
 
 
 
Quando um parceiro paga uma venda dentro do prazo, acumula um número de pontos correspondente a 10 vezes o valor pago. Não há contabilização de pontos em pagamentos atrasados. A verificação do atraso é realizada quando se realiza o pagamento de uma venda.
 
 
 
Os parceiros passam ao nível '''Selection''' se acumularem mais de 2000 pontos. Os parceiros passam ao nível '''Elite''' se acumularem mais de 25000 pontos.
 
 
 
Se um parceiro se atrasa no pagamento da venda, é despromovido: um parceiro '''Elite''' passa a '''Selection''' se o pagamento ocorrer com um atraso de pagamento superior a 15 dias (perde 75% dos pontos acumulados); um parceiro '''Selection''' passa a '''Normal''' se o pagamento ocorrer com um atraso de pagamento superior a 2 dias (perde 90% dos pontos acumulados). Se um parceiro '''Normal''' se atrasa num pagamento, perde os pontos que tem.
 
 
 
As multas e os descontos dependem do estatuto do parceiro e dos prazos associados à venda e ao estatuto.
 
  
 
{|
 
{|
|
+
! style="width: 40%;" |
! style="width: 11.25%; background: #ccddff;" | '''P1'''
+
! style="width: 20%;" | '''Normal'''
! style="width: 11.25%; background: #ccddff;" |
+
! style="width: 20%;" | '''Gold'''
! style="width: 11.25%; background: #e6eeff;" | '''P2'''
+
! style="width: 20%;" | '''Platinum'''
! style="width: 11.25%; background: #e6eeff;" |
 
! style="width: 11.25%; background: #ffe6cc;" | '''P3'''
 
! style="width: 11.25%; background: #ffe6cc;" |
 
! style="width: 11.25%; background: #ffad99;" | '''P4'''
 
! style="width: 11.25%; background: #ffad99;" |
 
 
|-
 
|-
|
+
| Comunicação de voz
| <font color="red">Multa</font>
+
| 20
| <font color="forestgreen">Desconto</font>
+
| 10
| <font color="red">Multa</font>
+
| 10
| <font color="forestgreen">Desconto</font>
 
| <font color="red">Multa</font>
 
| <font color="forestgreen">Desconto</font>
 
| <font color="red">Multa</font>
 
| <font color="forestgreen">Desconto</font>
 
 
|-
 
|-
| '''Normal'''
+
| Comunicação de vídeo
| 0
+
| 30
| 10%
+
| 20
| 0
+
| 10  
| 0
 
| 5% diários
 
| 0
 
| 10% diários
 
| 0
 
|-
 
| '''Selection'''
 
| 0
 
| 10%
 
| 0
 
| ≥ 2 dias antes da data limite: 5%; depois, sem desconto
 
| > 1 dia depois da data limite: 2% diários (0, caso contrário)
 
| 0
 
| 5% diários
 
| 0
 
|-
 
| '''Elite'''
 
| 0
 
| 10%
 
| 0
 
| 10%
 
| 0
 
| 5%
 
| 0
 
| 0
 
 
|}
 
|}
  
== Data ==
+
O custo de uma comunicação deve ser calculado quando a comunicação termina e guardado, por forma a garantir que o custo não é afectado por mudanças futuras dos planos tarifários.
 +
 
 +
== Entrega de notificações ==
  
A data é representada por um número inteiro e tem inicialmente o valor 0 (zero). A data pode começar com outro valor se se recuperar o estado do entreposto a partir de um suporte persistente.
+
Os clientes podem activar a recepção de notificações sobre eventos associados a terminais em algumas circunstâncias. Em qualquer momento, um cliente pode activar ou desactivar essas notificações. A entrega de notificações deve ser flexível e deve prever vários meios de entrega, e.g., correio postal, SMS, email, entre outras. O meio de entrega por omissão corresponde a registar a notificação na aplicação.
  
Os avanços de data são valores inteiros positivos que representam o número de dias.
+
As notificações contêm informação acerca da sua natureza e do terminal a que dizem respeito. Um dado evento apenas produz uma notificação por cliente (o conjunto de clientes a notificar é limpo após o envio da notificação).
  
 
= Funcionalidade da aplicação =
 
= Funcionalidade da aplicação =
Line 164: Line 154:
 
A aplicação permite manter informação sobre as entidades do modelo. Possui ainda a capacidade de preservar o seu estado (não é possível manter várias versões do estado da aplicação em simultâneo).
 
A aplicação permite manter informação sobre as entidades do modelo. Possui ainda a capacidade de preservar o seu estado (não é possível manter várias versões do estado da aplicação em simultâneo).
  
A base de dados com os conceitos pré-definidos é [[#Leitura de Dados a Partir de Ficheiros Textuais|carregada no início da aplicação]].
+
Deve ser possível efectuar pesquisas sujeitas a vários critérios e sobre as diferentes entidades geridas pela aplicação.
 +
 
 +
Uma base de dados textual com conceitos pré-definidos pode ser [[#Leitura de Dados a Partir de Ficheiros Textuais|carregada no início da aplicação]].
  
É possível saber os saldos do entreposto (diferencial entre vendas e compras). Existe um saldo disponível, correspondente à diferença entre as vendas realmente pagas e as compras, e um saldo contabilístico, correspondente à diferença entre o valor contabilístico das vendas (pagas ou não e considerando descontos/penalizações à data da consulta de saldo) e as compras. Note-se que as desagregações são consideradas combinações de compras e vendas, pelo que também influenciam o saldo do entreposto.
+
{{Suggestion|Note-se que não é necessário nem desejável implementar de raiz a aplicação: já existem classes que representam e definem a interface geral da funcionalidade do ''core'' da aplicação, tal como é visível pelos comandos da aplicação.}}
 +
{{CVSCode|A interface geral do ''core'' já está parcialmente implementada na classe '''prr.Manager''' e outras fornecidas (cujos nomes devem ser mantidos), devendo ser adaptadas onde necessário. É ainda necessário criar e implementar as restantes classes que suportam a operação da aplicação.}}
  
Deve ser possível efectuar pesquisas sujeitas a vários critérios e sobre as diferentes entidades geridas pelo entreposto.
+
== Serialização ==
  
{{Suggestion|Note-se que não é necessário/desejável implementar de raiz a aplicação: já existem classes que representam e definem a interface geral da  funcionalidade do ''core'' da aplicação, tal como é visível pelos comandos da aplicação.}}
+
É possível guardar e recuperar o estado actual da aplicação, preservando toda a informação relevante, descrita acima.
{{CVSCode|A interface geral do ''core''  já está parcialmente implementada na classe '''ggc.WharehouseManager''' e outras fornecidas (cujos nomes devem ser mantidos), devendo ser adaptadas onde necessário. É ainda necessário criar e implementar as restantes classes que suportam a operação da aplicação.}}
+
<!--== Funcionalidade Associada a Entidades do Domínio ==
  
== Serialização ==
+
=== Funcionalidade associadas aos clientes ===
 +
A seguinte funcionalidade sobre clientes deve ser suportada pela aplicação: (i) visualizar clientes; (ii) registar novos clientes; (iii) registar contactos falhados; (iv) controlar o registo de contactos falhados; (v) apresentar valores de pagamentos e dívidas de clientes.
  
É possível guardar e recuperar o estado actual da aplicação, preservando toda a informação relevante, descrita [[#Conceitos e Relações do Modelo|acima]].
+
Um cliente não pode ser removido, sendo sempre possível aceder a todo o seu historial.
  
== Funcionalidade Associada a Entidades do Domínio ==
+
=== Funcionalidade associadas aos terminais ===
 +
A seguinte funcionalidade sobre terminais deve ser suportada pela aplicação: (i) visualizar um terminal; (ii) registar um novo terminal; (iii) ligar e desligar um terminal; (iv) colocar terminal em silêncio; (v) adicionar e remover terminais da lista de amigos de um terminal; (vi) proceder a um pagamento; (vii) apresentar valores de pagamentos e dívidas de terminais.
  
A seguinte funcionalidade sobre produtos deve ser suportada pela aplicação: (i) visualizar produtos e lotes de produtos; (ii) visualizar lotes específicos.
+
Um terminal não pode ser removido, sendo sempre possível aceder a todo o seu historial.
  
A seguinte funcionalidade sobre parceiros deve ser suportada pela aplicação: (i) visualizar um ou mais parceiros; (ii) registar um novo parceiro; (iii) activar/desactivar notificações relativas a produtos; (iv) consultar o histórico de transacções realizadas por um parceiro.
+
=== Funcionalidade associada a comunicações ===
 +
A seguinte funcionalidade está associada às comunicações: (i) registar comunicações efectuadas por um terminal; (ii) activar/desactivar notificações associadas a terminais; (iii) consultar comunicações. A seguinte funcionalidade de consulta deve ser suportada: (i) comunicações efectuadas por um cliente; (ii) terminais que iniciaram comunicações para um determinado terminal; (iii) terminais que iniciaram uma comunicação para um determinado cliente; (iv) terminais com um determinado plano tarifário; (v) clientes com dívidas.
  
A seguinte funcionalidade sobre transacções deve ser suportada: (i) visualizar uma transacção; (ii) registar uma nova compra; (ii) registar uma nova venda; (iii) registar uma desagregação; (iv) receber pagamento de uma venda.
+
As comunicações, uma vez registadas, não podem ser alteradas ou removidas.
 +
-->
  
 
= Requisitos de Desenho =
 
= Requisitos de Desenho =
 
   
 
   
Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Assim, deve ser possível:
+
Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Assim, deve ser possível: definir novos tipos de clientes; definir novos tipos de comunicação; definir novos planos tarifários; definir novas formas de pesquisa; permitir a gestão de várias redes de terminais.
* Definir novas entidades que desejem ser notificadas da alteração do estado dos produtos;
 
* Adicionar novos modos de entrega de mensagens (notificações);
 
* Adicionar novas políticas de recompensa de parceiros;
 
* Adicionar novas formas de consulta.
 
  
Embora na especificação actual não seja possível remover entidades, a inclusão desta funcionalidade deve ser prevista, por forma a minimizar o impacto da sua futura inclusão.
+
<!--Deve ser possível a definição de diferentes entidades que estejam interessadas em ser notificadas com os dados referentes a cada comunicação realizada por um dado terminal sem que isso provoque alterações no código da aplicação. As entidades podem registar ou cancelar o seu interesse em qualquer momento e podem existir várias entidades distintas interessadas em simultâneo.
 +
-->
 +
Embora na especificação actual não seja possível remover algumas entidades, a inclusão desta funcionalidade deve ser prevista, por forma a minimizar o impacto da sua futura inclusão.
  
 
= Interacção com o utilizador =
 
= Interacção com o utilizador =
Line 199: Line 194:
 
Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de procederem à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu).
 
Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de procederem à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu).
  
As operações de pedido e apresentação de informação ao utilizador '''devem''' realizar-se através dos objectos ''form'' e ''display'', respectivamente, presentes em cada comando. As mensagens são produzidas pelos métodos das [[Programação com Objectos/Projecto de Programação com Objectos/Material de Apoio ao Desenvolvimento|bibliotecas de suporte]] ('''po-uilib''' e '''ggc-app'''). As mensagens não podem ser usadas no núcleo da aplicação ('''ggc-core'''). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.
+
As operações de pedido e apresentação de informação ao utilizador '''devem''' realizar-se através dos objectos ''form'' e ''display'', respectivamente, presentes em cada comando. As mensagens são produzidas pelos métodos das [[Programação com Objectos/Projecto de Programação com Objectos/Material de Apoio ao Desenvolvimento|bibliotecas de suporte]] ('''po-uilib''' e '''prr-app'''). As mensagens não podem ser usadas no núcleo da aplicação ('''prr-core'''). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.
  
A apresentação de valores monetários (preços, saldos, etc.) é sempre feita com arredondamento ao inteiro mais próximo.
+
A apresentação de valores monetários é sempre feita com arredondamento ao inteiro mais próximo, mas a representação interna não deve ser arredondada.
 +
 
 +
A apresentação de listas de entidades do domínio (clientes, etc.) faz-se por ordem crescente da respectiva chave: dependendo dos casos, a ordem pode ser numérica ou lexicográfica (UTF-8), não havendo distinção entre maiúsculas e minúsculas.
  
 
As excepções usadas na interacção (subclasses de '''pt.tecnico.uilib.menus.CommandException'''), excepto se indicado, são lançadas pelos comandos (subclasses de '''pt.tecnico.uilib.menus.Command''') e tratadas pelos menus (instâncias de subclasses de '''pt.tecnico.uilib.menus.Menu'''). Outras excepções não devem substituir as fornecidas nos casos descritos.
 
As excepções usadas na interacção (subclasses de '''pt.tecnico.uilib.menus.CommandException'''), excepto se indicado, são lançadas pelos comandos (subclasses de '''pt.tecnico.uilib.menus.Command''') e tratadas pelos menus (instâncias de subclasses de '''pt.tecnico.uilib.menus.Menu'''). Outras excepções não devem substituir as fornecidas nos casos descritos.
  
A apresentação de listas de entidades do domínio (parceiros, transacções, etc.) faz-se por ordem crescente da respectiva chave: dependendo dos casos, a ordem pode ser numérica ou lexicográfica (UTF-8), não havendo distinção entre maiúsculas e minúsculas.
+
{{CVSCode|Note-se que o programa principal e os comandos e menus, a seguir descritos, já estão parcialmente implementados nas ''packages'' '''prr.app''', '''prr.app.clients''', '''prr.app.lookups''', '''prr.app.main''', '''prr.app.terminal''', '''prr.app.terminals'''. Estas classes são de uso obrigatório e estão disponíveis no [[Programação com Objectos/Projecto de Programação com Objectos/Repositório CVS|CVS]] (módulo '''prr-app''').}}
 
 
{{CVSCode|Note-se que o programa principal e os comandos e menus, a seguir descritos, já estão parcialmente implementados nas ''packages'' '''ggc.app''', '''ggc.app.main''', '''ggc.app.products''', '''ggc.app.partners''', '''ggc.app.transactions''', '''ggc.app.lookups'''. Estas classes são de uso obrigatório e estão disponíveis no [[Programação com Objectos/Projecto de Programação com Objectos/Repositório CVS|CVS]] (módulo '''ggc-app''').}}
 
  
 
== Menu Principal ==
 
== Menu Principal ==
  
As acções deste menu permitem gerir a salvaguarda do estado da aplicação, abrir submenus e aceder a alguma informação global. A lista completa é a seguinte: [[#Salvaguarda do estado actual da aplicação|Abrir]], [[#Salvaguarda do estado actual da aplicação|Guardar]], [[#Mostrar data actual|Mostrar data actual]], [[#Avançar data actual|Avançar data actual]], [[#Gestão e consulta de dados da aplicação|Gestão de Produtos]], [[#Gestão e consulta de dados da aplicação|Gestão de Parceiros]], [[#Gestão e consulta de dados da aplicação|Gestão de Transacções]], [[#Gestão e consulta de dados da aplicação|Consultas]], [[#Menu Principal|Mostrar Saldo Global]].
+
As acções deste menu permitem gerir a salvaguarda do estado da aplicação, abrir submenus e aceder a alguma informação global. A lista completa é a seguinte: [[#Salvaguarda do estado actual da aplicação|Abrir]], [[#Salvaguarda do estado actual da aplicação|Guardar]], [[#Gestão e consulta de dados da aplicação|Gestão de clientes]], [[#Gestão e consulta de dados da aplicação|Gestão de terminais]], [[#Gestão e consulta de dados da aplicação|Consultas]], [[#Mostrar informação global sobre pagamentos e dívidas|Mostrar informação global sobre pagamentos e dívidas]].
  
As etiquetas das opções deste menu estão definidas na classe '''ggc.app.main.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''ggc.app.main.Message'''.
+
As etiquetas das opções deste menu estão definidas na classe '''prr.app.main.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''prr.app.main.Message'''.
  
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''ggc.app.main''' (disponível no CVS), respectivamente: '''DoOpenFile''', '''DoSaveFile''', '''DoDisplayDate''', '''DoAdvanceDate''', '''DoShowGlobalBalance'''.}}
+
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''prr.app.main''' (disponível no CVS), respectivamente: '''DoOpenFile''', '''DoSaveFile''', '''DoOpenMenuClient''', '''DoOpenMenuTerminals''', '''DoOpenMenuLookups''', '''DoShowGlobalBalance'''.}}
  
 
=== Salvaguarda do estado actual da aplicação ===
 
=== Salvaguarda do estado actual da aplicação ===
  
Inicialmente, a aplicação está vazia ou tem apenas informação sobre as entidades que foram carregados no arranque (via [[#Leitura de Dados a Partir de Ficheiros Textuais|ficheiro textual]]).
+
Inicialmente, a aplicação está vazia ou tem apenas informação sobre as entidades que foram carregadas via [[#Leitura de Dados a Partir de Ficheiros Textuais|ficheiro textual]].
  
 
O conteúdo da aplicação (toda a informação actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: '''java.io.Serializable'''). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:
 
O conteúdo da aplicação (toda a informação actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: '''java.io.Serializable'''). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:
  
 
* '''Abrir''' -- Carrega os dados de uma sessão anterior a partir de um ficheiro previamente guardado (ficando este ficheiro associado à aplicação, para futuras operações de salvaguarda). Pede-se o nome do ficheiro a abrir ('''Prompt.openFile()'''). Caso ocorra um problema na abertura ou processamento do ficheiro, deve ser lançada a excepção '''FileOpenFailedException'''. A execução bem-sucedida desta opção substitui toda a informação da aplicação.
 
* '''Abrir''' -- Carrega os dados de uma sessão anterior a partir de um ficheiro previamente guardado (ficando este ficheiro associado à aplicação, para futuras operações de salvaguarda). Pede-se o nome do ficheiro a abrir ('''Prompt.openFile()'''). Caso ocorra um problema na abertura ou processamento do ficheiro, deve ser lançada a excepção '''FileOpenFailedException'''. A execução bem-sucedida desta opção substitui toda a informação da aplicação.
* '''Guardar''' -- Guarda o estado actual da aplicação no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método '''Prompt.newSaveAs()'''. Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.
+
* '''Guardar''' -- Guarda o estado actual da aplicação no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado (para operações de salvaguarda subsequemtes). Esta interacção realiza-se através do método '''Prompt.newSaveAs()'''. Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.
  
Note-se que a opção '''Abrir''' não permite a leitura de ficheiros de texto (estes apenas são utilizados na inicialização da aplicação).
+
Note-se que a opção '''Abrir''' não permite a leitura de ficheiros de texto (estes apenas podem ser utilizados no início da aplicação).
  
 
A opção '''Sair''' nunca implica a salvaguarda do estado da aplicação, mesmo que existam alterações.
 
A opção '''Sair''' nunca implica a salvaguarda do estado da aplicação, mesmo que existam alterações.
 
=== Mostrar data actual ===
 
 
A data actual do sistema é apresentada através da mensagem '''Message.currentDate()'''.
 
 
=== Avançar data actual ===
 
 
O número de dias a avançar é pedido através de '''Prompt.daysToAdvance()'''. Deve ser lançada a excepção '''InvalidDateException''' se o número de dias a avançar for inválido.
 
  
 
=== Gestão e consulta de dados da aplicação ===
 
=== Gestão e consulta de dados da aplicação ===
  
* '''Menu de Gestão de Produtos''' -- Abre o menu de gestão de produtos.
+
* '''Menu de Gestão de Clientes''' -- Abre o menu de gestão de clientes.
* '''Menu de Gestão de Parceiros''' -- Abre o menu de gestão de parceiros.
+
* '''Menu de Gestão de Terminais''' -- Abre o menu de gestão de terminais.
* '''Menu de Gestão de Transacções''' -- Abre o menu de gestão de transacções.
 
 
* '''Menu de Consultas''' -- Abre o menu de consultas (pesquisas).
 
* '''Menu de Consultas''' -- Abre o menu de consultas (pesquisas).
  
=== Mostrar saldo global ===
+
=== Mostrar informação global sobre pagamentos e dívidas ===
  
Esta opção apresenta os valores correspondentes aos saldos disponível e contabilístico do entreposto. Embora internamente o valor dos saldos estejam representados em vírgula flutuante, a apresentação é arredondada ao inteiro mais próximo.
+
Esta opção apresenta os valores globais correspondentes a pagamentos e dívidas (soma dos valores parciais para todos os clientes registados), através da mensagem '''Message.globalPaymentsAndDebts()'''.
  
A apresentação faz-se através da mensagem '''Message.currentBalance()'''.
+
== Menu de gestão de clientes ==
  
== Menu de Gestão de Produtos ==
+
Este menu permite efectuar operações sobre a base de dados de clientes. A lista completa é a seguinte: [[#Visualizar cliente|Visualizar cliente]], [[#Visualizar todos os clientes|Visualizar todos os clientes]], [[#Registar cliente|Registar cliente]], [[#Activar recepção de contactos falhados|Activar recepção de contactos falhados]], [[#Desactivar recepção de contactos falhados|Desactivar recepção de contactos falhados]], [[#Mostrar informação sobre pagamentos e dívidas de cliente|Mostrar informação sobre pagamentos e dívidas de cliente]].
  
Este menu permite efectuar operações sobre a base de dados de produtos. A lista completa é a seguinte: [[#Visualizar todos os produtos|Visualizar todos os produtos]], [[#Visualizar todos os produtos disponíveis|Visualizar todos os produtos disponíveis]], [[#Visualizar os lotes fornecidos por um dado parceiro|Visualizar os lotes fornecidos por um dado parceiro]], [[#Visualizar os lotes de um dado produto|Visualizar os lotes de um dado produto]].
+
<!--As etiquetas das opções deste menu estão definidas na classe '''prr.app.clients.Label'''. -->Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''prr.app.clients.Message'''.
  
<!--As etiquetas das opções deste menu estão definidas na classe '''ggc.app.products.Label'''. -->Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''ggc.app.products.Message'''.
+
Sempre que é pedido o identificador de um cliente ('''Prompt.key()''') e o identificador não existir (excepto no processo de registo), é lançada a excepção '''UnknownClientKeyException'''. Na ocorrência de excepções<!-- (estas ou outras)-->, as operações não têm efeito.
  
Sempre que é pedido o identificador de um parceiro ('''Prompt.partnerKey()''') e o parceiro não existir, é lançada a excepção '''UnknownPartnerKeyException'''. Sempre que é pedido o identificador de um produto ('''Prompt.productKey()''') e o produto não existir, é lançada a excepção '''UnknownProductKeyException'''. Na ocorrência de excepções (estas ou outras), as operações não têm efeito.
+
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''prr.app.clients''' (disponível no CVS), respectivamente: '''DoShowClient''', '''DoShowAllClients''', '''DoRegisterClient''', '''DoEnableClientNotifications''', '''DoDisableClientNotifications''', '''DoShowClientPaymentsAndDebts'''.}}
  
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''ggc.app.products''' (disponível no CVS), respectivamente: '''DoShowAllProducts''', '''DoShowAvailableBatches''', '''DoShowBatchesByPartner''', '''DoShowBatchesByProduct'''.}}
+
=== Visualizar cliente ===
 +
É pedido o identificador do cliente e apresentada a sua informação. O formato de apresentação de cada cliente é como se indica de seguida. Os valores para ''type'' são '''NORMAL''', '''GOLD''', ou '''PLATINUM'''. Os valores para ''notifications'' são '''YES''' ou '''NO''' (notificações activas/inactivas). O campo ''terminals'' representa o número de terminais associados ao cliente, ''payments'' é o valor dos pagamentos do cliente e ''debts'' é o valor das dívidas do cliente. Se o cliente não tiver terminais, os valores de ''terminals'', ''payments'' e ''debts'' são 0 (zero).  
  
=== Visualizar todos os produtos ===
+
CLIENT|''key''|''name''|''taxId''|''type''|''notifications''|''terminals''|''payments''|''debts''
O formato de apresentação de cada produto é o seguinte (cada linha indica um produto):
 
  
Produtos simples:
+
Após esta linha, são apresentadas as notificações do cliente (modo de entrega por omissão), pela ordem em que foram enviadas pela aplicação.
''idProduto''|''preço-máximo''|''stock-actual-total''
 
  
Exemplo:
+
''tipo-de-notificação''|''idTerminal''
  HIDROGÉNIO|200|5000
 
  OXIGÉNIO|1200|2500
 
  
Produtos derivados (é apresentado o produto e a lista de componentes e respectivas quantidades da sua receita):
+
O tipo de notificação é um de '''O2S''' (off-to-silent), '''O2I''' (off-to-idle), '''B2I''' (busy-to-idle) ou '''S2I''' (silent-to-idle), tal como descritos acima. Após esta visualização, considera-se que o cliente fica sem notificações registadas.
''idProduto''|''preço-máximo''|''stock-actual-total''|''agravamento''|''componente-1'':''quantidade-1''#...#''componente-n'':''quantidade-n''
 
  
Exemplo:
+
=== Visualizar todos os clientes ===
  ÁGUA|5000|800|0.1|HIDROGÉNIO:2#OXIGÉNIO:1
 
  
Em ambos os casos, ''stock-actual-total'' pode ser 0 (zero).
+
O formato de apresentação é como para clientes individuais (opção anterior), mas não se apresentam as notificações dos clientes (nem se limpam as listas correspondentes).
  
=== Visualizar todos os lotes ===
+
=== Registar cliente ===
  
O formato de apresentação de cada lote de produto é o seguinte (cada linha indica um lote):
+
O sistema pede o identificador que ficará associado ao cliente (identificador único}. De seguida, pede o nome do cliente ('''Prompt.name()''') e o número de identificação fiscal ('''Prompt.taxId()'''). Após o registo, o cliente fica no estado '''Normal''' e o registo de contactos falhados fica activo.
  
Produtos simples e derivados:
+
Caso o identificador indicado já exista, deve ser lançada a excepção '''DuplicateClientKeyException''', não se realizando o registo.
''idProduto''|''idParceiro''|''preço''|''stock-actual''
 
  
Exemplo:
+
=== Activar recepção de contactos falhados ===
  ÁGUA|EPAL|1150|800
 
  HIDROGÉNIO|Cryostuffs|110|5000
 
  OXIGÉNIO|Cryostuffs|650|2500
 
  OXIGÉNIO|Cryostuffs|650|5000
 
  OXIGÉNIO|Cryostuffs|820|2500
 
  OXIGÉNIO|EPAL|820|2500
 
  
Note-se que podem existir vários lotes para o mesmo produto. Neste caso, a ordenação é, primeiro, pelo identificador do produto e, depois, pelo identificador do parceiro. Se um parceiro fornecer vários lotes do mesmo produto, apresentam-se por ordem crescente de preço e existências.
+
É pedido o identificador do cliente. Se o registo de contactos falhados já estava activo, o cliente não é alterado e é apresentada a mensagem '''Message.clientNotificationsAlreadyEnabled()'''.
  
=== Visualizar os lotes fornecidos por um dado parceiro ===
+
=== Desactivar recepção de contactos falhados ===
  
É pedido o identificador do parceiro e são apresentados os lotes por ele fornecidos. A ordenação é como indicada para a listagem de todos os produtos disponíveis (lotes).
+
É pedido o identificador do cliente. Se o registo de contactos falhados já estava inactivo, o cliente não é alterado e é apresentada a mensagem '''Message.clientNotificationsAlreadyDisabled()'''.
  
=== Visualizar os lotes de um dado produto ===
+
=== Mostrar informação sobre pagamentos e dívidas de cliente ===
  
É pedido o identificador do produto e são apresentados os lotes conhecidos para esse produto. A ordenação é como indicada para a listagem de todos os produtos.
+
O sistema pede o identificador do cliente, apresentando os valores dos seus pagamentos e dívidas ('''Message.clientPaymentsAndDebts()''').
  
== Menu de Gestão de Parceiros ==
+
== Menu de Gestão de Terminais ==
  
Este menu permite efectuar operações sobre a base de dados de parceiros. A lista completa é a seguinte: [[#Mostrar parceiro|Mostrar parceiro]], [[#Mostrar todos os parceiros|Mostrar todos os parceiros]], [[#Registar parceiro|Registar parceiro]], [[#Activar/desactivar notificações de um produto|Activar/desactivar notificações de um produto]], [[#Mostrar transacções de compra com parceiro|Mostrar transacções de compra com parceiro]], [[#Mostrar transacções de venda (e desagregação) com parceiro|Mostrar transacções de venda (e desagregação) com parceiro]].
+
Este menu permite efectuar operações sobre a base de dados de terminais. A lista completa é a seguinte: [[#Mostrar todos os terminais|Mostrar todos os terminais]], [[#Registar terminal|Registar terminal]], [[#Menu da consola de um terminal|Menu da consola de um terminal]].
  
<!--As etiquetas das opções deste menu estão definidas na classe '''ggc.app.partners.Label'''. -->Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''ggc.app.partners.Message'''.
+
Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''prr.app.terminals.Message'''.
  
Sempre que for pedido o identificador de um parceiro ('''Prompt.partnerKey()''') e o parceiro não existir, é lançada a excepção '''UnknownPartnerKeyException''' (excepto no processo de registo). No processo de registo, caso o identificador indicado já exista, deve ser lançada a excepção '''DuplicatePartnerKeyException'''.
+
Sempre que for pedido o identificador de um terminal ('''Prompt.terminalKey()''') e o terminal não existir, é lançada a excepção '''UnknownTerminalKeyException''' (excepto no processo de registo).<!-- No processo de registo, caso o identificador indicado já exista, deve ser lançada a excepção '''DuplicateTerminalKeyException'''.--> Sempre que é pedido o identificador de um cliente ('''Prompt.clientKey()''') e o identificador não existir, é lançada a excepção '''UnknownClientKeyException'''. Na ocorrência de excepções (estas ou outras), as operações não têm efeito.
  
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''ggc.app.partners''' (disponível no CVS), respectivamente: '''DoShowPartner''', '''DoShowAllPartners''', '''DoRegisterPartner''', '''DoToggleProductNotifications''', '''DoShowPartnerAcquisitions''', '''DoShowPartnerSales'''.}}
+
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''prr.app.terminals''' (disponível no CVS), respectivamente: '''DoShowAllTerminals''', '''DoRegisterTerminal''', '''DoOpenMenuTerminalConsole'''.}}
  
=== Mostrar parceiro ===
+
=== Mostrar todos os terminais ===
  
É pedido o identificador do parceiro e apresentada a sua informação. O formato de apresentação é o seguinte (o valor das vendas efectuadas -- não inclui desagregações -- refere-se ao momento da venda; o valor das vendas pagas refere-se ao valor realmente pago): Note-se que compras e vendas são ponto de vista do entreposto e não do parceiro. Os valores dos pontos e dos preços são apresentados como inteiros (valores arredondados ao inteiro mais próximo).
+
O formato de apresentação de cada terminal é um dos seguintes:
  
  ''id''|''nome''|''endereço''|''estatuto''|''pontos''|''valor-compras''|''valor-vendas-efectuadas''|''valor-vendas-pagas''
+
  ''terminalType''|''terminalId''|''clientId''|''terminalStatus''|''balance-paid''|''balance-debts''|''friend1'',...,''friend''
 +
''terminalType''|''terminalId''|''clientId''|''terminalStatus''|''balance-paid''|''balance-debts''
  
O estatuto corresponde a '''NORMAL''', '''SELECTION''', '''ELITE''', conforme o caso.
+
Os valores para o campo ''terminalType'' são '''BASIC''' (terminais básicos) ou '''FANCY''' (terminais sofisticados). Os valores para o campo ''terminalStatus'' são '''IDLE''', '''OFF''', '''SILENCE''', '''BUSY'''. Os valores ''friend1'', ..., ''friendN'' são os identificadores dos terminais amigos e são apresentados por ordem crescente desses identificadores. O segundo formato é para o caso de um terminal que não tem amigos. Note-se que os valores dos pagamentos efectuados e os dos valores em dívida são apresentados separadamente.
  
Após esta linha, são apresentadas as notificações do parceiro (modo de entrega por omissão), pela ordem em que foram enviadas pela aplicação.
+
=== Registar terminal ===
  
''tipo-de-notificação''|''idProduto''|''preço-do-produto''
+
É pedido o número identificador do terminal. De seguida, o sistema pede o tipo de terminal ('''Prompt.terminalType()'''). A resposta deve ser '''BASIC''' (terminal básico) ou '''FANCY''' (terminal sofisticado). Se a resposta não corresponder a nenhum dos dois valores, a pergunta é repetida até se obter uma resposta válida. Finalmente, é pedido o identificador do cliente que ficará associado ao terminal.
  
Após esta visualização, considera-se que o parceiro fica sem notificações registadas.
+
Caso o identificador do terminal seja inválido, lança-se a excepção '''InvalidTerminalKeyException''', não se realizando o registo. Caso o identificador indicado já exista, deve ser lançada a excepção '''DuplicateTerminalKeyException''', não se realizando o registo.
  
=== Mostrar todos os parceiros ===
+
=== Menu da consola de um terminal ===
  
O formato de apresentação é como para parceiros individuais (opção anterior), mas não se apresentam as notificações dos parceiros.
+
É pedido o número identificador de um terminal, sendo aberto o correspondente menu da sua consola.
  
=== Registar parceiro ===
+
== Menu de Consultas ==
 
 
São pedidos o identificador do parceiro, o nome ('''Prompt.partnerName()''') (cadeia de caracteres) e o endereço do parceiro ('''Prompt.partnerAddress()''') (cadeia de caracteres) e regista-se o novo parceiro. Quando um parceiro é registado, aceita notificações relativas a todos os produtos.
 
  
Se já existir um parceiro com o mesmo identificador, deve ser lançada a excepção '''DuplicatePartnerKeyException''', não se realizando o registo.
+
Este menu apresenta as operações relacionadas com consultas. A lista completa é a seguinte: [[#Mostrar todas as comunicações|Mostrar todas as comunicações]], [[#Mostrar comunicações feitas por um cliente|Mostrar comunicações feitas por um cliente]], [[#Mostrar comunicações recebidas por um cliente|Mostrar comunicações recebidas por um cliente]], [[#Mostrar clientes sem dívidas|Mostrar clientes sem dívidas]], [[#Mostrar clientes com dívidas|Mostrar clientes com dívidas]], [[#Mostrar terminais sem actividade|Mostrar terminais sem actividade]], [[#Mostrar terminais com saldo positivo|Mostrar terminais com saldo positivo]].
  
=== Activar/desactivar notificações de um produto ===
+
Sempre que é pedido o identificador do cliente ('''Prompt.clientKey()'''), é lançada a excepção '''UnknownClientKeyException''' se o cliente indicado não existir. Sempre que é pedido o identificador de terminal ('''Prompt.terminalKey()'''), é lançada a excepção '''UnknownTerminalKeyException''' se o terminal indicado não existir.
  
É pedido o identificador do produto ('''Prompt.partnerKey()''') e o identificador do produto ('''Prompt.productKey()'''). Se as notificações relativas ao produto estavam activas para o parceiro em causa, passam a estar inactivas, e vice-versa. <!--É apresentada na saída o resultado da operação: '''notificationsOn()''' ou '''notificationsOff()'''.-->
+
A apresentação de resultados é como se indica nos casos já descritos de apresentação das várias entidades. Sempre que for feita uma consulta e nenhuma entidade satisfizer as condições associadas ao pedido, nada deve ser apresentado.
  
Se o produto indicado não existir, é lançada a excepção '''UnknownProductKeyException'''.
+
{{CVSCode|Estes comandos já estão parcialmente implementados nas classes da ''package'' '''prr.app.lookups''' (disponível no CVS), respectivamente: '''DoShowAllCommunications''', '''DoShowCommunicationsFromClient''', '''DoShowCommunicationsToClient''', '''DoShowClientsWithoutDebts''', '''DoShowClientsWithDebts''', '''DoShowUnusedTerminals''', '''DoShowTerminalsWithPositiveBalance'''.}}
  
=== Mostrar transacções de compra com parceiro ===
+
=== Mostrar todas as comunicações ===
  
É pedido o identificador do parceiro e apresentadas todas as compras com ele realizadas. O formato de apresentação é como descrito abaixo (visualização de transacções).
+
O formato de apresentação é o seguinte:
  
=== Mostrar transacções de venda (e desagregação) com parceiro ===
+
''type''|''idCommunication''|''idSender''|''idReceiver''|''units''|''price''|''status''
  
É pedido o identificador do parceiro e apresentadas todas as vendas (e desagregações) com ele realizadas. O formato de apresentação é como descrito abaixo (visualização de transacções).
+
Os possíveis valores para o campo ''type'' são '''VOICE''', '''TEXT''' ou '''VIDEO'''. Os campos ''idSender'' e ''idReceiver'' correspondem a identificadores de terminais. Os possíveis valores para o campo ''status'' são '''ONGOING''' (comunicação em curso) ou '''FINISHED''' (comunicação terminada). O valor de ''units'' corresponde às unidades de contabilização (caracteres ou minutos). Caso a comunicação esteja em curso, os valores de '''units''' e de '''price''' são ambos zero.
  
== Menu de Gestão de Transacções ==
+
=== Mostrar comunicações feitas por um cliente ===
Este menu apresenta as operações relacionadas com transacções. A lista completa é a seguinte: [[#Visualizar|Visualizar]], [[#Registar Desagregação (pelo entreposto a pedido de um parceiro)|Registar Desagregação]], [[#Registar Venda (do entreposto a um parceiro)|Registar Venda]], [[#Registar Compra (do entreposto a um parceiro)|Registar Compra]], [[#Receber Pagamento de Venda (do entreposto a um parceiro)|Receber Pagamento de Venda]].
 
  
<!--As etiquetas das opções deste menu estão definidas na classe '''ggc.app.transactions.Label'''. -->Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''ggc.app.transactions.Message'''.
+
É pedido o identificador do cliente, sendo apresentadas as comunicações iniciadas pelos seus terminais. O formato de apresentação é o descrito acima.
  
Sempre que é pedido o identificador do parceiro ('''Prompt.partnerKey()'''), é lançada a excepção '''UnknownPartnerKeyException''', se o parceiro indicado não existir. Sempre que é pedido o identificador de produto ('''Prompt.productKey()'''), é lançada a excepção '''UnknownProductKeyException''', se o produto indicado não existir. Sempre que é pedido o identificador da transacção ('''Prompt.transactionKey()'''), é lançada a excepção '''UnknownTransactionKeyException''', se a transacção indicada não existir.
+
=== Mostrar comunicações recebidas por um cliente ===
<!--
 
Se o parceiro indicado estiver inibido de efectuar transacções, deve ser lançada a excepção '''UnauthorizedPartnerException''' e o comando não tem efeito. --><!--Se o identificador de um produto não corresponder ao fornecedor indicado, deve ser lançada a excepção '''WrongSupplierException''' e o comando não tem efeito.-->
 
  
{{CVSCode|Estes comandos já estão implementados  nas classes da ''package'' '''ggc.app.transactions''' (disponível no CVS), respectivamente: '''DoShowTransaction''', '''DoRegisterBreakdownTransaction''', '''DoRegisterSaleTransaction''', '''DoRegisterAcquisitionTransaction''', '''DoReceivePayment'''.}}
+
É pedido o identificador do cliente, sendo apresentadas as comunicações recebidas pelos seus terminais. O formato de apresentação é o descrito acima.
  
=== Visualizar transacção ===
+
=== Mostrar clientes sem dívidas ===
  
O sistema pede o identificador da transacção a visualizar.
+
São apresentados os clientes sem dívidas. Utiliza-se o formato de apresentação de um cliente descrito anteriormente.
  
Nas apresentações, o campo ''valor-base'' é o valor da transacção sem multas/descontos.
+
=== Mostrar clientes com dívidas ===
  
Se a transacção for respeitante a uma venda a um parceiro, apresenta-se com o seguinte formato:
+
São apresentados os clientes por ordem decrescente do valor das respectivas dívidas (valores superiores a zero). Se as dívidas tiverem o mesmo valor, apresentam-se os clientes por ordem crescente do seu identificador. Utiliza-se o formato de apresentação de um cliente descrito anteriormente.
  
'''VENDA'''|''id''|''idParceiro''|''idProduto''|''quantidade''|''valor-base''|''valor-a-pagamento''|''data-limite''|''data-pagamento''
+
=== Mostrar terminais sem actividade ===
  
O campo ''valor-a-pagamento'' corresponde ao valor que será realmente pago (considerando possíveis multas/descontos à data da visualização). A data de pagamento (e o separador correspondente) só é apresentada se a venda tiver sido paga.
+
São apresentados os terminais que ainda não efectuaram nem receberam qualquer comunicação. O formato de apresentação é o descrito acima.
  
Se a transacção corresponder a uma compra a um parceiro, apresenta-se com o seguinte formato:
+
=== Mostrar terminais com saldo positivo ===
  
'''COMPRA'''|''id''|''idParceiro''|''idProduto''|''quantidade''|''valor-pago''|''data-pagamento''
+
São apresentados os terminais que que têm um valor de pagamentos estritamente superior ao valor das dívidas. O formato de apresentação é o descrito acima.
  
Se a transacção for uma desagregação, então deve ser apresentada, primeiro a "venda" do produto derivado, seguida da "compra" dos componentes obtidos na desagregação. O formato para N componentes é o seguinte:
+
== Menu da consola de um terminal ==
 +
Este menu apresenta as operações relacionadas com a consola de um terminal, i.e., o seu uso e administração. A lista completa é a seguinte: [[#Ligar terminal|Ligar terminal]],  [[#Desligar terminal|Desligar terminal]], [[#Silenciar terminal|Silenciar terminal]], [[#Adicionar amigo|Adicionar amigo]], [[#Retirar amigo|Retirar amigo]], [[#Efectuar pagamento|Efectuar pagamento]], [[#Mostrar informação sobre pagamentos e dívidas|Mostrar informação sobre pagamentos e dívidas]], [[#Enviar comunicação de texto|Enviar comunicação de texto]], [[#Iniciar comunicação interactiva|Iniciar comunicação interactiva]], [[#Terminar comunicação interactiva|Terminar comunicação interactiva]], [[#Mostrar comunicações em curso|Mostrar comunicações em curso]].
  
'''DESAGREGAÇÃO'''|''id''|''idPa''|''idPr''|''quantidade''|''vbase''|''vpag''|''data''|''idC1'':''q1'':''v1''#...#''idCN'':''qN'':''vN''
+
Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''prr.app.terminal.Message'''.
  
O valor base de uma desagregação corresponde à diferença entre a compra e a venda. O valor pago corresponde ao valor realmente pago pelo parceito (é um valor não negativo, podendo ser 0 caso o valor base seja negativo. Nesta linha, '''idPa''' é o identificador do parceiro; '''idPr''' é o identificador do produto; '''vbase''' é o valor real da transacção (diferencial); '''vpag''' é o valor a pagar (não negativo); '''data''' é a data da transacção; '''idCx''', '''qx''', '''vx''', são (respectivamente) o identificador, a quantidade e o valor do componente '''x''' (não confundir com valor unitário). Cada componente é separado do seguinte por '''#'''.
+
Sempre que é pedido o identificador do cliente ('''Prompt.clientKey()'''), é lançada a excepção '''UnknownClientKeyException''', se o cliente indicado não existir. Sempre que é pedido o identificador do terminal ('''Prompt.terminalKey()'''), é lançada a excepção '''UnknownTerminalKeyException''', se o terminal seleccionado não existir.
 
+
<!--
=== Registar Desagregação (pelo entreposto a pedido de um parceiro) ===
+
Se o parceiro indicado estiver inibido de efectuar transacções, deve ser lançada a excepção '''UnauthorizedPartnerException''' e o comando não tem efeito. --><!--Se o identificador de um produto não corresponder ao fornecedor indicado, deve ser lançada a excepção '''WrongSupplierException''' e o comando não tem efeito.-->
 
 
Para registar uma desagregação, é pedido o identificador do parceiro que pede a operação e o identificador do produto a desagregar e a respectiva quantidade ('''Prompt.amount()'''). Se a quantidade for superior às existências actuais, deve ser lançada a excepção '''UnavailableProductException''' (não se realiza a desagregação). Se o produto a desagregar não for derivado, a acção não tem efeito.
 
  
A actualização dos produtos do entreposto, bem como o respectivo valor, tem lugar logo após o registo da desagregação, ou seja, considera-se que a operação é instantânea.
+
{{CVSCode|Estes comandos já estão implementados nas classes da ''package'' '''prr.app.terminal''' (disponível no CVS), respectivamente: '''DoTurnOnTerminal''', '''DoOffTurnTerminal''', '''DoSilenceTerminal''', '''DoAddFriend''', '''DoRemoveFriend''', '''DoPerformPayment''', '''DoShowTerminalBalance''', '''DoSendTextCommunication''', '''DoStartInteractiveCommunication''', '''DoEndInteractiveCommunication''', '''DoShowOngoingCommunications'''.}}
  
Esta transacção regista o produto de origem e os lotes dos resultantes (associados ao parceiro que pede a operação), bem como os respectivos valores.
+
=== Colocar terminal em espera ===
 +
Coloca o terminal em espera. Se o terminal já estiver em espera, é apresentada a mensagem '''Message.alreadyOn()'''. Se não for possível colocar o terminal em espera, o comando não tem efeito.
  
=== Registar Venda (do entreposto a um parceiro) ===
+
=== Desligar terminal ===
 +
Desliga o terminal. Se o terminal já estiver desligado, é apresentada a mensagem '''Message.alreadyOff()''' e o comando não realiza outras acções. Se não for possível desligar o terminal, o comando não tem efeito.
  
Para registar uma venda, é pedido o identificador do parceiro, a data limite para o pagamento ('''Prompt.paymentDeadline()'''), o identificador do produto a vender e a respectiva quantidade ('''Prompt.amount()'''). Se a quantidade for superior às existências actuais, deve ser lançada a excepção '''UnavailableProductException''' (não se realiza a venda).
+
=== Silenciar terminal ===
 +
Coloca o terminal em silêncio. Se o terminal já estiver em silêncio, é apresentada a mensagem '''Message.alreadySilent()'''. Se não for possível colocar o terminal em silêncio, o comando não tem efeito.
  
A determinação das existências começa com o preço mais baixo e vai prosseguindo por todos os parceiros que tenham o produto disponível. O preço a pagar é determinado pelo preço das várias fracções.
+
=== Adicionar amigo ===
 +
É pedido o identificador do terminal a adicionar à lista de amigos. Se o terminal indicado já fizer parte da lista de amigos, a operação termina sem alterações.
  
Se a venda corresponder a um produto derivado e não existir produto suficiente nos lotes disponíveis, o produto pode ser fabricado a partir de componentes de outros lotes. As existências dos componentes têm de ser suficientes para satisfazer a totalidade das necessidades de produto derivado a fabricar. <!--O produto fabricado é colocado num lote associado ao parceiro comprador. -->O preço é calculado como descrito acima.
+
=== Retirar amigo ===
 +
É pedido o identificador do terminal a retirar da lista de amigos. Se o terminal indicado não fizer parte da lista de amigos, a operação termina sem alterações.
  
A actualização dos produtos do entreposto tem lugar logo após o registo da venda, ou seja, considera-se que a venda é instantânea.
+
=== Efectuar pagamento ===
 +
É pedido o identificador da comunicação a pagar ('''Prompt.commKey()'''). A comunicação tem de ter sido iniciada pelo terminal actual e tem de ter terminado e não pode ter sido paga. Caso contrário, é apresentada a mensagem '''Message.invalidCommunication()'''.
  
=== Registar Compra (do entreposto a um parceiro) ===
+
=== Mostrar informação sobre pagamentos e dívidas ===
 +
São apresentados os valores dos pagamentos e das dívidas do terminal ('''Message.terminalPaymentsAndDebts()''').
  
É pedido o identificador do parceiro a quem se realiza a compra, o identificador do produto que se está a comprar, o preço do produto ('''Prompt.price()''') e a respectiva quantidade ('''Prompt.amount()'''). O produto comprado mantém informação sobre o processo de aquisição: em particular, se vários parceiros fornecerem o mesmo produto, podem fazê-lo com preços diferentes. O entreposto poderá, então, vender um dado produto com diferentes preços.
+
=== Enviar comunicação de texto ===
 +
Esta operação está disponível para um terminal que não esteja desligado ou em comunicação. Permite enviar uma comunicação de texto para outro terminal.
  
Se o identificador do produto for desconhecido, então é perguntado ao parceiro se quer introduzir uma receita para um produto derivado ('''Prompt.addRecipe()'''). Se a resposta for negativa, trata-se de um produto simples. Caso contrário, é pedido o número de componentes da receita ('''Prompt.numberOfComponents()'''), o valor do agravamento ('''Prompt.alpha()''') e, em ciclo, os identificadores ('''Prompt.productkey()''') e quantidades ('''Prompt.amount()''') dos vários componentes.
+
É pedido o número do terminal de destino e o corpo da mensagem ('''Prompt.textMessage()''').
  
A actualização de existências dos produtos do entreposto tem lugar logo após o registo da compra, ou seja, considera-se que a compra é instantânea. A actualização do saldo do entreposto também é assumida como instantânea, i.e., assume-se que a compra é paga a pronto.
+
Quando o terminal de destino está desligado, é apresentada a mensagem '''Message.destinationIsOff()'''.
  
=== Receber Pagamento de Venda (do entreposto a um parceiro) ===
+
=== Iniciar comunicação interactiva ===
 +
Esta operação está disponível caso o terminal selecionado não esteja desligado ou em comunicação. Permite estabelecer uma comunicação interactiva entre o terminal selecionado com outro terminal.
  
Apenas é permitido o pagamento de vendas a parceiros. Tentativas de pagamento de transacções que não correspondam a vendas não produzem nenhum resultado.
+
É pedido o número do terminal de destino e o tipo de comunicação ('''Prompt.commType()'''). A resposta deve ser uma das seguintes opções (cadeia de caracteres): '''VIDEO''', '''VOICE'''. Se a resposta não corresponder a nenhum destes valores, a pergunta é repetida até se obter uma resposta válida.
  
É pedido o identificador da venda a pagar. Se a venda já tiver sido paga, não é realizada nenhuma acção.
+
Se se tenta iniciar uma comunicação não suportada pelo terminal de origem ou pelo terminal de destino, devem ser apresentadas as '''Message.unsupportedAtOrigin()''' ou '''Message.unsupportedAtDestination()''', respectivamente.
  
== Menu de Consultas ==
+
Quando o terminal de destino está desligado, é apresentada a mensagem '''Message.destinationIsOff()'''. Quando o terminal de destino está ocupado (ou quando é tentada uma ligação para o próprio), é apresentada a mensagem '''Message.destinationIsBusy()'''. Quando o terminal de destino está em silêncio, é apresentada a mensagem '''Message.destinationIsSilent()'''.
  
Este menu apresenta as operações relacionadas com consultas. A lista completa é a seguinte: [[#Mostrar produtos com preço abaixo de limite|Mostrar produtos com preço abaixo de limite]], [[#Mostrar facturas pagas por parceiro|Mostrar facturas pagas por parceiro]].
+
=== Terminar comunicação interactiva ===
  
<!--As etiquetas das opções deste menu estão definidas na classe '''ggc.app.lookups.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''ggc.app.lookups.Message'''.
+
Esta operação está disponível para um terminal que iniciou uma comunicação interactiva enquanto a comunicação está em curso. O terminal de destino não pode interromper a comunicação, pelo que o comando não está disponível. O comando permite terminar a comunicação e registar a sua duração.
-->
 
Sempre que é pedido o identificador do parceiro ('''Prompt.partnerKey()'''), é lançada a excepção '''UnknownPartnerKeyException''', se o parceiro indicado não existir. Sempre que é pedido o identificador de produto ('''Prompt.productKey()'''), é lançada a excepção '''UnknownProductKeyException''', se o produto indicado não existir.
 
  
A apresentação de resultados é como indicado nos casos já descritos de apresentação das várias entidades.
+
É pedida a duração da comunicação ('''Prompt.duration()''', em minutos). Após o término da comunicação, é apresentado o seu custo, através da mensagem '''Message.communicationCost()'''.
  
{{CVSCode|Estes comandos já estão parcialmente implementados  nas classes da ''package'' '''ggc.app.lookups''' (disponível no CVS), respectivamente: '''DoLookupProductBatchesUnderGivenPrice''', '''DoLookupPaymentsByPartner'''.}}
+
=== Mostrar comunicação em curso ===
  
=== Mostrar lotes de produtos com preço abaixo de limite ===
+
É apresentada a comunicação em curso (de acordo com o formato indicado acima).
  
Pede-se o valor limite pretendido ('''Prompt.priceLimit()''') e apresentam-se todos os lotes cujo preço é inferior ao preço indicado. É apresentado um lote por linha.
+
Se não houver nenhuma comunicação em curso, é apresentada a mensagem '''Message.noOngoingCommunication()'''.
 
 
=== Mostrar transacções pagas por parceiro ===
 
 
 
Pede-se o identificador do parceiro e apresentam-se as transacções do parceiro que já estão pagas (uma transacção por linha).<!-- Não são considerados pagamentos de desagregações.-->
 
  
 
= Leitura de Dados a Partir de Ficheiros Textuais =
 
= Leitura de Dados a Partir de Ficheiros Textuais =
Line 441: Line 413:
 
Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''.
 
Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''.
  
As várias entidades têm os formatos descritos abaixo. Assume-se que os títulos não podem conter o carácter '''|''' e que o preço é um número inteiro (sugere-se a utilização do método '''String.split''' para o processamento preliminar destas linhas). Não existem entradas mal-formadas.
+
As várias entidades têm os formatos descritos abaixo. Assume-se que os títulos não podem conter o carácter '''|''' (sugere-se a utilização do método '''String.split''' para o processamento preliminar destas linhas). Não existem entradas mal-formadas.
 
 
Cada linha tem uma descrição distinta, mas que segue os seguintes formatos.
 
  
PARTNER|''id''|''nome''|''endereço''
+
Cada linha tem uma descrição distinta, mas que segue os seguintes formatos:
  
Os lotes têm as seguintes definições (respectivamente, para produtos simples e derivados). Produtos derivados (é apresentado o produto e a lista de componentes e respectivas quantidades da sua receita):
+
CLIENT|''id''|''nome''|''taxId''
 +
''terminal-type''|''idTerminal''|''idClient''|''state''
 +
FRIENDS|''idTerminal''|''idTerminal1'',...,''idTerminalN''
  
BATCH_S|''idProduto''|''idParceiro''|''preço''|''stock-actual''
+
As definições de clientes precedem sempre as dos terminais. As ligações entre amigos estão sempre após a definição das restantes entidades. Para os terminais, ''terminal-type'' é '''BASIC''' ou '''FANCY'''.
BATCH_M|''idProduto''|''idParceiro''|''preço''|''stock-actual''|''agravamento''|''componente-1'':''quantidade-1''#...#''componente-n'':''quantidade-n''
 
 
 
As definições de parceiros precedem sempre as dos lotes.
 
  
 
Um exemplo de conteúdo do ficheiro inicial é como se segue:
 
Um exemplo de conteúdo do ficheiro inicial é como se segue:
Line 458: Line 427:
 
{{CollapsedCode|Exemplo de ficheiro de entrada textual|
 
{{CollapsedCode|Exemplo de ficheiro de entrada textual|
 
<source lang="text">
 
<source lang="text">
PARTNER|S1|Toshiba|Tokyo, Japan
+
CLIENT|cli001|Manuel Pinheiro|103443
PARTNER|W2|Papelaria Fernandes|Oeiras, Portugal
+
CLIENT|cli002|Pedro Pinheiro|103447
PARTNER|P1|Publicações Europa-América|Lisboa, Portugal
+
CLIENT|Cli201|Ludgero Oliveira|103440
PARTNER|P3|O’Reilly|Köln, Germany
+
CLIENT|cli Es|Maria Eucalipto|103441
PARTNER|R2|Jorge Figueiredo|Lisboa, Portugal
+
CLIENT|01|Oliveira Preto|103547
PARTNER|E4|Filomena Figueiredo|Lisboa, Portugal
+
CLIENT|cli003|Pedro Oliveira|103449
PARTNER|ER|Abdul Figueiredo|Casablanca, Morocco
+
BASIC|969001|cli001|ON
PARTNER|O9|Hellen Figueiredo|San Francisco, CA, USA
+
BASIC|969003|cli002|ON
PARTNER|H2SO4|John Figueiredo|Wellington, New Zealand
+
FANCY|969002|cli002|SILENCE
PARTNER|H2O|Rohit Figueiredo|New Delhi, India
+
FANCY|969007|cli Es|ON
BATCH_S|HIDROGÉNIO|S1|200|5000
+
BASIC|969008|cli003|OFF
BATCH_S|OXIGÉNIO|S1|1200|2500
+
BASIC|969009|cli003|OFF
BATCH_M|ÁGUA|R2|800|5000|0.1|HIDROGÉNIO:2#OXIGÉNIO:1
+
BASIC|969010|cli003|ON
 +
FANCY|969006|cli003|ON
 +
FANCY|969005|cli003|ON
 +
BASIC|969004|cli003|ON
 +
FRIENDS|969001|969008,969009,969004
 +
FRIENDS|969004|969001
 +
FRIENDS|969003|969008
 
</source>
 
</source>
 
}}
 
}}
Line 479: Line 454:
 
= Execução dos Programas e Testes Automáticos =
 
= Execução dos Programas e Testes Automáticos =
  
Usando os ficheiros '''test.import''', '''test.in''' e '''test.out''', é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável '''CLASSPATH''' (ou da opção equivalente '''-cp''' do comando '''java'''), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação ('''ggc.app.App.main'''). As propriedades são tratadas automaticamente pelo código de apoio.
+
Usando os ficheiros '''test.import''', '''test.in''' e '''test.out''', é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável '''CLASSPATH''' (ou da opção equivalente '''-cp''' do comando '''java'''), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação ('''prr.app.App.main'''). As propriedades são tratadas automaticamente pelo código de apoio.
  
         java -Dimport=test.import -Din=test.in -Dout=test.outhyp ggc.app.App
+
         java -Dimport=test.import -Din=test.in -Dout=test.outhyp prr.app.App
  
 
Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída '''test.outhyp'''. Em caso de sucesso, os ficheiros das saídas esperada ('''test.out''') e obtida ('''test.outhyp''') devem ser iguais. A comparação pode ser feita com o comando:
 
Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída '''test.outhyp'''. Em caso de sucesso, os ficheiros das saídas esperada ('''test.out''') e obtida ('''test.outhyp''') devem ser iguais. A comparação pode ser feita com o comando:
Line 497: Line 472:
 
A representação e manipulação de datas e tempos deve ser realizada através das classes da ''package'' '''[https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html java.time]''', em particular, através das classes '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html java.time.LocalDate]''' e '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html java.time.LocalTime]'''. Diferenças entre tempos são representadas pela classe '''[https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html java.time.Duration]'''.
 
A representação e manipulação de datas e tempos deve ser realizada através das classes da ''package'' '''[https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html java.time]''', em particular, através das classes '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html java.time.LocalDate]''' e '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html java.time.LocalTime]'''. Diferenças entre tempos são representadas pela classe '''[https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html java.time.Duration]'''.
 
-->
 
-->
<!--
+
 
 
[[category:Ensino]]
 
[[category:Ensino]]
 
[[category:PO]]
 
[[category:PO]]
 
[[category:Projecto de PO]]
 
[[category:Projecto de PO]]
 
[[en:Object-Oriented Programming]]
 
[[en:Object-Oriented Programming]]
-->
 

Latest revision as of 16:27, 29 October 2022

AVISOS - Avaliação em Época Normal

Esclarecimento de dúvidas:

  • Consultar sempre o corpo docente atempadamente: presencialmente ou através do endereço oficial da disciplina [1].
  • Não utilizar fontes de informação não oficialmente associadas ao corpo docente (podem colocar em causa a aprovação à disciplina).
  • Não são aceites justificações para violações destes conselhos: quaisquer consequências nefastas são da responsabilidade do aluno.

Requisitos para desenvolvimento, material de apoio e actualizações do enunciado (ver informação completa em Projecto de Programação com Objectos):

  • O material de apoio é de uso obrigatório e não pode ser alterado.
  • Verificar atempadamente (mínimo de 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de desenvolvimento.

Processo de avaliação (ver informação completa em Avaliação do Projecto):

  • Datas: 2022/10/04 12:00 (inicial); 2022/10/21 12:00 (intercalar); 2022/11/04 12:00 (final); 2022/11/04 (early bird) 2022/11/07 (normal) (teste prático).
  • Todas as entregas são cruciais para o bom desenvolvimento do projecto, sendo obrigatórias: a não realização de uma entrega implica a exclusão da avaliação do projecto e, por consequência, da avaliação da disciplina.
  • Verificar atempadamente (até 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de avaliação, incluindo a capacidade de acesso ao repositório.
  • Apenas se consideram para avaliação os projectos existentes no repositório oficial. Apenas se considera para avaliação o ramo 'main'.
  • Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório ou de outros materiais, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.
  • A avaliação do projecto pressupõe o compromisso de honra de que o trabalho correspondente foi realizado pelos alunos correspondentes ao grupo de avaliação.
  • Fraudes na execução do projecto terão como resultado a exclusão dos alunos implicados do processo de avaliação.
Material de Uso Obrigatório
As bibliotecas po-uilib e o conteúdo inicial do CVS são de uso obrigatório:
  • po-uilib (classes de base) po-uilib-202209081626.tar.bz2 (não pode ser alterada) - javadoc
  • prr-core (classes do "core") (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
  • prr-app (classes de interacção) (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
A máquina virtual, fornecida para desenvolvimento do projecto, já contém todo o material de apoio.
Uso Obrigatório: Repositório CVS
Apenas se consideram para avaliação os projectos existentes no repositório CVS oficial.

Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.

Contents

ÉPOCA NORMAL

O objectivo do projecto e desenvolver uma aplicação de gestão de uma rede de terminais de comunicação, denominada por prr. Genericamente, o programa permite o registo, gestão e consulta de clientes, terminais e comunicações.

Clientes, terminais, comunicações, planos tarifários

Os clientes, terminais e comunicações possuem chaves únicas, cadeias de caracteres para os clientes e para os terminais e inteiros para as comunicações.

A noção de saldo é definida como a diferença entre os valores dos pagamentos efectuados e das dívidas por pagar. O saldo pode ser calculado globalmente (considerando todos os clientes), por cliente (considerando todos os terminais do cliente) ou por terminal (considerando todas as comunicações do terminal, pagas e por pagar).

Propriedades e funcionalidade dos clientes

Cada cliente, para além da chave única, tem ainda o nome (cadeia de caracteres) e o número de identificação fiscal (inteiro). A cada cliente podem estar associados vários terminais.

O cliente mantém informação sobre os pagamentos efectuados (sobre comunicações passadas) e valores em dívida (comunicações cujo valor ainda não foi pago).

Existem três tipos de clientes: Normal (situação inicial, após o registo -- no entanto, ver a situação da leitura de dados textuais), Gold e Platinum. O tipo de cliente influencia o custo das comunicações que efectua (ver planos tarifários). O tipo do cliente evolui nas seguintes condições:

Antes Depois Condição
Normal Gold O saldo do cliente (após realizar um pagamento) é superior a 500 créditos.
Normal Platinum (não é possível)
Gold Normal O saldo do cliente (após realizar uma comunicação) é negativo.
Gold Platinum O cliente realizou 5 comunicações de vídeo consecutivas e não tem saldo negativo. A contabilização da 5ª comunicação ainda considera que o cliente é do tipo Gold.
Platinum Gold O cliente realizou 2 comunicações de texto consecutivas e não tem saldo negativo. A contabilização da 2ª comunicação ainda considera que o cliente é do tipo Platinum.
Platinum Normal O saldo do cliente (após realizar uma comunicação) é negativo.

Propriedades e funcionalidade dos terminais

Cada terminal é identificado por uma cadeia de caracteres numérica (exactamente 6 dígitos) e está associado a um único cliente.

Os terminais podem realizar três tipos de comunicação: texto, voz e vídeo. As comunicações realizadas pelo terminal são contabilizadas de acordo com o tarifário associado ao cliente. O terminal tem contabilidade própria, sendo sempre possível saber os valores dos pagamentos efectuados e dos valores devidos.

Existem, pelo menos, dois tipos de terminal: básicos e sofisticados. Os terminais básicos só conseguem realizar comunicações de texto e de voz, não podendo nem iniciar nem receber comunicações de vídeo. Os terminais sofisticados podem realizar todos os tipos de comunicação.

Cada terminal tem uma lista de amigos (inicialmente vazia). Um terminal não pode ser amigo de si próprio.

Um terminal recém-criado fica no estado de espera (idle); tem os valores de pagamentos e dívidas ambos a zero. Existem outros estados, definidos a seguir.

Estados dos terminais

Cada terminal pode estar em espera, em silêncio, ocupado ou desligado.

  • Espera -- situação normal sem actividade (idle);
  • Silêncio -- tal como em Espera, pode iniciar-se qualquer tipo de comunicação suportada pelo terminal, mas só podem ser recebidas comunicações de texto;
  • Ocupado -- não podendo iniciar-se comunicações, mas podem ser recebidas mensagens de texto;
  • Desligado -- não podem ser iniciadas ou recebidas comunicações.

Transições entre estados de terminais

Um terminal pode chegar aos vários estados nas seguintes condições (outras transições não são possíveis):

  • Espera -- de desligado (ao ligar em espera); de silêncio (ir para espera); de ocupado (final de comunicação);
  • Silêncio -- de desligado (ao ligar em silêncio); de espera (colocar em silêncio); de ocupado (final de comunicação);
  • Ocupado -- de espera ou de silêncio (início de comunicação);
  • Desligado -- de espera ou de silêncio (ao desligar).

Notificações

Apenas são passíveis de notificação os clientes que tentaram comunicação com um terminal e a comunicação não foi possível nessa altura. Quando uma comunicação não se efectua, regista-se a tentativa de contacto, para que, assim que seja possível a realização do contacto pretendido, se enviarem notificações aos clientes associados aos terminais de origem. O registo da tentativa de contactos só tem lugar quando o cliente do terminal de origem tem activa a recepção de contactos falhados no instante em que se tentou efectuar a comunicação (por omissão, a recepção de notificações está activa).

São geradas notificações e é possível avisar um cliente nas seguintes circunstâncias:

  1. Um terminal desligado é colocado em silêncio (off-to-silent): notifica-se disponibilidade para receber comunicações de texto.
  2. Um terminal desligado ou em silêncio é colocado em espera (off-to-idle ou silent-to-idle): notifica-se disponibilidade para receber comunicações (qualquer suportada).
  3. Um terminal deixa de estar ocupado (busy-to-idle): notifica-se disponibilidade para receber comunicações (qualquer suportada).

Propriedades e funcionalidade das comunicações

Cada comunicação tem um identificador único (número inteiro, no contexto de todos os clientes). A primeira tem como identificador “1”, sendo os identificadores subsequentes obtidos por incremento unitário do mais recente identificador utilizado. A comunicação contém ainda informação sobre os terminais de origem e de destino e o estado da comunicação: em curso ou terminada.

As comunicações de texto têm ainda a mensagem enviada. As comunicações interactivas (vídeo e voz) possuem informação sobre a duração da comunicação. O custo de uma comunicação depende do comprimento da mensagem de texto ou da duração das comunicações interactivas. O custo depende ainda do plano tarifário associado a cada cliente (calculado no final da comunicação). Todos os cálculos envolvendo os custos das comunicações devem ser realizados sem arredondamentos.

Um terminal não pode estabelecer uma comunicação interactiva consigo próprio.

Planos tarifários

Cada plano tarifário define os custos para cada tipo de comunicação, baseado no nível do cliente, no tipo de comunicação, entre outras características. Os planos tarifários têm um nome único no contexto da rede de terminais a que estão associados. A rede de terminais pode oferecer vários planos tarifários mas em cada momento um cliente apenas tem um plano tarifário. A rede de terminais oferece pelo menos o plano tarifário designado como base. Este plano tarifário é o plano atribuído inicialmente a todos os clientes. O custos das comunicações é medido em créditos.

O custo (medido em créditos) de uma comunicação de texto com N caracteres no plano tarifário base está representado na tabela seguinte:

Normal Gold Platinum
N < 50 caracteres 10 10 0
50 caracteres <= N < 100 caracteres 16 10 4
N >= 100 caracteres 2 x N 2 x N 4

Quando é efectuada uma comunicação de voz ou de vídeo, o custo no plano tarifário base é proporcional ao tempo de conversação e, quando se comunica com um terminal amigo, é aplicado um desconto de 50%. O custo, em créditos por minuto, é o seguinte para terminais não amigos:

Normal Gold Platinum
Comunicação de voz 20 10 10
Comunicação de vídeo 30 20 10

O custo de uma comunicação deve ser calculado quando a comunicação termina e guardado, por forma a garantir que o custo não é afectado por mudanças futuras dos planos tarifários.

Entrega de notificações

Os clientes podem activar a recepção de notificações sobre eventos associados a terminais em algumas circunstâncias. Em qualquer momento, um cliente pode activar ou desactivar essas notificações. A entrega de notificações deve ser flexível e deve prever vários meios de entrega, e.g., correio postal, SMS, email, entre outras. O meio de entrega por omissão corresponde a registar a notificação na aplicação.

As notificações contêm informação acerca da sua natureza e do terminal a que dizem respeito. Um dado evento apenas produz uma notificação por cliente (o conjunto de clientes a notificar é limpo após o envio da notificação).

Funcionalidade da aplicação

A aplicação permite manter informação sobre as entidades do modelo. Possui ainda a capacidade de preservar o seu estado (não é possível manter várias versões do estado da aplicação em simultâneo).

Deve ser possível efectuar pesquisas sujeitas a vários critérios e sobre as diferentes entidades geridas pela aplicação.

Uma base de dados textual com conceitos pré-definidos pode ser carregada no início da aplicação.

Note-se que não é necessário nem desejável implementar de raiz a aplicação: já existem classes que representam e definem a interface geral da funcionalidade do core da aplicação, tal como é visível pelos comandos da aplicação.
A interface geral do core já está parcialmente implementada na classe prr.Manager e outras fornecidas (cujos nomes devem ser mantidos), devendo ser adaptadas onde necessário. É ainda necessário criar e implementar as restantes classes que suportam a operação da aplicação.

Serialização

É possível guardar e recuperar o estado actual da aplicação, preservando toda a informação relevante, descrita acima.

Requisitos de Desenho

Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Assim, deve ser possível: definir novos tipos de clientes; definir novos tipos de comunicação; definir novos planos tarifários; definir novas formas de pesquisa; permitir a gestão de várias redes de terminais.

Embora na especificação actual não seja possível remover algumas entidades, a inclusão desta funcionalidade deve ser prevista, por forma a minimizar o impacto da sua futura inclusão.

Interacção com o utilizador

Descreve-se nesta secção a funcionalidade máxima da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de procederem à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção Sair (fecha o menu).

As operações de pedido e apresentação de informação ao utilizador devem realizar-se através dos objectos form e display, respectivamente, presentes em cada comando. As mensagens são produzidas pelos métodos das bibliotecas de suporte (po-uilib e prr-app). As mensagens não podem ser usadas no núcleo da aplicação (prr-core). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.

A apresentação de valores monetários é sempre feita com arredondamento ao inteiro mais próximo, mas a representação interna não deve ser arredondada.

A apresentação de listas de entidades do domínio (clientes, etc.) faz-se por ordem crescente da respectiva chave: dependendo dos casos, a ordem pode ser numérica ou lexicográfica (UTF-8), não havendo distinção entre maiúsculas e minúsculas.

As excepções usadas na interacção (subclasses de pt.tecnico.uilib.menus.CommandException), excepto se indicado, são lançadas pelos comandos (subclasses de pt.tecnico.uilib.menus.Command) e tratadas pelos menus (instâncias de subclasses de pt.tecnico.uilib.menus.Menu). Outras excepções não devem substituir as fornecidas nos casos descritos.

Note-se que o programa principal e os comandos e menus, a seguir descritos, já estão parcialmente implementados nas packages prr.app, prr.app.clients, prr.app.lookups, prr.app.main, prr.app.terminal, prr.app.terminals. Estas classes são de uso obrigatório e estão disponíveis no CVS (módulo prr-app).

Menu Principal

As acções deste menu permitem gerir a salvaguarda do estado da aplicação, abrir submenus e aceder a alguma informação global. A lista completa é a seguinte: Abrir, Guardar, Gestão de clientes, Gestão de terminais, Consultas, Mostrar informação global sobre pagamentos e dívidas.

As etiquetas das opções deste menu estão definidas na classe prr.app.main.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe prr.app.main.Message.

Estes comandos já estão implementados nas classes da package prr.app.main (disponível no CVS), respectivamente: DoOpenFile, DoSaveFile, DoOpenMenuClient, DoOpenMenuTerminals, DoOpenMenuLookups, DoShowGlobalBalance.

Salvaguarda do estado actual da aplicação

Inicialmente, a aplicação está vazia ou tem apenas informação sobre as entidades que foram carregadas via ficheiro textual.

O conteúdo da aplicação (toda a informação actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: java.io.Serializable). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:

  • Abrir -- Carrega os dados de uma sessão anterior a partir de um ficheiro previamente guardado (ficando este ficheiro associado à aplicação, para futuras operações de salvaguarda). Pede-se o nome do ficheiro a abrir (Prompt.openFile()). Caso ocorra um problema na abertura ou processamento do ficheiro, deve ser lançada a excepção FileOpenFailedException. A execução bem-sucedida desta opção substitui toda a informação da aplicação.
  • Guardar -- Guarda o estado actual da aplicação no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado (para operações de salvaguarda subsequemtes). Esta interacção realiza-se através do método Prompt.newSaveAs(). Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.

Note-se que a opção Abrir não permite a leitura de ficheiros de texto (estes apenas podem ser utilizados no início da aplicação).

A opção Sair nunca implica a salvaguarda do estado da aplicação, mesmo que existam alterações.

Gestão e consulta de dados da aplicação

  • Menu de Gestão de Clientes -- Abre o menu de gestão de clientes.
  • Menu de Gestão de Terminais -- Abre o menu de gestão de terminais.
  • Menu de Consultas -- Abre o menu de consultas (pesquisas).

Mostrar informação global sobre pagamentos e dívidas

Esta opção apresenta os valores globais correspondentes a pagamentos e dívidas (soma dos valores parciais para todos os clientes registados), através da mensagem Message.globalPaymentsAndDebts().

Menu de gestão de clientes

Este menu permite efectuar operações sobre a base de dados de clientes. A lista completa é a seguinte: Visualizar cliente, Visualizar todos os clientes, Registar cliente, Activar recepção de contactos falhados, Desactivar recepção de contactos falhados, Mostrar informação sobre pagamentos e dívidas de cliente.

Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe prr.app.clients.Message.

Sempre que é pedido o identificador de um cliente (Prompt.key()) e o identificador não existir (excepto no processo de registo), é lançada a excepção UnknownClientKeyException. Na ocorrência de excepções, as operações não têm efeito.

Estes comandos já estão implementados nas classes da package prr.app.clients (disponível no CVS), respectivamente: DoShowClient, DoShowAllClients, DoRegisterClient, DoEnableClientNotifications, DoDisableClientNotifications, DoShowClientPaymentsAndDebts.

Visualizar cliente

É pedido o identificador do cliente e apresentada a sua informação. O formato de apresentação de cada cliente é como se indica de seguida. Os valores para type são NORMAL, GOLD, ou PLATINUM. Os valores para notifications são YES ou NO (notificações activas/inactivas). O campo terminals representa o número de terminais associados ao cliente, payments é o valor dos pagamentos do cliente e debts é o valor das dívidas do cliente. Se o cliente não tiver terminais, os valores de terminals, payments e debts são 0 (zero).

CLIENT|key|name|taxId|type|notifications|terminals|payments|debts

Após esta linha, são apresentadas as notificações do cliente (modo de entrega por omissão), pela ordem em que foram enviadas pela aplicação.

tipo-de-notificação|idTerminal

O tipo de notificação é um de O2S (off-to-silent), O2I (off-to-idle), B2I (busy-to-idle) ou S2I (silent-to-idle), tal como descritos acima. Após esta visualização, considera-se que o cliente fica sem notificações registadas.

Visualizar todos os clientes

O formato de apresentação é como para clientes individuais (opção anterior), mas não se apresentam as notificações dos clientes (nem se limpam as listas correspondentes).

Registar cliente

O sistema pede o identificador que ficará associado ao cliente (identificador único}. De seguida, pede o nome do cliente (Prompt.name()) e o número de identificação fiscal (Prompt.taxId()). Após o registo, o cliente fica no estado Normal e o registo de contactos falhados fica activo.

Caso o identificador indicado já exista, deve ser lançada a excepção DuplicateClientKeyException, não se realizando o registo.

Activar recepção de contactos falhados

É pedido o identificador do cliente. Se o registo de contactos falhados já estava activo, o cliente não é alterado e é apresentada a mensagem Message.clientNotificationsAlreadyEnabled().

Desactivar recepção de contactos falhados

É pedido o identificador do cliente. Se o registo de contactos falhados já estava inactivo, o cliente não é alterado e é apresentada a mensagem Message.clientNotificationsAlreadyDisabled().

Mostrar informação sobre pagamentos e dívidas de cliente

O sistema pede o identificador do cliente, apresentando os valores dos seus pagamentos e dívidas (Message.clientPaymentsAndDebts()).

Menu de Gestão de Terminais

Este menu permite efectuar operações sobre a base de dados de terminais. A lista completa é a seguinte: Mostrar todos os terminais, Registar terminal, Menu da consola de um terminal.

Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe prr.app.terminals.Message.

Sempre que for pedido o identificador de um terminal (Prompt.terminalKey()) e o terminal não existir, é lançada a excepção UnknownTerminalKeyException (excepto no processo de registo). Sempre que é pedido o identificador de um cliente (Prompt.clientKey()) e o identificador não existir, é lançada a excepção UnknownClientKeyException. Na ocorrência de excepções (estas ou outras), as operações não têm efeito.

Estes comandos já estão implementados nas classes da package prr.app.terminals (disponível no CVS), respectivamente: DoShowAllTerminals, DoRegisterTerminal, DoOpenMenuTerminalConsole.

Mostrar todos os terminais

O formato de apresentação de cada terminal é um dos seguintes:

terminalType|terminalId|clientId|terminalStatus|balance-paid|balance-debts|friend1,...,friend
terminalType|terminalId|clientId|terminalStatus|balance-paid|balance-debts

Os valores para o campo terminalType são BASIC (terminais básicos) ou FANCY (terminais sofisticados). Os valores para o campo terminalStatus são IDLE, OFF, SILENCE, BUSY. Os valores friend1, ..., friendN são os identificadores dos terminais amigos e são apresentados por ordem crescente desses identificadores. O segundo formato é para o caso de um terminal que não tem amigos. Note-se que os valores dos pagamentos efectuados e os dos valores em dívida são apresentados separadamente.

Registar terminal

É pedido o número identificador do terminal. De seguida, o sistema pede o tipo de terminal (Prompt.terminalType()). A resposta deve ser BASIC (terminal básico) ou FANCY (terminal sofisticado). Se a resposta não corresponder a nenhum dos dois valores, a pergunta é repetida até se obter uma resposta válida. Finalmente, é pedido o identificador do cliente que ficará associado ao terminal.

Caso o identificador do terminal seja inválido, lança-se a excepção InvalidTerminalKeyException, não se realizando o registo. Caso o identificador indicado já exista, deve ser lançada a excepção DuplicateTerminalKeyException, não se realizando o registo.

Menu da consola de um terminal

É pedido o número identificador de um terminal, sendo aberto o correspondente menu da sua consola.

Menu de Consultas

Este menu apresenta as operações relacionadas com consultas. A lista completa é a seguinte: Mostrar todas as comunicações, Mostrar comunicações feitas por um cliente, Mostrar comunicações recebidas por um cliente, Mostrar clientes sem dívidas, Mostrar clientes com dívidas, Mostrar terminais sem actividade, Mostrar terminais com saldo positivo.

Sempre que é pedido o identificador do cliente (Prompt.clientKey()), é lançada a excepção UnknownClientKeyException se o cliente indicado não existir. Sempre que é pedido o identificador de terminal (Prompt.terminalKey()), é lançada a excepção UnknownTerminalKeyException se o terminal indicado não existir.

A apresentação de resultados é como se indica nos casos já descritos de apresentação das várias entidades. Sempre que for feita uma consulta e nenhuma entidade satisfizer as condições associadas ao pedido, nada deve ser apresentado.

Estes comandos já estão parcialmente implementados nas classes da package prr.app.lookups (disponível no CVS), respectivamente: DoShowAllCommunications, DoShowCommunicationsFromClient, DoShowCommunicationsToClient, DoShowClientsWithoutDebts, DoShowClientsWithDebts, DoShowUnusedTerminals, DoShowTerminalsWithPositiveBalance.

Mostrar todas as comunicações

O formato de apresentação é o seguinte:

type|idCommunication|idSender|idReceiver|units|price|status

Os possíveis valores para o campo type são VOICE, TEXT ou VIDEO. Os campos idSender e idReceiver correspondem a identificadores de terminais. Os possíveis valores para o campo status são ONGOING (comunicação em curso) ou FINISHED (comunicação terminada). O valor de units corresponde às unidades de contabilização (caracteres ou minutos). Caso a comunicação esteja em curso, os valores de units e de price são ambos zero.

Mostrar comunicações feitas por um cliente

É pedido o identificador do cliente, sendo apresentadas as comunicações iniciadas pelos seus terminais. O formato de apresentação é o descrito acima.

Mostrar comunicações recebidas por um cliente

É pedido o identificador do cliente, sendo apresentadas as comunicações recebidas pelos seus terminais. O formato de apresentação é o descrito acima.

Mostrar clientes sem dívidas

São apresentados os clientes sem dívidas. Utiliza-se o formato de apresentação de um cliente descrito anteriormente.

Mostrar clientes com dívidas

São apresentados os clientes por ordem decrescente do valor das respectivas dívidas (valores superiores a zero). Se as dívidas tiverem o mesmo valor, apresentam-se os clientes por ordem crescente do seu identificador. Utiliza-se o formato de apresentação de um cliente descrito anteriormente.

Mostrar terminais sem actividade

São apresentados os terminais que ainda não efectuaram nem receberam qualquer comunicação. O formato de apresentação é o descrito acima.

Mostrar terminais com saldo positivo

São apresentados os terminais que que têm um valor de pagamentos estritamente superior ao valor das dívidas. O formato de apresentação é o descrito acima.

Menu da consola de um terminal

Este menu apresenta as operações relacionadas com a consola de um terminal, i.e., o seu uso e administração. A lista completa é a seguinte: Ligar terminal, Desligar terminal, Silenciar terminal, Adicionar amigo, Retirar amigo, Efectuar pagamento, Mostrar informação sobre pagamentos e dívidas, Enviar comunicação de texto, Iniciar comunicação interactiva, Terminar comunicação interactiva, Mostrar comunicações em curso.

Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe prr.app.terminal.Message.

Sempre que é pedido o identificador do cliente (Prompt.clientKey()), é lançada a excepção UnknownClientKeyException, se o cliente indicado não existir. Sempre que é pedido o identificador do terminal (Prompt.terminalKey()), é lançada a excepção UnknownTerminalKeyException, se o terminal seleccionado não existir.

Estes comandos já estão implementados nas classes da package prr.app.terminal (disponível no CVS), respectivamente: DoTurnOnTerminal, DoOffTurnTerminal, DoSilenceTerminal, DoAddFriend, DoRemoveFriend, DoPerformPayment, DoShowTerminalBalance, DoSendTextCommunication, DoStartInteractiveCommunication, DoEndInteractiveCommunication, DoShowOngoingCommunications.

Colocar terminal em espera

Coloca o terminal em espera. Se o terminal já estiver em espera, é apresentada a mensagem Message.alreadyOn(). Se não for possível colocar o terminal em espera, o comando não tem efeito.

Desligar terminal

Desliga o terminal. Se o terminal já estiver desligado, é apresentada a mensagem Message.alreadyOff() e o comando não realiza outras acções. Se não for possível desligar o terminal, o comando não tem efeito.

Silenciar terminal

Coloca o terminal em silêncio. Se o terminal já estiver em silêncio, é apresentada a mensagem Message.alreadySilent(). Se não for possível colocar o terminal em silêncio, o comando não tem efeito.

Adicionar amigo

É pedido o identificador do terminal a adicionar à lista de amigos. Se o terminal indicado já fizer parte da lista de amigos, a operação termina sem alterações.

Retirar amigo

É pedido o identificador do terminal a retirar da lista de amigos. Se o terminal indicado não fizer parte da lista de amigos, a operação termina sem alterações.

Efectuar pagamento

É pedido o identificador da comunicação a pagar (Prompt.commKey()). A comunicação tem de ter sido iniciada pelo terminal actual e tem de ter terminado e não pode ter sido paga. Caso contrário, é apresentada a mensagem Message.invalidCommunication().

Mostrar informação sobre pagamentos e dívidas

São apresentados os valores dos pagamentos e das dívidas do terminal (Message.terminalPaymentsAndDebts()).

Enviar comunicação de texto

Esta operação está disponível para um terminal que não esteja desligado ou em comunicação. Permite enviar uma comunicação de texto para outro terminal.

É pedido o número do terminal de destino e o corpo da mensagem (Prompt.textMessage()).

Quando o terminal de destino está desligado, é apresentada a mensagem Message.destinationIsOff().

Iniciar comunicação interactiva

Esta operação está disponível caso o terminal selecionado não esteja desligado ou em comunicação. Permite estabelecer uma comunicação interactiva entre o terminal selecionado com outro terminal.

É pedido o número do terminal de destino e o tipo de comunicação (Prompt.commType()). A resposta deve ser uma das seguintes opções (cadeia de caracteres): VIDEO, VOICE. Se a resposta não corresponder a nenhum destes valores, a pergunta é repetida até se obter uma resposta válida.

Se se tenta iniciar uma comunicação não suportada pelo terminal de origem ou pelo terminal de destino, devem ser apresentadas as Message.unsupportedAtOrigin() ou Message.unsupportedAtDestination(), respectivamente.

Quando o terminal de destino está desligado, é apresentada a mensagem Message.destinationIsOff(). Quando o terminal de destino está ocupado (ou quando é tentada uma ligação para o próprio), é apresentada a mensagem Message.destinationIsBusy(). Quando o terminal de destino está em silêncio, é apresentada a mensagem Message.destinationIsSilent().

Terminar comunicação interactiva

Esta operação está disponível para um terminal que iniciou uma comunicação interactiva enquanto a comunicação está em curso. O terminal de destino não pode interromper a comunicação, pelo que o comando não está disponível. O comando permite terminar a comunicação e registar a sua duração.

É pedida a duração da comunicação (Prompt.duration(), em minutos). Após o término da comunicação, é apresentado o seu custo, através da mensagem Message.communicationCost().

Mostrar comunicação em curso

É apresentada a comunicação em curso (de acordo com o formato indicado acima).

Se não houver nenhuma comunicação em curso, é apresentada a mensagem Message.noOngoingCommunication().

Leitura de Dados a Partir de Ficheiros Textuais

Além das opções de manipulação de ficheiros descritas no menu principal, é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java import.

As várias entidades têm os formatos descritos abaixo. Assume-se que os títulos não podem conter o carácter | (sugere-se a utilização do método String.split para o processamento preliminar destas linhas). Não existem entradas mal-formadas.

Cada linha tem uma descrição distinta, mas que segue os seguintes formatos:

CLIENT|id|nome|taxId
terminal-type|idTerminal|idClient|state
FRIENDS|idTerminal|idTerminal1,...,idTerminalN

As definições de clientes precedem sempre as dos terminais. As ligações entre amigos estão sempre após a definição das restantes entidades. Para os terminais, terminal-type é BASIC ou FANCY.

Um exemplo de conteúdo do ficheiro inicial é como se segue:

Exemplo de ficheiro de entrada textual
CLIENT|cli001|Manuel Pinheiro|103443
CLIENT|cli002|Pedro Pinheiro|103447
CLIENT|Cli201|Ludgero Oliveira|103440
CLIENT|cli Es|Maria Eucalipto|103441
CLIENT|01|Oliveira Preto|103547
CLIENT|cli003|Pedro Oliveira|103449
BASIC|969001|cli001|ON
BASIC|969003|cli002|ON
FANCY|969002|cli002|SILENCE
FANCY|969007|cli Es|ON
BASIC|969008|cli003|OFF
BASIC|969009|cli003|OFF
BASIC|969010|cli003|ON
FANCY|969006|cli003|ON
FANCY|969005|cli003|ON
BASIC|969004|cli003|ON
FRIENDS|969001|969008,969009,969004
FRIENDS|969004|969001
FRIENDS|969003|969008

A codificação dos ficheiros a ler é garantidamente UTF-8.

Note-se que o programa nunca produz ficheiros com este formato.

Execução dos Programas e Testes Automáticos

Usando os ficheiros test.import, test.in e test.out, é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável CLASSPATH (ou da opção equivalente -cp do comando java), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação (prr.app.App.main). As propriedades são tratadas automaticamente pelo código de apoio.

       java -Dimport=test.import -Din=test.in -Dout=test.outhyp prr.app.App

Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída test.outhyp. Em caso de sucesso, os ficheiros das saídas esperada (test.out) e obtida (test.outhyp) devem ser iguais. A comparação pode ser feita com o comando:

        diff -b test.out test.outhyp

Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.

Notas de Implementação

Tal como indicado acima, algumas classes fornecidas como material de apoio, são de uso obrigatório e não podem ser alteradas. Outras dessas classes são de uso obrigatório e têm de ser alteradas.

A serialização Java usa as classes da package java.io, em particular, a interface java.io.Serializable e as classes de leitura java.io.ObjectInputStream e escrita java.io.ObjectOutputStream (entre outras).