Procesar tabla de préstamos

Bueno, esta es la última tabla, pero no se ajusta al mismo patrón que las anteriores.

Con los préstamos las opereaciones son algo diferentes, al menos si queremos que el programa resulte operativo según el funcionamiento que vamos a suponer para la biblioteca.

Los préstamos siempre empiezan con un socio que pide un libro. Así, para crear una fila en la tabla de préstamos tenemos que obtener una clave de socio, y una clave de ejemplar, generalmente a través de una clave de libro. En las altas de préstamos dejaremos la fecha de devolución a NULL y la de entrega será, por defecto, la fecha actual, aunque pueda ser modificada por el usuario.

La función ListaEjemplares que teníamos definida en "ejemplar.cpp" no nos sirve para esta tarea, ya que necesitamos que nos deje elegir sólo entre los ejemplares que no han sido prestados, que son los disponibles en este caso. Por lo tanto, crearemos una función para seleccionar el ejemplar adecuado.

Esta tarea no es tan simple como puede parecer en principio, ya que no existe una consulta que nos de estos datos de forma directa.

Para conseguirlo primero crearemos una tabla temporal en la que insertaremos los ejemplares existentes del libro seleccionado. Una vez hecho esto, eliminaremos de esa tabla los ejemplares que estén en préstamo (aquellos cuya fecha de devolución sea nula). Podemos eliminar también los ejemplares que no puedan ser prestados al socio, debido a la categoría. Por ejemplo, un ejemplar de categoría C sólo puede ser prestado a socios de categoria B y C. (Recordemos que no puede haber socios de categoría A.

Además, una vez identificado el socio, podemos mostrar una lista de los ejemplares que tiene en préstamo actualmente, para ver si, por ejemplo, ya tiene prestados el número límite (si es que existe un límite).

Los registros de préstamos, en principio, no deberían poderse eliminar ni editar. Pero si se produce algún error en el préstamo podemos añadir una opción de anular, y podemos dejar editar algunos campos en los préstamos cerrados, por ejemplo, la fecha de préstamo.

Para editar y anular préstamos tendremos que elegirlos desde una lista de préstamos abiertos de un socio. Pero la tabla de préstamos no tiene una clave primaria arbitraria, de manera que necesitamos un valor único, a ser posible numérico, que identifique a cada préstamo.

Afortunadamente, cada ejemplar sólo puede estar prestado una vez en cada momento, por lo que podremos usar la clave de ejemplar para seleccionar el préstamo que queramos editar o anular.

Las devoluciones las haremos a partir de un socio, y una vez conocido éste, mostraremos una lista de los préstamos actuales. Se nos pedirá una fecha de devolución y podremos agregar alguna nota. De hecho, esta función será muy parecida a las de anular y modificar fecha.

Otras posibles opciones son la consulta de historiales de préstamos, tanto de un socio como de un libro.

También nos puede interesar un listado de préstamos que lleven abiertos más de n días.

Y en general, cualquier consulta que se nos ocurra que puede ser útil. De momento nos quedaremos con las anteriores.

En cualquier caso, el procedimiento para agregar las nuevas opciones es similar a los anteriores. Empezamos por crear los nuevos identificadores, en "menus.h":

#define NUEVOPRESTAMO       30
#define EDITARPRESTAMO      31
#define ANULARPRESTAMO      32
#define DEVOLUCION          33
#define EDITARDEVOLUCION    34
#define HISTORIALSOCIO      35
#define HISTORIALLIBRO      36
#define CONSULTAPRESTAMOS   37

También añadiremos las nuevas opciones de menú en "menus.cpp", y borraremos la tabla de "menu" para que se vuelva a generar completa:

    {5, "-", "---MENU PRESTAMOS---",0,TITULO},
    {5, "1", "Nuevo prestamo", 0, NUEVOPRESTAMO},
    {5, "2", "Editar prestamo", 0, EDITARPRESTAMO},
    {5, "3", "Anular prestamo", 0, ANULARPRESTAMO},
    {5, "4", "Devolucion", 0, DEVOLUCION},
    {5, "5", "Historial de libro", 0, HISTORIALLIBRO},
    {5, "6", "Historial de socio", 0, HISTORIALSOCIO},
    {5, "7", "Consultar prestamos", 0, CONSULTAPRESTAMOS},
    {5, "0", "Salir <", 1, ABRIRMENU},

A continuación, modificaremos el fichero "main.cpp" para que se incluya el nuevo fichero de cabecera "prestamo.h", y para que se procesen las nuevas opciones:

#include "prestamo.h"
...
            case NUEVOPRESTAMO:
               NuevoPrestamo(db);
               break;
            case EDITARPRESTAMO:
               CambiarFechaPrestamo(db);
               break;
            case ANULARPRESTAMO:
               AnularPrestamo(db);
               break;
            case DEVOLUCION:
               DevolucionPrestamo(db);
               break;
            case HISTORIALLIBRO:
               HistorialLibro(db);
               break;
            case HISTORIALSOCIO:
               HistorialSocio(db);
               break;
            case CONSULTAPRESTAMOS:
               ConsultaPrestamos(db);
               break;

Por último, añadimos dos nuevos ficheros al proyecto: 'prestamo.h' y 'prestamo.cpp', con su consiguiente contenido:

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Abril de 2012
 * Fichero: prestamo.h
 * fichero de cabecera para manupular datos de prestamos
 */

#ifndef __PRESTAMO_H__
#define __PRESTAMO_H__

#include <sqlite/sqlite3.h>

void NuevoPrestamo(sqlite3 *);
void MostrarEjemplaresPrestados(sqlite3 *, int);
char LeerCategoriaSocio(sqlite3 *, int);
int ListaEjemplaresBiblioteca(sqlite3 *, int, int);
void CambiarFechaPrestamo(sqlite3 *);
void AnularPrestamo(sqlite3 *);
int ListaEjemplaresLibro(sqlite3 *db, int clavelibro);
int ListaEjemplaresPrestados(sqlite3 *, int);
void DevolucionPrestamo(sqlite3 *);
void HistorialLibro(sqlite3 *);
void HistorialSocio(sqlite3 *);
void ConsultaPrestamos(sqlite3 *);
#endif
/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Abril de 2012
 * Fichero: prestamo.cpp
 * fichero de implementación para manipular datos de prestamos
 */

#include <iostream>
#include <iomanip>
#include "prestamo.h"
#include "socio.h"
#include "libro.h"
#include "ejemplar.h"

using namespace std;

void NuevoPrestamo(sqlite3 *db) {
    int clavesocio;
    int clavelibro;
    int claveejemplar;
    char fecha[12];
    char consulta[1024];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un socio, un libro y un ejemplar para dar de alta un prestamo." << endl;

    cin.ignore();
    cout << "Socio: ";
    clavesocio = ListaSocios(db);
    if(!clavesocio) return;
    MostrarEjemplaresPrestados(db, clavesocio);
    cout << "Libro:";
    clavelibro = ListaLibros(db);
    if(!clavelibro) return;
    claveejemplar = ListaEjemplaresBiblioteca(db, clavesocio, clavelibro);
    if(!claveejemplar) {
        cout << "No hay ejemplares disponibles para prestamo" << endl;
        cin.ignore();
        cin.get();
        return;
    }
    cin.ignore();
    cout << "Fecha de entrega (AAAA/MM/DD) [dejar en blanco para fecha actual]: ";
    cin.getline(fecha, 12);

    if(clavesocio && claveejemplar) {
        if(strlen(fecha) > 0)
            sprintf(consulta, "INSERT INTO prestamo(clavesocio,claveejemplar,fecha_prestamo,fecha_devolucion,notas) VALUES(%d,%d,'%s',NULL,'');",
                clavesocio, claveejemplar, fecha);
        else
            sprintf(consulta, "INSERT INTO prestamo(clavesocio,claveejemplar,fecha_prestamo,fecha_devolucion,notas) VALUES(%d,%d,date('now'),NULL,'');",
                clavesocio, claveejemplar);
        if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        }
        else cout << "Prestamo creado" << endl;
    }
    cin.ignore();
}

void MostrarEjemplaresPrestados(sqlite3 *db, int clavesocio) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];

    if(clavesocio) {
        sprintf(consulta, "SELECT fecha_prestamo,titulo,formato FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro "
                "WHERE clavesocio=%d AND fecha_devolucion IS NULL;", clavesocio);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            cout << "Este socio tiene prestados actualmente los siguientes ejemplares:" << endl;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_text(ppStmt, 0) << " " <<
                    sqlite3_column_text(ppStmt, 1) << " (" <<
                    sqlite3_column_text(ppStmt, 2) << ")" << endl;
            }
            cout << "------------------------" << endl;
            sqlite3_finalize(ppStmt);
        }
    }
    cin.ignore();
    cin.get();
}

char LeerCategoriaSocio(sqlite3 *db, int clavesocio) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[128];
    char categoriasocio[2];

    sprintf(consulta, "SELECT categoria FROM socio WHERE clavesocio=%d;", clavesocio);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        return 0;
    } else {
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            strncpy(categoriasocio, (char*)sqlite3_column_text(ppStmt, 0), 1);
            categoriasocio[2] = 0;
        }
        sqlite3_finalize(ppStmt);
    }
    return categoriasocio[0];
}

int ListaEjemplaresBiblioteca(sqlite3 *db, int clavesocio, int clavelibro) {
    sqlite3_stmt *ppStmt;
    sqlite3_stmt *ppStmt2;
    int rc;
    char consulta[1024];
    int desplazamiento=0;
    char resp[10];
    bool salir=false;
    bool ultima;
    int fila=0;
    int i;

    // Crear tabla temporal con los ejemplares del libro indicado:
    sprintf(consulta, "CREATE TEMPORARY TABLE ejemplar2 AS SELECT * FROM ejemplar WHERE clavelibro=%d;", clavelibro);
    if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        return 0;
    }
    // Eliminar ejemplares de categorias superiores a categoriasocio:
    sprintf(consulta, "DELETE FROM ejemplar2 WHERE categoria<'%c';", LeerCategoriaSocio(db, clavesocio));
    if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        return 0;
    }

    // Eliminar ejemplares prestados:
    sprintf(consulta, "SELECT claveejemplar FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro "
            "WHERE clavelibro=%d AND fecha_devolucion IS NULL AND claveejemplar IS NOT NULL;", clavelibro);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc==SQLITE_OK ) {
        rc = sqlite3_prepare_v2(db, "DELETE FROM ejemplar2 WHERE claveejemplar=@clave;", -1, &ppStmt2, NULL);
    }
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        return 0;
    } else {
        while(SQLITE_ROW == sqlite3_step(ppStmt)) {
            sqlite3_bind_int(ppStmt2, sqlite3_bind_parameter_index(ppStmt2, "@clave"), sqlite3_column_int(ppStmt, 0));
            sqlite3_step(ppStmt2);
            sqlite3_reset(ppStmt2);
        }
        sqlite3_finalize(ppStmt);
    }

    // Si la lista de ejemplares disponibles está vacía, retornar 0:
    sprintf(consulta, "SELECT COUNT(*) FROM ejemplar2;");
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        return 0;
    } else {
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            if(sqlite3_column_int(ppStmt, 0) == 0) {
                salir = true;
            }
        }
        sqlite3_finalize(ppStmt);
    }
    if(salir) {
        sqlite3_exec(db, "DROP TABLE ejemplar2;", 0, 0, 0);
        return 0;
    }

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Elegir ejemplar" << endl << endl;
        sprintf(consulta, "SELECT claveejemplar,titulo,numeroorden FROM ejemplar2 NATURAL JOIN libro "
                "ORDER BY titulo,numeroorden LIMIT 20 OFFSET %d;", desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                    sqlite3_column_text(ppStmt, 1) << " [" <<
                    sqlite3_column_int(ppStmt, 2) << "]" << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
            default:
                if(isdigit(resp[0])) {
                    fila = atoi(resp);
                    salir=true;
                }
                break;
        }
    } while(!salir);
    // Borrar tabla temporal:
    sqlite3_exec(db, "DROP TABLE ejemplar2;", 0, 0, 0);

    return fila;
}

void CambiarFechaPrestamo(sqlite3 *db) {
    int clavesocio;
    int claveejemplar;
    char fecha[12];
    char consulta[1024];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un socio, y se podrá editar la fecha de entrega de uno de sus prestamos abiertos." << endl;

    cin.ignore();
    cout << "Socio: ";
    clavesocio = ListaSocios(db);
    if(!clavesocio) return;
    claveejemplar = ListaEjemplaresPrestados(db, clavesocio);
    if(!claveejemplar) {
        cout << "No hay prestamos abiertos para este socio" << endl;
        cin.ignore();
        cin.get();
        return;
    }
    cin.ignore();
    cout << "Fecha de entrega (AAAA/MM/DD) [dejar en blanco para fecha actual]: ";
    cin.getline(fecha, 12);
    sprintf(consulta, "UPDATE prestamo SET fecha_prestamo='%s' WHERE claveejemplar=%d AND fecha_devolucion IS NULL;", fecha, claveejemplar);
    if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    }
}

void AnularPrestamo(sqlite3 *db) {
    int clavesocio;
    int claveejemplar;
    char consulta[1024];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un socio, y se podrá editar la fecha de entrega de uno de sus prestamos abiertos." << endl;

    cin.ignore();
    cout << "Socio: ";
    clavesocio = ListaSocios(db);
    if(!clavesocio) return;
    claveejemplar = ListaEjemplaresPrestados(db, clavesocio);
    if(!claveejemplar) {
        cout << "No hay prestamos abiertos para este socio" << endl;
        cin.ignore();
        cin.get();
        return;
    }
    cin.ignore();
    // Borrar fila:
    sprintf(consulta, "DELETE FROM prestamo WHERE claveejemplar=%d AND fecha_devolucion IS NULL;", claveejemplar);
    if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    }
}

int ListaEjemplaresPrestados(sqlite3 *db, int clavesocio) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char resp[10];
    int desplazamiento=0;
    bool ultima;
    bool salir=false;
    int fila=0, i;

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    if(clavesocio) {
        do {
            cout << "Elegir un prestamo:" << endl << endl;
            sprintf(consulta, "SELECT claveejemplar, fecha_prestamo,titulo,formato FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro "
                    "WHERE clavesocio=%d AND fecha_devolucion IS NULL ORDER BY fecha_prestamo LIMIT 20 OFFSET %d;", clavesocio, desplazamiento);
            rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
            if( rc!=SQLITE_OK ){
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            } else {
                i = 0;
                while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                    cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                        sqlite3_column_text(ppStmt, 1) << " " <<
                        sqlite3_column_text(ppStmt, 2) << " (" <<
                        sqlite3_column_text(ppStmt, 3) << ")" << endl;
                    i++;
                }
                sqlite3_finalize(ppStmt);
            }
            ultima = (i < 20);
            while(i < 20) { cout << endl; i++; }
            cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
            cin >> resp;
            switch(resp[0]) {
                case 's':
                    if(!ultima) desplazamiento+=20;
                    break;
                case 'a':
                    if(desplazamiento > 0) desplazamiento-=20;

                    break;
                case 'x':
                    salir=true;
                    break;
                default:
                    if(isdigit(resp[0])) {
                        fila = atoi(resp);
                        salir=true;
                    }
                    break;
            }
        } while(!salir);
        return fila;
    } else return 0;
}

void DevolucionPrestamo(sqlite3 *db) {
    int clavesocio;
    int claveejemplar;
    char fecha[12];
    char consulta[1024];
    char notas[256];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un socio, y se podrá editar la fecha de devolución y algunas notas de uno de sus prestamos abiertos." << endl;

    cin.ignore();
    cout << "Socio: ";
    clavesocio = ListaSocios(db);
    if(!clavesocio) return;
    claveejemplar = ListaEjemplaresPrestados(db, clavesocio);
    if(!claveejemplar) {
        cout << "No hay prestamos abiertos para este socio" << endl;
        cin.ignore();
        cin.get();
        return;
    }
    cin.ignore();
    cout << "Fecha de devolucion (AAAA/MM/DD) [dejar en blanco para fecha actual]: ";
    cin.getline(fecha, 12);
    cout << "Algun comentario: ";
    cin.getline(notas, 256);
    if(strlen(fecha) > 0)
        sprintf(consulta, "UPDATE prestamo SET fecha_devolucion='%s',notas='%s' "
                "WHERE claveejemplar=%d AND fecha_devolucion IS NULL;", fecha, notas, claveejemplar);
    else
        sprintf(consulta, "UPDATE prestamo SET fecha_devolucion=date('now'),notas='%s' "
                "WHERE claveejemplar=%d AND fecha_devolucion IS NULL;", notas, claveejemplar);
    if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
        cout << "Error: " << sqlite3_errmsg(db) << endl;
        cin.ignore();
        cin.get();
    }
}

int ListaEjemplaresLibro(sqlite3 *db, int clavelibro) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int desplazamiento=0;
    char resp[10];
    bool salir=false;
    bool ultima;
    int fila=0;
    int i;

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Elegir ejemplar" << endl << endl;
        sprintf(consulta, "SELECT claveejemplar,titulo,numeroorden FROM ejemplar NATURAL JOIN libro "
                "WHERE clavelibro=%d ORDER BY titulo,numeroorden LIMIT 20 OFFSET %d;", clavelibro, desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                    sqlite3_column_text(ppStmt, 1) << " [" <<
                    sqlite3_column_int(ppStmt, 2) << "]" << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
            default:
                if(isdigit(resp[0])) {
                    fila = atoi(resp);
                    salir=true;
                }
                break;
        }
    } while(!salir);

    return fila;
}

void HistorialLibro(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    int clavelibro;
    int claveejemplar;
    char consulta[1024];
    int i, desplazamiento=0;
    bool ultima;
    bool salir=false;
    char resp[10];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un libro y un ejemplar para mostrar su historial de prestamos." << endl;

    cin.ignore();
    cout << "Libro:";
    clavelibro = ListaLibros(db);
    if(!clavelibro) return;
    claveejemplar = ListaEjemplaresLibro(db, clavelibro);
    if(!claveejemplar) {
        cout << "No hay ejemplares disponibles de este libro" << endl;
        cin.ignore();
        cin.get();
        return;
    }
    cin.ignore();

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Historial de ejemplar" << endl << endl;
        sprintf(consulta, "SELECT fecha_prestamo,fecha_devolucion,titulo,numeroorden FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro "
                "WHERE claveejemplar=%d ORDER BY fecha_prestamo LIMIT 20 OFFSET %d;", claveejemplar, desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_text(ppStmt, 0) << " ~ " <<
                    sqlite3_column_text(ppStmt, 1) << " " <<
                    sqlite3_column_text(ppStmt, 2) << " [" <<
                    sqlite3_column_int(ppStmt, 3) << "]" << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
        }
    } while(!salir);
}

void HistorialSocio(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    int clavesocio;
    char consulta[1024];
    int i, desplazamiento=0;
    bool ultima;
    bool salir=false;
    char resp[10];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira un socio para mostrar su historial de prestamos." << endl;

    cin.ignore();
    cout << "Socio:";
    clavesocio = ListaSocios(db);
    if(!clavesocio) return;
    cin.ignore();

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Historial del socio" << endl << endl;
        sprintf(consulta, "SELECT fecha_prestamo,fecha_devolucion,titulo,numeroorden FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro "
                "WHERE clavesocio=%d ORDER BY fecha_prestamo LIMIT 20 OFFSET %d;", clavesocio, desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_text(ppStmt, 0) << " ~ ";
                // Verificar si la fecha de devolucion es null
                if(sqlite3_column_text(ppStmt, 1)) cout << sqlite3_column_text(ppStmt, 1);
                else cout << " prestado ";
                cout << " " << sqlite3_column_text(ppStmt, 2) << " [" <<
                    sqlite3_column_int(ppStmt, 3) << "]" << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
        }
    } while(!salir);
}

void ConsultaPrestamos(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    int ndias;
    char consulta[1024];
    char numero[15];
    int i, desplazamiento=0;
    bool ultima;
    bool salir=false;
    char resp[10];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "Prestamos abiertos mas de un numero de dias." << endl;

    cin.ignore();
    cout << "Dias: ";
    cin.getline(numero, 15);
    ndias = atoi(numero);

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Prestamos abiertos mas de " << ndias << " dias" << endl << endl;
        sprintf(consulta, "SELECT fecha_prestamo,titulo,numeroorden,socio,JULIANDAY('now')-JULIANDAY(fecha_prestamo) AS dias "
                "FROM prestamo NATURAL JOIN ejemplar NATURAL JOIN libro NATURAL JOIN socio "
                "WHERE fecha_devolucion IS NULL AND JULIANDAY('now')-JULIANDAY(fecha_prestamo)>%d ORDER BY fecha_prestamo LIMIT 20 OFFSET %d;", ndias, desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_text(ppStmt, 0) << " ~ ";
                cout << sqlite3_column_text(ppStmt, 1);
                cout << " [" << sqlite3_column_int(ppStmt, 2) << "] ";
                cout << "Socio: " << sqlite3_column_text(ppStmt, 3);
                cout << " Dias: " << sqlite3_column_int(ppStmt, 4) << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
        }
    } while(!salir);
}

  Nombre Fichero Fecha Tamaño Contador Descarga
D bytes
[INACTIVO]