Sistema de cadastro de clientes

 
 

Actions-user-group-new-iconÍndice

  • 1 Introdução
  • 2 Primeira versão
    • 2.1 Armazenamento de dados de clientes
    • 2.2 Organização do sistema e Menu
    • 2.3 Cadastro de novos clientes
    • 2.4 Listar relação de clientes
    • 2.5 Modificação de dados de clientes
  • 3 Versão usando interface gráfica
  • 4 Versão usando arquivos
  • 5 Versão eficiente em memória principal

 

 

Introdução

Imagine você desenvolver seu próprio sistema de cadastro de clientes! Imagine que você pode criar um produto que é muito vendido desde o início e quem sabe ter um negócio próprio. Não é impossível. Na verdade é até uma boa ideia. São poucos os prestadores de serviço ou lojas que vendem produtos que não utilizam um sistema para gerenciar os seus clientes e produtos.

Isto é, o mercado para esses sistemas é muito grande. Nosso objetivo então, é apresentar os conceitos iniciais para desenvolver um sistema simples, robusto e rápido. Existem muitos tipos diferentes de sistemas de cadastro de cliente. Desde os mais simples até os mais sofisticados. Porém, todos compartilham conceitos e funcionalidades que você poderá aprender e praticar a seguir.

 

Primeira versão

O objetivo inicial é criar uma aplicação simples. Queremos um sistema capaz de executar as ações básicas de:

  • inserir novo cliente,
  • listar clientes e
  • alterar dados do cliente.

Para implementar essas funções do sistema serão necessárias:

  • uma forma para guardar dados de clientes
  • funções para manipular dados guardados

 

Armazenamento de dados de clientes

Para armazenar os dados dos cliente podemos criar uma estrutura especial de dados do tipo struct.

Inicialmente, devemos definir a estrutura que irá representar o cliente.

typedef struct cliente {

       char nome[40];
       char endereco[40];
       int codigo;

} Cliente ;
Essa estrutura básica poderá compor um vetor do tipo Cliente para armazenar os atuais clientes que o sistema guardou. Você deve organizar a estrutura de acordo com as necessidades do seu sistema.

No exemplo acima, podemos observar o uso de uma estrutura e também de ponteiro.

Organização do sistema e Menu

Com a struct bem definida, nosso objetivo agora é organizar as funções que irão realizar as operações desejadas: cadastrar clientes na lista, remover da lista, editar e apresentar.

Nessa primeira tarefa, a proposta é organizar com o sistema vai funcionar. Uma forma é construirmos o sistema em torno de um menu que pode ser usado. Com ele o usuário identifica quais são as ações existentes.

   [1. Criar base de clientes]
   [2. Adicionar novo cliente]
   [3. Apresentar na tela]
   [4. Editar informações]
   [5. Remover cliente]
   [6. Sair do sistema]

Em texto, por exemplo, teríamos algo parecido com a imagem abaixo:

Menu_exemplo

Esse menu pode ajudar o desenvolvimento do sistema sendo que uma funcionalidade é programada de cada vez.

Para a criação dessa interface em forma de menu podemos utilizar um scanf para ler a escolha do usuário e um switchcase para selecionar a ação desejada.

Cadastro de novos clientes

A primeira funcionalidade que deve ser programada no sistema é o cadastro de novos clientes. Essa é a funcionalidade mais importante.

Para isso queremos definir o protótipo de uma função que vai cadastrar um novo cliente no nosso sistema. Tecnicamente devemos alocar um novo espaço na memória contendo uma struct que armazena os dados e colocá-lo na base de clientes.

Então, podemos sugerir o seguinte protótipo:

   int cadastrar_cliente(Cliente novo_cliente, Cliente* base_de_clientes);

A função deve retornar um inteiro apenas para avisar que a função listou todos os clientes ou adicionou, no caso da função de adicionar, o novo cliente.

Como podemos observar, essa função recebe dois parâmetros: o primeiro seria o novo cliente a ser adicionado e o segundo a base de clientes.

Você pode desenvolver uma interação com o usuário de maneira simples, como no exemplo:

Primeiramente, podemos solicitar o nome do cliente:

It_usuario

E depois o seu endereço:

It_usuario2
Como podemos verificar em nossa struct, existe uma variável chamada código. Ela permite que possamos criar possibilidades para o usuário. Por exemplo, se você quiser pode criar uma função que permita que o usuário busque um determinado cliente através do código. Ou então, em uma única função você pode oferecer duas opções: o usuário pode fornecer o código do cliente ou pode optar por listar todos.

É comum em certos sistemas atribuir códigos de identificação de clientes que facilitam a busca pelo cliente. Esse código pode ser definido no momento da criação do usuário e é muito comum que seja definido automaticamente. Isso é feito de maneira incremental sendo que cada vez que um novo usuário é cadastrado ele recebe o próximo número inteiro de código disponível.

Listar relação de clientes

Sendo o programa capaz de criar um cadastro de clientes, vamos criar uma função que apresente os clientes cadastrados. Esse tipo de funcionalidade é bastante usada pelos gerentes que utilizam o sistema para criar relatórios e também para verificar de cada cliente.

Assim como na função de adicionar queremos definir um protótipo para termos mais noção de como desenvolver o corpo da função:

   void listar_clientes(Cliente* base_de_clientes, int TAM);

Como a base_de_clientes é um vetor, a codificação dessa função deve percorrer o array e mostrar cada Cliente até ter mostrado todos os TAM clientes.

Mas, e a apresentação ao usuário? Ele pode ser realizada de diversas maneiras. Por exemplo, você pode mostrá-las como uma lista vertical:

It_usuario3

Ou podemos também, apresentar as informações sequencialmente:

It_usuario4

Modificação de dados de clientes

Com o passar do tempo, os clientes podem ter seus dados modificados. Por exemplo, o telefone pode mudar, o endereço, entre outros dados. Assim, é essencial que exista a possibilidade de editar as informações já existentes.

Dessa forma, vamos agora criar uma função capaz de percorrer a lista de clientes e encontrar um determinado cliente pelo seu código e editar seus dados.

Como organizamos nosso sistema através de um menu devemos apresentar algumas mensagens para o usuário e ao mesmo tempo pedir as informações necessárias para a operação:

   ** Edição de dados **
   --> Por favor, digite o código do cliente: 

Optamos por pedir o código do cliente que será editado. Agora podemos solicitar os dados que serão alterados e mante-los em uma variável auxiliar do tipo struct:

   --> Digite o nome completo:
   --> Digite o endereço:

Observa que estamos pedindo informações com base na struct que definimos. No caso, a nossa possui apenas ‘nome’ e ‘endereço’.

Um próximo passo é procurar o cliente utilizando seu nome. Por enquanto, vamos utilizar o código.

Com o código do cliente a função deve percorrer o vetor. Se o cliente não for encontrado, um valor deve ser retornado. Defina um valor para essa ação e assim na função principal, apresente uma mensagem quando esse valor for recebido. Caso contrário, ao encontrar o cliente troque as informações antigas pelas novas passadas pelo usuário.

Da mesma forma, retorne um valor que você definiu para afirmar a função principal que a operação de edição de dados foi realizada com sucesso. Na função principal apresente uma mensagem ao usuário afirmando que a operação foi realizada.

 

 

Versão usando interface gráfica

Nosso objetivo agora é aprender a construir uma interface gráfica para nosso sistema. Para tal, vamos utilizar a biblioteca Allegro.

Após a instalação, iniciaremos um projeto e teremos a seguinte estrutura de código inicial:

Intro.png

Temos a função init() responsável pela inicialização do Allegro. Além da inicialização, ela também é responsável pela inicialização das cores, criação e verificação de uma tela e também inicialização do teclado, relógio e mouse.

Quanto a função deinit(), ela é utilizada para encerrar o allegro.

Na versão anterior, nós criamos um menu utilizando a função printf. Podemos fazer algo parecido utilizando o allegro:

   textout_ex(screen,font,"[1- Cadastrar novo cliente]",  0,10,makecol(255,255,255),-1);
   textout_ex(screen,font,"[2- Editar cadastro]",  0,20,makecol(255,255,255),-1);
   textout_ex(screen,font,"[3- Listar clientes]",  0,30,makecol(255,255,255),-1);

Obtemos então um menu bem simples assim:

Menu

Agora, podemos adicionar linhas para separar as opções do menu. Podemos utilizar funções para realizar esse desenho.

   line(screen, 0,20, 640, 20, -1); 
   line(screen, 0,40, 640, 40, -1);
   line(screen, 0,60, 640, 60, -1);

Assim, obtemos:

Segundo_menu

Contudo, podemos perceber que o ponteiro do mouse não aparece em nossa tela. Para solucionar esse problema vamos adicionar a seguinte função no início da função main, logo após a função init().

   show_mouse(screen); //Parâmetro screen pois queremos que a ação aconteça na tela principal

Agora, que podemos ver a seta do mouse na tela vamos definir um evento de clique. Quando ele ocorrer, o nosso programa produzirá alguma reação ao clique. Por exemplo:

   if((mouse_b & 1) && (mouse_x >= 0 || mouse_x <= 640) && (mouse_y >= 0 || mouse_y <= 10))
   {
       textout_ex(screen,font,"[ok]",  0,80,makecol(255,255,255),200);           
   }

O código acima determina que ao clicar na região de número 1, referente ao cadastro de novo cliente, o programa escreva OK na tela.

A variável pré-definida mouse_b é utilizada para sabermos se algum botão do mouse esta pressionado.

As variáveis pré-definidas mouse_x e mouse_y guardam a posição em que o mouse esta.

Contudo, podemos definir uma função e chamá-la quando o clique for realizado.

   void test()
   {
       while(!key[KEY_ESC])
       {
           textout_ex(screen,font,"Nome do cliente: ",  0,10,makecol(255,255,255),200);
       }
   }

Assim, temos:

   if((mouse_b & 1) && (mouse_x >= 0 || mouse_x <= 640) && (mouse_y >= 0 || mouse_y <= 10))
   {
         clear(screen);      
         test();           
   }

Versão usando arquivos

Nesta versão iremos complementar a primeira versão. Então, vamos utilizar todas as ideias lá apresentadas e somar com essa nova que tem o intuito de armazenar dados em disco.

Provavelmente você já viu ou utilizou algum sistema de cadastro, seja ele de clientes, produtos, serviços ou completo com todas essas opções. Então, o que acontece é que todos os dados inseridos são armazenados em arquivos no disco. Dessa forma, é possível recuperar as informações quando for necessário. Nosso objetivo agora é estudar essa ideia.

Para isso, a sugestão é criar uma função que irá receber o vetor e armazenar as informações dele em um arquivo cujo nome será dado pelo usuário. Primeiramente, o usuário deve informar um nome para o arquivo onde serão armazenados os dados.

Vamos supor que o nome dado seja clientes. A partir dai, a função deve criar um arquivo de nome clientes e salvar as informações contidas no vetor de clientes.

Ideia de protótipo:

   int salvar_arquivo(Cliente array_cliente, char nome_arquivo);

Nessa função, vamos verificar se a operação foi realizada com sucesso. Assim, vamos retornar um inteiro para tal informação. Normalmente, 0 significa que a ação não foi realizada e qualquer outro valor maior que 0 é interpretado como ação realizada com sucesso.

procurar sobre o tipo bool e includes.. http://icube-icps.unistra.fr/img_auth.php/d/db/ModernC.pdf

Assim, como vamos criar uma função para armazenar em arquivo também devemos criar uma função que faz a leitura desse arquivo. Isto é, recupera as informações arquivadas. O protótipo é praticamente o mesmo. Ela receberá o endereço de um vetor e o nome do arquivo onde as informações foram salvas. Então, deverá recuperar as informações para o nosso vetor.

   int recuperar_arquivo(Cliente array_cliente, char nome_arquivo);

Versão eficiente em memória principal

A versão 0 possui uma deficiência. Nela utilizamos um vetor para manipular os dados dos clientes. Quando definimos um vetor precisamos, também, definir o seu tamanho. Contudo, não é possível saber quantos clientes vamos cadastrar. Então, a deficiência está no fato de alocarmos uma determinada quantidade de memória para o vetor sem ter em mente quantas posições precisamos usar.

Nesta versão eficiente iremos resolver os problemas utilizando alocação dinâmica. Sempre que formos cadastrar um novo cliente, solicitaremos uma posição de memória para esses dados. Então, não precisamos definir com antecedência uma quantidade de memória para alocar. Fazemos isso gradativamente.

Isso será feito usando uma lista simplesmente encadeada.

A estrutura abaixo, exemplifica como seria a definição de um na lista encadeada:

   typedef struct cliente{
       char nome[40];
       char endereco[40];
       int codigo;
       struct cliente * prox;
   }Cliente;

Essa struct representa as informações do nosso cliente.

Inicialmente, não teremos nenhum cliente na lista. Quando formos cadastrar o primeiro cliente, alocaremos uma posição de memória para os dados e o ponteiro prox do tipo struct cliente irá apontar para NULL. Porém, quando formos cadastrar o segundo cliente faremos mais uma alocação de memória para os novos dados e o prox do primeiro cliente recebe o endereço da posição de memória recentemente alocada. E assim por diante. Essas ações irão se repetir para os próximos clientes.

Devemos perceber que com essa nova abordagem não será mais necessário armazenar o tamanho do vetor. Pois, não precisaremos mais saber em qual posição do vetor o último cliente esta.

Essa estrutura é criada a partir de alocação dinâmica.

Vamos analisar como a lista de clientes vai funcionar.

Primeiramente, organizamos as informações, que queremos armazenar, em nossa struct.

Visando a simplificação, vamos considerar que a primeira alocação de memória já foi realizada e exista apenas um cliente na lista. Vamos representá-lo como uma bolinha preenchida com a cor preta e imaginar que as informações do cliente estão contidas no interior.

Nodo_unitario

Ok. Podemos então, adicionar um novo cliente. Para isso, vamos alocar uma nova posição de memória, que irá armazenar os novos dados, e fazer a primeira bolinha apontar para a nova.

Lista

Perceba que, como ocorreu no primeiro exemplo, a nova bolinha aponta para NULL (nada). Mas, quando um novo cliente for adicionado esta segunda bolinha passará a apontar para ele.

A ideia é basicamente essa. Abaixo podemos analisar uma maneira de definir a alocação usando a struct que temos de exemplo:

   Lista * lista_de_clientes; // Definição de um ponteiro do tipo struct cliente
   lista_de_clientes = (Lista *)malloc(sizeof(Lista)); // Requisitando a alocação de um espaço de memória para a primeira bolinha

Para adicionar o segundo cliente(segunda bolinha no nosso exemplo) vamos alocar mais um espaço de memória. Porém, uma ideia é que se crie uma função para isso. Isto é, esta função irá receber os novos dados, alocar uma nova posição de memória e armazenar esses dados.

Logo abaixo temos um tópico chamado de segunda etapa na onde vamos discutir mais sobre uma possível função que adiciona clientes.

   add_cliente(&novo_cliente, lista_de_clientes); // A ideia da chamada da função pode ser como essa.

Porém, como os dados digitados pelo usuário serão manipulados pelas nossas funções? Temos uma implementação diferente agora. Então, vamos modificar os protótipos das funções que discutimos na versão 0.

A primeira e principal função. Cadastrar clientes:

   Cliente cadastrar_cliente(Cliente * novo_cliente, Cliente * base_de_clientes);

Passaremos não mais a retornar um inteiro, como foi proposto na versão 0, e sim um endereço para cliente. Por que dessa forma teremos sempre o endereço do primeiro cliente da lista e a partir dele vamos percorrer o restante da lista.

A segunda função, listar clientes:

   void listar_clientes(Cliente* base_de_clientes);

Na versão anterior, ela possuía, também, o parâmetro TAM, que era um inteiro utilizado para indicar qual a posição do último cliente na lista. Como já foi discutido, não precisaremos desse parâmetro.

A função de edição de dados não terá seu protótipo alterado. Pois, precisamos apenas do código do cliente. A alteração será realizada no inteiro da função pois a maneira de manipular a lista agora é diferente.

Também devemos alterar o protótipo da função que realiza o armazenamento das informações em um arquivo

   int salvar_arquivo(Cliente * array_cliente, char nome_arquivo);

O tipo de retorno continuará sendo o mesmo. Por que, desejamos saber se o armazenamento ocorreu corretamente. O mesmo se dá para a função que recuperar as informações do arquivo.