- Add non_loanable_devices table to database schema - Add web interface routes: /non_loanable_devices, /add_non_loanable_device, /edit_non_loanable_device, /delete_non_loanable_device - Add CLI menu options 8-10 for non-loanable device management - Add templates for non-loanable device CRUD operations - Update README.md with new feature documentation - Update REQUIREMENTS.md marking non-loanable devices as implemented - Add gestiontablets.sh script Implements: #porDefinir Registrar nuevos dispositivos no prestables
390 lines
No EOL
12 KiB
Python
390 lines
No EOL
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Minimal Tablet Lending and Return Management System
|
|
Using only built-in Python modules
|
|
"""
|
|
|
|
import sqlite3
|
|
from datetime import datetime
|
|
|
|
def init_db():
|
|
"""Initialize database with required tables"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
# Create tables
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS tablets (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
brand TEXT NOT NULL,
|
|
model TEXT NOT NULL,
|
|
serial_number TEXT UNIQUE NOT NULL,
|
|
status TEXT DEFAULT 'available'
|
|
)
|
|
''')
|
|
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
identification TEXT UNIQUE
|
|
)
|
|
''')
|
|
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS loans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
tablet_id INTEGER NOT NULL,
|
|
user_id INTEGER NOT NULL,
|
|
loan_date TEXT NOT NULL,
|
|
return_date TEXT,
|
|
status TEXT DEFAULT 'active',
|
|
FOREIGN KEY (tablet_id) REFERENCES tablets (id),
|
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
|
)
|
|
''')
|
|
|
|
# Create non_loanable_devices table
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS non_loanable_devices (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
brand TEXT NOT NULL,
|
|
model TEXT NOT NULL,
|
|
serial_number TEXT UNIQUE NOT NULL,
|
|
device_type TEXT NOT NULL,
|
|
location TEXT,
|
|
status TEXT DEFAULT 'available',
|
|
notes TEXT,
|
|
purchase_date TEXT,
|
|
purchase_cost REAL
|
|
)
|
|
''')
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def add_tablet(brand, model, serial_number):
|
|
"""Add a new tablet to inventory"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
cursor.execute('''
|
|
INSERT INTO tablets (brand, model, serial_number, status)
|
|
VALUES (?, ?, ?, 'available')
|
|
''', (brand, model, serial_number))
|
|
conn.commit()
|
|
print(f"✓ Added tablet: {brand} {model} ({serial_number})")
|
|
except sqlite3.IntegrityError:
|
|
print(f"✗ Error: Serial number {serial_number} already exists")
|
|
finally:
|
|
conn.close()
|
|
|
|
def add_user(name, identification):
|
|
"""Add a new user"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
cursor.execute('''
|
|
INSERT INTO users (name, identification)
|
|
VALUES (?, ?)
|
|
''', (name, identification))
|
|
conn.commit()
|
|
print(f"✓ Added user: {name} ({identification})")
|
|
except sqlite3.IntegrityError:
|
|
print(f"✗ Error: Identification {identification} already exists")
|
|
finally:
|
|
conn.close()
|
|
|
|
def loan_tablet(tablet_id, user_id):
|
|
"""Loan a tablet to a user"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
# Check if tablet is available
|
|
cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet_id,))
|
|
result = cursor.fetchone()
|
|
|
|
if result and result[0] == 'available':
|
|
# Update tablet status
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet_id,))
|
|
|
|
# Create loan record
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
cursor.execute('''
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
|
VALUES (?, ?, ?, 'active')
|
|
''', (tablet_id, user_id, loan_date))
|
|
|
|
conn.commit()
|
|
print(f"✓ Tablet {tablet_id} loaned to user {user_id}")
|
|
else:
|
|
print(f"✗ Error: Tablet {tablet_id} is not available for loan")
|
|
|
|
conn.close()
|
|
|
|
def return_tablet(loan_id):
|
|
"""Return a loaned tablet"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
# Get loan information
|
|
cursor.execute("SELECT tablet_id FROM loans WHERE id = ? AND status = 'active'", (loan_id,))
|
|
loan = cursor.fetchone()
|
|
|
|
if loan:
|
|
tablet_id = loan[0]
|
|
|
|
# Update loan status and return date
|
|
return_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
cursor.execute('''
|
|
UPDATE loans SET status = 'returned', return_date = ? WHERE id = ?
|
|
''', (return_date, loan_id))
|
|
|
|
# Update tablet status
|
|
cursor.execute("UPDATE tablets SET status = 'available' WHERE id = ?", (tablet_id,))
|
|
|
|
conn.commit()
|
|
print(f"✓ Tablet returned for loan {loan_id}")
|
|
else:
|
|
print(f"✗ Error: Loan {loan_id} not found or already returned")
|
|
|
|
conn.close()
|
|
|
|
def show_available_tablets():
|
|
"""Show available tablets"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("SELECT id, brand, model, serial_number FROM tablets WHERE status = 'available'")
|
|
tablets = cursor.fetchall()
|
|
|
|
print("\n=== Available Tablets ===")
|
|
if tablets:
|
|
for tablet in tablets:
|
|
print(f"ID: {tablet[0]}, {tablet[1]} {tablet[2]} ({tablet[3]})")
|
|
else:
|
|
print("No available tablets")
|
|
|
|
conn.close()
|
|
|
|
def show_active_loans():
|
|
"""Show active loans"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('''
|
|
SELECT l.id, t.brand, t.model, t.serial_number, u.name, l.loan_date
|
|
FROM loans l
|
|
JOIN tablets t ON l.tablet_id = t.id
|
|
JOIN users u ON l.user_id = u.id
|
|
WHERE l.status = 'active'
|
|
''')
|
|
loans = cursor.fetchall()
|
|
|
|
print("\n=== Active Loans ===")
|
|
if loans:
|
|
for loan in loans:
|
|
print(f"Loan ID: {loan[0]}, Tablet: {loan[1]} {loan[2]} ({loan[3]}), Borrower: {loan[4]}, Loan Date: {loan[5]}")
|
|
else:
|
|
print("No active loans")
|
|
|
|
conn.close()
|
|
|
|
def show_loan_history():
|
|
"""Show complete loan history"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('''
|
|
SELECT l.id, t.brand, t.model, t.serial_number, u.name,
|
|
l.loan_date, l.return_date, l.status
|
|
FROM loans l
|
|
JOIN tablets t ON l.tablet_id = t.id
|
|
JOIN users u ON l.user_id = u.id
|
|
ORDER BY l.loan_date DESC
|
|
''')
|
|
loans = cursor.fetchall()
|
|
|
|
print("\n=== Loan History ===")
|
|
if loans:
|
|
for loan in loans:
|
|
return_date = loan[6] or 'Not returned'
|
|
print(f"Loan ID: {loan[0]}, Tablet: {loan[1]} {loan[2]} ({loan[3]}), Borrower: {loan[4]}")
|
|
print(f" Loan Date: {loan[5]}, Return Date: {return_date}, Status: {loan[7]}")
|
|
else:
|
|
print("No loan history")
|
|
|
|
conn.close()
|
|
|
|
|
|
def add_non_loanable_device(brand, model, serial_number, device_type, location='', notes='', purchase_date='', purchase_cost=None):
|
|
"""Add a new non-loanable device to inventory"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
cursor.execute('''
|
|
INSERT INTO non_loanable_devices
|
|
(brand, model, serial_number, device_type, location, status, notes, purchase_date, purchase_cost)
|
|
VALUES (?, ?, ?, ?, ?, 'available', ?, ?, ?)
|
|
''', (brand, model, serial_number, device_type, location, notes, purchase_date, purchase_cost))
|
|
conn.commit()
|
|
print(f"✓ Added non-loanable device: {brand} {model} ({serial_number}) - Type: {device_type}")
|
|
except sqlite3.IntegrityError:
|
|
print(f"✗ Error: Serial number {serial_number} already exists")
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def show_non_loanable_devices():
|
|
"""Show all non-loanable devices"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("SELECT id, brand, model, serial_number, device_type, location, status FROM non_loanable_devices")
|
|
devices = cursor.fetchall()
|
|
|
|
print("\n=== Non-Loanable Devices ===")
|
|
if devices:
|
|
for device in devices:
|
|
print(f"ID: {device[0]}, Type: {device[4]}, {device[1]} {device[2]} ({device[3]}), Location: {device[5] or 'N/A'}, Status: {device[6]}")
|
|
else:
|
|
print("No non-loanable devices")
|
|
|
|
conn.close()
|
|
|
|
|
|
def delete_non_loanable_device(device_id):
|
|
"""Delete a non-loanable device"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("DELETE FROM non_loanable_devices WHERE id = ?", (device_id,))
|
|
conn.commit()
|
|
|
|
if cursor.rowcount > 0:
|
|
print(f"✓ Non-loanable device {device_id} deleted")
|
|
else:
|
|
print(f"✗ Error: Device {device_id} not found")
|
|
|
|
conn.close()
|
|
|
|
def main():
|
|
"""Main menu"""
|
|
init_db()
|
|
|
|
print("=== Tablet Lending and Return Management System ===")
|
|
print("Using SQLite database: tablets.db")
|
|
|
|
while True:
|
|
print("\nMenu:")
|
|
print("1. Add Tablet")
|
|
print("2. Add User")
|
|
print("3. Loan Tablet")
|
|
print("4. Return Tablet")
|
|
print("5. Show Available Tablets")
|
|
print("6. Show Active Loans")
|
|
print("7. Show Loan History")
|
|
print("8. Add Non-Loanable Device")
|
|
print("9. Show Non-Loanable Devices")
|
|
print("10. Delete Non-Loanable Device")
|
|
print("11. Exit")
|
|
|
|
choice = input("Enter your choice (1-11): ")
|
|
|
|
if choice == '1':
|
|
print("\n=== Add Tablet ===")
|
|
brand = input("Brand: ")
|
|
model = input("Model: ")
|
|
serial_number = input("Serial Number: ")
|
|
add_tablet(brand, model, serial_number)
|
|
|
|
elif choice == '2':
|
|
print("\n=== Add User ===")
|
|
name = input("Name: ")
|
|
identification = input("Identification: ")
|
|
add_user(name, identification)
|
|
|
|
elif choice == '3':
|
|
print("\n=== Loan Tablet ===")
|
|
show_available_tablets()
|
|
show_users()
|
|
|
|
tablet_id = input("Enter Tablet ID to loan: ")
|
|
user_id = input("Enter User ID: ")
|
|
|
|
try:
|
|
loan_tablet(int(tablet_id), int(user_id))
|
|
except ValueError:
|
|
print("✗ Error: Invalid ID format")
|
|
|
|
elif choice == '4':
|
|
print("\n=== Return Tablet ===")
|
|
show_active_loans()
|
|
|
|
loan_id = input("Enter Loan ID to return: ")
|
|
|
|
try:
|
|
return_tablet(int(loan_id))
|
|
except ValueError:
|
|
print("✗ Error: Invalid Loan ID format")
|
|
|
|
elif choice == '5':
|
|
show_available_tablets()
|
|
|
|
elif choice == '6':
|
|
show_active_loans()
|
|
|
|
elif choice == '7':
|
|
show_loan_history()
|
|
|
|
elif choice == '8':
|
|
print("\n=== Add Non-Loanable Device ===")
|
|
brand = input("Brand: ")
|
|
model = input("Model: ")
|
|
serial_number = input("Serial Number: ")
|
|
device_type = input("Device Type (e.g., projector, monitor): ")
|
|
location = input("Location (optional): ")
|
|
notes = input("Notes (optional): ")
|
|
add_non_loanable_device(brand, model, serial_number, device_type, location, notes)
|
|
|
|
elif choice == '9':
|
|
show_non_loanable_devices()
|
|
|
|
elif choice == '10':
|
|
print("\n=== Delete Non-Loanable Device ===")
|
|
show_non_loanable_devices()
|
|
device_id = input("Enter Device ID to delete: ")
|
|
try:
|
|
delete_non_loanable_device(int(device_id))
|
|
except ValueError:
|
|
print("✗ Error: Invalid ID format")
|
|
|
|
elif choice == '11':
|
|
print("Goodbye!")
|
|
break
|
|
|
|
else:
|
|
print("✗ Invalid choice. Please try again.")
|
|
|
|
def show_users():
|
|
"""Show available users"""
|
|
conn = sqlite3.connect('tablets.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("SELECT id, name, identification FROM users")
|
|
users = cursor.fetchall()
|
|
|
|
print("\n=== Available Users ===")
|
|
if users:
|
|
for user in users:
|
|
print(f"ID: {user[0]}, {user[1]} ({user[2]})")
|
|
else:
|
|
print("No users available")
|
|
|
|
conn.close()
|
|
|
|
if __name__ == '__main__':
|
|
main() |