modified: Dockerfile

modified:   README.md
	modified:   app.py
	new file:   blueprints/__init__.py
	new file:   blueprints/__pycache__/.gitignore
	new file:   blueprints/auth.py
	new file:   blueprints/finance.py
	new file:   blueprints/inventory.py
	new file:   blueprints/pos.py
	new file:   blueprints/sales.py
	new file:   core/__pycache__/.gitignore
	new file:   core/db.py
	new file:   core/db/.gitignore
	new file:   core/events.py
	new file:   core/openfood.py
	new file:   core/utils.py
	modified:   static/style.css
	modified:   templates/checkout.html
	modified:   templates/dicom.html
	modified:   templates/login.html
	modified:   templates/macros/base.html
	modified:   templates/macros/modals.html
	modified:   templates/macros/navbar.html
This commit is contained in:
2026-05-21 00:05:31 -04:00
parent c2373c3ed6
commit a5babd8131
23 changed files with 2102 additions and 1169 deletions

2
core/__pycache__/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

79
core/db.py Normal file
View File

@@ -0,0 +1,79 @@
import sqlite3
_db_file = None
def init_db(db_file):
global _db_file
_db_file = db_file
with get_db_connection() as conn:
conn.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY, username TEXT UNIQUE, password TEXT)''')
conn.execute('''CREATE TABLE IF NOT EXISTS products
(barcode TEXT PRIMARY KEY,
name TEXT,
price REAL,
image_url TEXT,
stock REAL DEFAULT 0,
unit_type TEXT DEFAULT 'unit')''')
conn.execute('''CREATE TABLE IF NOT EXISTS sales
(id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT DEFAULT CURRENT_TIMESTAMP,
total REAL,
payment_method TEXT)''')
conn.execute('''CREATE TABLE IF NOT EXISTS sale_items
(id INTEGER PRIMARY KEY AUTOINCREMENT,
sale_id INTEGER,
barcode TEXT,
name TEXT,
price REAL,
quantity REAL,
subtotal REAL,
FOREIGN KEY(sale_id) REFERENCES sales(id))''')
conn.execute('''CREATE TABLE IF NOT EXISTS dicom
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE,
amount REAL DEFAULT 0,
notes TEXT,
image_url TEXT,
last_updated TEXT DEFAULT CURRENT_TIMESTAMP)''')
conn.execute('''CREATE TABLE IF NOT EXISTS debtors
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE,
contact_info TEXT)''')
conn.execute('''CREATE TABLE IF NOT EXISTS debtor_tickets
(id INTEGER PRIMARY KEY AUTOINCREMENT,
debtor_id INTEGER NOT NULL,
date TEXT DEFAULT CURRENT_TIMESTAMP,
total REAL NOT NULL,
amount_paid REAL DEFAULT 0,
status TEXT DEFAULT 'unpaid',
FOREIGN KEY(debtor_id) REFERENCES debtors(id) ON DELETE CASCADE)''')
conn.execute('''CREATE TABLE IF NOT EXISTS debtor_ticket_items
(id INTEGER PRIMARY KEY AUTOINCREMENT,
ticket_id INTEGER NOT NULL,
barcode TEXT,
name TEXT,
price REAL,
quantity REAL,
subtotal REAL,
FOREIGN KEY(ticket_id) REFERENCES debtor_tickets(id) ON DELETE CASCADE)''')
user = conn.execute('SELECT * FROM users WHERE username = ?', ('admin',)).fetchone()
if not user:
from werkzeug.security import generate_password_hash
hashed_pw = generate_password_hash('choripan1234')
conn.execute('INSERT INTO users (username, password) VALUES (?, ?)', ('admin', hashed_pw))
conn.commit()
def get_db_connection():
if _db_file is None:
raise RuntimeError("Database not initialized. Call init_db(db_file) first.")
return sqlite3.connect(_db_file)

2
core/db/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

3
core/events.py Normal file
View File

@@ -0,0 +1,3 @@
from flask_socketio import SocketIO
socketio = SocketIO()

23
core/openfood.py Normal file
View File

@@ -0,0 +1,23 @@
import requests
from core.utils import download_image
def fetch_from_openfoodfacts(barcode, cache_dir):
url = f"https://world.openfoodfacts.org/api/v2/product/{barcode}.json"
try:
headers = {'User-Agent': 'SekiPOS/1.0'}
resp = requests.get(url, headers=headers, timeout=5).json()
if resp.get('status') == 1:
p = resp.get('product', {})
name = p.get('product_name_es') or p.get('product_name') or p.get('brands', 'Unknown')
imgs = p.get('selected_images', {}).get('front', {}).get('display', {})
img_url = imgs.get('es') or imgs.get('en') or p.get('image_url', '')
if img_url:
local_img = download_image(img_url, barcode, cache_dir)
return {"name": name, "image": local_img}
return {"name": name, "image": None}
except Exception as e:
print(f"API Error: {e}")
return None

46
core/utils.py Normal file
View File

@@ -0,0 +1,46 @@
import os
import sys
import mimetypes
import requests
def get_bundled_path(relative_path):
"""Path for read-only files packed inside the .exe (templates, static)"""
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base_path = project_root
return os.path.join(base_path, relative_path)
def get_persistent_path(relative_path):
"""Path for read/write files that must survive reboots (db, cache)"""
if getattr(sys, 'frozen', False):
base_path = os.path.dirname(sys.executable)
else:
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base_path = project_root
return os.path.join(base_path, relative_path)
def download_image(url, barcode, cache_dir):
if not url or not url.startswith('http'):
return url
try:
headers = {'User-Agent': 'SekiPOS/1.2'}
with requests.get(url, headers=headers, stream=True, timeout=5) as r:
r.raise_for_status()
content_type = r.headers.get('content-type')
ext = mimetypes.guess_extension(content_type) or '.jpg'
local_filename = f"{barcode}{ext}"
local_path = os.path.join(cache_dir, local_filename)
with open(local_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
return f"/static/cache/{local_filename}"
except Exception as e:
print(f"Download failed: {e}")
return url