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:
2
core/__pycache__/.gitignore
vendored
Normal file
2
core/__pycache__/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
79
core/db.py
Normal file
79
core/db.py
Normal 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
2
core/db/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
3
core/events.py
Normal file
3
core/events.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from flask_socketio import SocketIO
|
||||
|
||||
socketio = SocketIO()
|
||||
23
core/openfood.py
Normal file
23
core/openfood.py
Normal 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
46
core/utils.py
Normal 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
|
||||
Reference in New Issue
Block a user