/* ------------------------------------------------------ * MODULO: MANARTICULOS.C MANTENIMIENTO DE UN FICHERO DIRECTO Autor: Alumnos de 1º ASI ( Tarde ) & Profesor Versión : 0.0 Fecha del último cambio: 22/04/2002 * EJEMPLO DE MANTENIMIENTO DE UN FICHERO DE ARTICULOS * Controlado por un menu: - Opciones de movimiento: Siguiente : Muestra el siguiente registro Anterior : Muestra el anterior Otro : Muestra un registro concreto a partir de su posición Primero : Muestra el primer registro Ultimo : Muestra el último Buscar : Busca un código de articulo utilizando una tabla índices - Opciones de Acciones Nuevo : Graba al final un nuevo registro Modificar : Modifica el registro actual Eliminar : Realiza un borrado lógico marcando el registro Venta : Anota los datos relativos a la venta del articulo actual Compra : Anota los datos relativos a la compra de acticulo actual POSIBLES MEJORAS: 1) Borrado físico de los registros marcados al salir del programa 2) Mantener una lista de registro borrados para utilizar su espacio cuando añadimos nuevos registros 3) Incluir la tabla de indices en la estructura del fichero 4) Permitir varias tablas de indices dinámicas 5) Mejora general de la presentación y de la validación de los datos 6) Utilizar la liberia curses para portar sobre UNIX/LINUX * ------------------------------------------------------ */ #include #include #include #include #include #include "articulo.h" // Include local con la estructura del tipo articulo /* Código que indica que el registro está eliminado */ #define MARCABORRADO "*****" #define MAXREGIDX 100000 // Máximo número de elemento en la tabla indice /* Variables Globables */ TipoArticulo RArticulo; /* Registro donde leo o escribo */ FILE * fent; /* Puntero a estructura Fichero */ /* Tabla de indices */ struct ElementoIdx { char clave[10]; long posicion; }; struct ElementoIdx TablaIdx[MAXREGIDX]; // Tabla de indices int numregidx; // Número de claves almacendas /* --------------------------------------------------- */ /* Definición de Funciones del Modulo : MANARTI.C : */ /* --------------------------------------------------- */ /* FUNCIONES DE DESPLAZAMIENTO POR EL FICHERO */ void Siguiente( void ); void Anterior ( void ); void Primero ( void ); void Ultimo ( void ); void Otro ( void ); void Buscar ( void ); /* FUNCIONES DE ACCION QUE MODIFICAN EL CONTENIDO DEL FICHERO */ void Nuevo ( void ); void Compra ( void ); void Venta ( void ); void Modificar ( void ); void Eliminar ( void ); /* FUNCIONES DE BUSQUEDA Y MANTENIMIENTO DE UNA TABLA DE INDICES */ void InitTablaIdx ( void ); void AnotarIdx ( char *clave, long posicion ); void BorrarIdx ( char *clave ); int BuscarIdx ( char *clave ); long PosicionCodIdx ( char *clave ); /* FUNCIONES AUXILIARES */ void VerMenu ( void ); void VerCampos ( void ); void VerError ( char *msg ); char LeerOpcion ( char * pcontrol ); void ObtenerFecha (int x, int y, Tfecha *f ); /* ---------------------------------------------------------------*/ /* MUESTRA UNA LINEA DE MENUS CON LAS LETRAS INICIALES ILUMINADAS */ /* ---------------------------------------------------------------*/ void VerMenu ( void ) { textcolor(10); gotoxy (6,2); cputs("____________________________________________ "); gotoxy (6,3); cputs(" "); gotoxy (6,4); cputs("A R C H I V O D E A R T I C U L O S "); gotoxy (6,5); cputs("____________________________________________ "); textcolor(6); gotoxy (5,22); cprintf ("N"); highvideo(); cprintf ("uevo "); lowvideo(); cprintf ("E"); highvideo(); cprintf ("liminar "); lowvideo(); cprintf ("M"); highvideo(); cprintf ("odificar "); lowvideo(); cprintf ("V"); highvideo(); cprintf ("enta "); lowvideo(); cprintf ("C"); highvideo(); cprintf ("ompra "); lowvideo(); cprintf ("T"); highvideo(); cprintf ("erminar "); lowvideo(); textcolor(9); gotoxy (5,24); cprintf ("S"); highvideo(); cprintf ("iguiente "); lowvideo(); cprintf ("A"); highvideo(); cprintf ("nterior "); lowvideo(); cprintf ("P"); highvideo(); cprintf ("rimero "); lowvideo(); cprintf ("U"); highvideo(); cprintf ("ltimo "); lowvideo(); cprintf ("O"); highvideo(); cprintf ("tro "); lowvideo(); cprintf ("B"); highvideo(); cprintf ("uscar :"); lowvideo(); } /* ---------------------------------------------------------------*/ /* Muestra por pantalla la información de un registro o CAMPOS */ /* ---------------------------------------------------------------*/ void VerCampos ( void ) { textcolor(GREEN); gotoxy(15, 7); cprintf(" N§ Reg :"); clreol(); textcolor(14); cprintf("%5d",ftell(fent)/sizeof(RArticulo)); textcolor(DARKGRAY); gotoxy(24, 8);clreol(); gotoxy(24,10);clreol(); gotoxy(24,11);clreol(); gotoxy(29,12);clreol(); gotoxy(29,13);clreol(); gotoxy(27,14);clreol(); gotoxy(27,15);clreol(); gotoxy(27,16);clreol(); gotoxy(27,17);clreol(); gotoxy(27,18);clreol(); gotoxy(27,19);clreol(); if ( strcmp (RArticulo.codigo,MARCABORRADO)== 0) { VerError(" ---- REGISTRO ELIMINADO ---- "); } else { gotoxy( 15,10); cprintf(" CODIGO :%s" ,RArticulo.codigo); gotoxy( 15,11); cprintf(" GRUPO :%d",RArticulo.grupo); gotoxy( 15,12); cprintf(" DESCRIPCION :%s",RArticulo.descripcion); gotoxy( 15,13); cprintf(" PROVEEDOR :%s" ,RArticulo.proveedor); gotoxy( 15,14); cprintf(" STOCK :%3d",RArticulo.stock); gotoxy( 15,15); cprintf(" STOCKMIN :%3d" ,RArticulo.stockmin); gotoxy( 15,16); cprintf(" PRECIOV :%6d",RArticulo.preciov); gotoxy( 15,17); cprintf(" PRECIOC :%6d" ,RArticulo.precioc); gotoxy( 15,18); cprintf(" FCOMPRA :%02d/%02d/%4d",RArticulo.fcompra.dia, RArticulo.fcompra.mes,RArticulo.fcompra.anio); gotoxy( 15,19); cprintf(" FVENTA :%02d/%02d/%4d" ,RArticulo.fventa.dia, RArticulo.fventa.mes,RArticulo.fventa.anio); textcolor(GREEN); } } /* -----------------------------------------*/ /* Muestra un Mensaje de error */ /* -----------------------------------------*/ void VerError ( char *msg ) { textcolor(RED+BLINK); gotoxy(5,20); cputs( msg ); ungetch(getch()); textcolor(WHITE); gotoxy(5,20); clreol(); } /* ---------------------------------------------------------------*/ /* Lee un caracter que este dentro de la cadena de control */ /* ---------------------------------------------------------------*/ char LeerOpcion ( char * pcontrol ) { int numc; char letra; short i; numc = strlen ( pcontrol); while ( 1 ) { gotoxy(54, 24); letra = (char ) toupper(getch()); for (i=0 ; i < numc ; i++ ) { if ( letra == pcontrol[i] ) { return letra; } } } } /* ------------------------------------------------------------*/ /* Rellena la estructura Tfecha solicitando datos en las */ /* posiciones X e Y de la pantalla. /* ------------------------------------------------------------*/ void ObtenerFecha (int x, int y, Tfecha *f ) { static dmes[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; int error = 1; while ( error == 1) { error = 0; gotoxy(x,y); cputs (" / / "); gotoxy(x,y); cscanf("%d",& f->dia); gotoxy(x+3,y); cscanf("%d",& f->mes); gotoxy(x+6,y); cscanf("%d",& f->anio); if ((f->mes >12) || (f->mes<1)) { getch(); VerError ("ERROR EN LA INTRODUCION DE MES"); error=1; } else { if (f->dia > dmes[f->mes-1] || f->dia < 1) { getch(); VerError ("ERROR EN LA INTRODUCION DE DIA"); error=1; } } if (f->anio <1000) { getch(); VerError ("ERROR EN LA INTRODUCION DEL A¥O"); error=1; } } } /* ----------------------------------------*/ /* Lee el siguiente registro */ /* ----------------------------------------*/ void Siguiente ( void ) { if ( fread(&RArticulo,sizeof(RArticulo),1,fent) == 0 ) { VerError("Fin de Fichero."); } } /* --------------------------------------*/ /* Lee registro anterior al actual */ /* --------------------------------------*/ void Anterior( void ) { long pos; /* Obtengo la posición del registro anterior */ pos = ftell(fent) - (2L * sizeof(RArticulo)); /* Si no es antes del principio de fichero */ if ( pos >= 0 ) { fseek(fent,pos,SEEK_SET); fread(&RArticulo,sizeof(RArticulo),1,fent); } else { VerError("Inicio del fichero"); } } /* ---------------------------------------------------------------*/ /* Me situo y leo un registro determinado del fichero */ /* ---------------------------------------------------------------*/ void Otro ( void ) { long new_pos; long max_pos; long num_reg; /* Calculo la posición del ultimo registro */ fseek(fent,0L,SEEK_END); max_pos = ftell(fent) - sizeof(RArticulo); do { gotoxy(24, 7); cprintf(" "); gotoxy(15, 7); cprintf(" N§ Reg : "); // Mientra falle el scan while ( cscanf("%ld",&num_reg) == 0); /* Posición física a partir del Número de registro */ new_pos = ( num_reg - 1) * sizeof(RArticulo); } while ( ( new_pos < 0 ) || ( new_pos > max_pos ) ); fseek(fent,new_pos,SEEK_SET ); fread(&RArticulo, sizeof(RArticulo), 1, fent ); } /* -----------------------------------------------*/ /* Me situo y leo el primer registro del fichero */ /* ----------------------------------------------*/ void Primero( void ) { fseek(fent,0L,SEEK_SET); fread(&RArticulo, sizeof(RArticulo),1, fent); } /* ----------------------------------------------*/ /* Me situo y leo el ultimo registro del fichero */ /* ----------------------------------------------*/ void Ultimo ( void ) { fseek(fent, 0L - sizeof(RArticulo),SEEK_END); fread(&RArticulo, sizeof(RArticulo), 1, fent); } /* ---------------------------------------------------------------*/ /* Rellena campos en el fichero */ /* ---------------------------------------------------------------*/ void RellenarCampos ( void ) { // Borro los valores anterior mostrados en pantalla gotoxy(24, 8);clreol(); gotoxy(24,10);clreol(); gotoxy(24,11);clreol(); gotoxy(29,12);clreol(); gotoxy(29,13);clreol(); gotoxy(27,14);clreol(); gotoxy(27,15);clreol(); gotoxy(27,16);clreol(); gotoxy(27,17);clreol(); gotoxy(27,18);clreol(); gotoxy(27,19);clreol(); textcolor(DARKGRAY); // Solicito Nuevos valores gotoxy(24,10); cscanf("%s", RArticulo.codigo); gotoxy(24,11); cscanf("%d", & RArticulo.grupo); gotoxy(29,12); cscanf("%s", RArticulo.descripcion); gotoxy(29,13); cscanf("%s", RArticulo.proveedor); gotoxy(27,14); cscanf("%d", & RArticulo.stock); gotoxy(27,15); cscanf("%d", & RArticulo.stockmin); gotoxy(27,16); cscanf("%d", & RArticulo.preciov); gotoxy(27,17); cscanf("%d", & RArticulo.precioc); ObtenerFecha(27,18,& RArticulo.fcompra); ObtenerFecha(27,19,& RArticulo.fventa); } /* -------------------------------------*/ /* Incluir un nuevo REGISTRO */ /* -------------------------------------*/ void Nuevo ( void ) { int i; fseek(fent,0,SEEK_END); // Me situo al final RellenarCampos(); // Solicito nuevo valores for (i=0;i<12;i++) { RArticulo.ventasmes[i]=0; // Ventas iniciales a cero } // Actualizo la Tabla de Indices AnotarIdx(RArticulo.codigo,ftell(fent)); // Grabo el registro al fichero fwrite(& RArticulo,sizeof(RArticulo),1,fent); fseek(fent,0,SEEK_CUR); // fseek para despues poder leer sin problemas } /* ---------------------------------------------------------------*/ /* Modificar registro ya existente */ /* ---------------------------------------------------------------*/ void Modificar ( void ) { long pos; char clave[30]; /* Obtengo la posición del registro */ pos = ftell(fent) - ( 1 * sizeof(RArticulo)); fseek(fent,pos,SEEK_SET); // Guardo la antigua clave por si cambia. strcpy(clave,RArticulo.codigo); RellenarCampos(); // Si ha cambiado la clave // Actualizo la tabla de índices. if ( strcmp(clave,RArticulo.codigo) != 0 ) { BorrarIdx(clave); AnotarIdx(RArticulo.codigo, pos ); } /* Graba el nuevo registro */ fwrite( &RArticulo, sizeof(RArticulo),1,fent ); fseek(fent,0,SEEK_CUR); } /* ----------------------------------------------------------------*/ /* VENTA Solicita fecha y cantidad de unidades vendidas y actualiza */ /* ----------------------------------------------------------------*/ void Venta ( void ) { long pos; int mes; int unidades; gotoxy(51,11); cprintf("------- V E N T A -----------"); gotoxy(51,12); cprintf(" Unidades vendidas: "); gotoxy(51,13); cprintf(" Fecha de venta : "); gotoxy(51,14); cprintf("-----------------------------"); gotoxy(70,12);cscanf("%d", & unidades); // Mientra no tenga suficientes existencias while (unidades > RArticulo.stock) { getch(); VerError("Error: Las unidades superan el stock actual."); getch(); gotoxy(70,12); clreol(); gotoxy(70,12); cscanf("%d", & unidades); } ObtenerFecha(70,13, & RArticulo.fventa); gotoxy(51,11);clreol(); gotoxy(51,12);clreol(); gotoxy(51,13);clreol(); gotoxy(51,14);clreol(); // Actualizo el registro en memoria RArticulo.stock = RArticulo.stock - unidades; mes = RArticulo.fventa.mes - 1 ; RArticulo.ventasmes[mes] += unidades; // Incremeta la ventas del mes // Grabo el registro en el fichero pos = ftell(fent) - sizeof(RArticulo); fseek(fent,pos,SEEK_SET); fwrite( & RArticulo,sizeof(RArticulo),1,fent); fseek(fent,0,SEEK_CUR); } /* --------------------------------------------------------------------*/ /* Compra Solicita fecha y cantidad de unidades compradas y actualiza */ /* --------------------------------------------------------------------*/ void Compra ( void ) { long pos; int unidades; gotoxy(51,11); cprintf("------- C O M P R A ----------"); gotoxy(51,12); cprintf(" Unidades Compradas: "); gotoxy(51,13); cprintf(" Fecha de Compra : "); gotoxy(51,14); cprintf("-----------------------------"); gotoxy(72,12);cscanf("%d", & unidades); ObtenerFecha(70,13, & RArticulo.fcompra); gotoxy(51,11);clreol(); gotoxy(51,12);clreol(); gotoxy(51,13);clreol(); gotoxy(51,14);clreol(); // Actualizo el registro en memoria RArticulo.stock = RArticulo.stock + unidades; // Grabo el registro en el fichero pos = ftell(fent) - sizeof(RArticulo); fseek(fent,pos,SEEK_SET); fwrite( & RArticulo,sizeof(RArticulo),1,fent); fseek(fent,0,SEEK_CUR); } /* ------------------------------------------- */ /* Borro un registro mediante una marca lógica */ /* ------------------------------------------- */ void Eliminar (void) { char opcion; VerError(" ¨Est  seguro que desea eliminar este registro? (S/N):"); opcion = (char ) getch(); if ( toupper(opcion) == 'S') { // Elimino de la tabla de indices BorrarIdx(RArticulo.codigo); //Copio la marca al campo codigo strcpy(RArticulo.codigo,MARCABORRADO); // Actualizo el fichero //Me posiciono al principio del registro actual fseek (fent, 0 - sizeof(RArticulo),SEEK_CUR); fwrite(&RArticulo,sizeof(RArticulo),1,fent); fseek (fent,0,SEEK_CUR); } } /* ---------------------------------------*/ /* Busca un código en la tabla de indices */ /* ---------------------------------------*/ void Buscar () { char clave[30]; long pos; gotoxy(50,10); cprintf(" C¢digo a Buscar:");cscanf("%s",clave); // Llamo a la función de busqueda en la tabla pos = PosicionCodIdx ( clave ); if ( pos == -1 ) { getch(); // Leo el salto de linea VerError(" Error : Clave no encontrada. "); getch(); } else { // Me situo en la posición y leo el registro fseek(fent,pos,SEEK_SET); fread(&RArticulo,sizeof(RArticulo),1,fent); } gotoxy(50,10); clreol(); // Borro el mensaje } /* -----------------------------------------------------------------------*/ /* -----------------------------------------------------------------------*/ /* FUNCIONES DE MANEJO DE LA TABLA DE INDICES */ /* -----------------------------------------------------------------------*/ /* -----------------------------------------------------------------------*/ // Función que compara dos Elementos de la tabla de indices // Parámetro necesario de la fúnción de liberia: qsort int micomp ( const void *n1, const void *n2) { return strcmp( (( struct ElementoIdx *) n1)->clave, (( struct ElementoIdx *) n2)->clave ); } /* ---------------------------------------------------- */ /* Inicia las tabla de indices leyendo todo el fichero y ordenando la tabla por el método rápido función qsort */ /* ---------------------------------------------------- */ void InitTablaIdx ( void ) { long pos,posold; TipoArticulo reg; char dib[5] = "-\|/"; // Tonteria animada int i; clrscr(); cprintf(" Cargando indice : "); /* Guardo la antigua posicion y me situo al principio */ posold = ftell(fent); rewind(fent); i=0; fread(®,sizeof(reg),1,fent); while(!feof(fent)) { // No guardo los registros borrados if ( strcmp(reg.codigo,MARCABORRADO) != 0 ) { pos = ftell(fent)-(sizeof(reg)); TablaIdx[i].posicion = pos; strcpy(TablaIdx[i].clave,reg.codigo); i++; // Muestro la Tonteria animada gotoxy(30,1); cprintf("%6d:%c",i,dib[i % 4]); } fread(& reg,sizeof(reg),1,fent); } // Ordeno la tabla qsort(TablaIdx,i,sizeof(struct ElementoIdx),micomp); numregidx = i; // Guardo el número de claves almacenadas /* Vuelvo a situarme en la antigua posición */ fseek(fent,posold,SEEK_SET); cprintf(" Indice cargado."); } /* ----------------------------------------- */ /* Anota ordenadamente una clave en la tabla */ /* ----------------------------------------- */ void AnotarIdx ( char *clave, long posicion ) { int I; struct ElementoIdx nuevo; strcpy(nuevo.clave,clave); nuevo.posicion = posicion; if(numregidx == 0) { TablaIdx[0]=nuevo; } else { I = numregidx - 1; while((strcmp(nuevo.clave,TablaIdx[I].clave)<0)&& (I > 0 )) { TablaIdx[I+1] = TablaIdx [I]; // Desplazo los elementos mayores I--; } // Situo el nuevo elemento if ( strcmp(TablaIdx[I].clave,nuevo.clave ) > 0) { TablaIdx[I+1] = TablaIdx[I]; TablaIdx[I] = nuevo; } else { TablaIdx[I+1] = nuevo; } } numregidx++; // Incremento el número de elemento de la tabla indice } /* ----------------------------------------- */ /* Suprime una clave de la tabla de indices */ /* ----------------------------------------- */ void BorrarIdx ( char *clave ) { int posi; int i; // Obtengo la posición en la tabla posi = BuscarIdx ( clave ); if ( posi != -1 ) { /* Desplazo los elementos, machacando el antiguo */ for (i= posi; i < numregidx -1 ; i++ ) { TablaIdx[i] = TablaIdx[i+1]; } numregidx--; // Decremento el número de elementos en el indice } } /* --------------------------------------------*/ /* Devuelve la posición en la tabla donde esta, la clave o -1 si no está */ /* Realiza una busqueda binaria o dicotómica */ /* --------------------------------------------*/ int BuscarIdx ( char *clave ) { int encontrado=0; int izda = 0; int dcha = numregidx - 1; int resu = -1; // No está int resucmp; int medio; while ((encontrado==0)&&(izda <= dcha)) { medio=( izda + dcha)/2; resucmp = strcmp(TablaIdx[medio].clave,clave); if ( resucmp == 0) { encontrado=1; } else { if ( resucmp > 0) { dcha=medio-1; } else { izda=medio+1; } } } if ( encontrado ) { resu = medio; /* Guarda la posición */ } return resu; } /* ------------------------------------------------ */ /* Devuelve la posición del registro en el fichero */ /* o -1 si no está */ /* ------------------------------------------------- */ long PosicionCodIdx ( char *clave) { long pos ; // Posición dentro del fichero int posi; // Posición en la tabla de indices pos = -1; // No está posi = BuscarIdx( clave ); if ( posi != -1 ) { pos = TablaIdx[posi].posicion; } return pos; } /* -----------------------------------------------------------------------*/ /* PROGRAMA PRINCIPAL */ /* Abre el fichero, Inicializa la tabla de indices, muestra el primer registro y entra el ciclo del menu */ /* -----------------------------------------------------------------------*/ void main() { char opcion; // Dos listas: una completa y otra limitada cuando el registro está borrado char * ListaOpciones[2] ={"SAOPUBVCNMET","SAOPUBNT"}; fent = fopen("ARTICULOS.DAT","rb+"); if ( fent == NULL ) { perror("fopen"); exit(1); } InitTablaIdx(); // Creo la tabla de indices. /* Muestro el menu y el primer registro */ clrscr(); VerMenu(); Siguiente(); VerCampos(); // Si el registro no está borrado if ( strcmp(RArticulo.codigo,MARCABORRADO) != 0) { opcion = LeerOpcion(ListaOpciones[0]); } else { opcion = LeerOpcion(ListaOpciones[1]); } while ( opcion !='T') { switch ( opcion ) { case 'S': Siguiente (); break; case 'A': Anterior (); break; case 'O': Otro (); break; case 'P': Primero(); break; case 'U': Ultimo(); break; case 'V': Venta(); break; case 'C': Compra(); break; case 'N': Nuevo(); break; case 'M': Modificar(); break; case 'E': Eliminar(); break; case 'B': Buscar(); break; } VerCampos(); // Si el registro no está Borrado if ( strcmp(RArticulo.codigo,MARCABORRADO) != 0) { opcion = LeerOpcion(ListaOpciones[0]); } else { opcion = LeerOpcion(ListaOpciones[1]); } } fclose(fent); }