from flask import Flask def create_app(): app = Flask(__name__) # Tenant subdomain resolver (before every request) from middleware_tenant import resolve_tenant app.before_request(resolve_tenant) # ─── PWA: Service Worker must be served from /pos/ scope ────── @app.route('/pos/sw.js') def pos_sw(): from flask import send_from_directory return send_from_directory('static/pwa', 'sw.js', mimetype='application/javascript') # Register blueprints from blueprints.auth_bp import auth_bp app.register_blueprint(auth_bp) from blueprints.config_bp import config_bp app.register_blueprint(config_bp) from blueprints.inventory_bp import inventory_bp app.register_blueprint(inventory_bp) from blueprints.catalog_bp import catalog_bp app.register_blueprint(catalog_bp) from blueprints.pos_bp import pos_bp app.register_blueprint(pos_bp) from blueprints.customers_bp import customers_bp app.register_blueprint(customers_bp) from blueprints.cashregister_bp import cashregister_bp app.register_blueprint(cashregister_bp) from blueprints.invoicing_bp import invoicing_bp app.register_blueprint(invoicing_bp) from blueprints.accounting_bp import accounting_bp app.register_blueprint(accounting_bp) from blueprints.chat_bp import chat_bp app.register_blueprint(chat_bp) # Health check @app.route('/pos/health') def health(): return {'status': 'ok'} from flask import render_template, send_from_directory, jsonify, g @app.route('/favicon.ico') def favicon(): return send_from_directory('static/pwa', 'icon-192.png', mimetype='image/png') @app.route('/pos/login') def pos_login(): return render_template('login.html', tenant_id=getattr(g, 'tenant_id', None), tenant_name=getattr(g, 'tenant_name', None), tenant_subdomain=getattr(g, 'tenant_subdomain', None)) @app.route('/pos/catalog') def pos_catalog(): return render_template('catalog.html') @app.route('/pos/inventory') def pos_inventory(): return render_template('inventory.html') @app.route('/pos/sale') def pos_sale(): return render_template('pos.html') @app.route('/pos/customers') def pos_customers(): return render_template('customers.html') @app.route('/pos/invoicing') def pos_invoicing(): return render_template('invoicing.html') @app.route('/pos/accounting') def pos_accounting(): return render_template('accounting.html') @app.route('/pos/dashboard') def pos_dashboard(): return render_template('dashboard.html') @app.route('/pos/config') def pos_config(): return render_template('config.html') @app.route('/pos/reports') def pos_reports(): return render_template('reports.html') @app.route('/pos/static/') def pos_static(filename): return send_from_directory('static', filename) # ─── Sync: full inventory for offline cache ─────────────────── from middleware import require_auth as _require_auth @app.route('/pos/api/sync/inventory', methods=['GET']) @_require_auth() def sync_full_inventory(): """Download full inventory for offline cache.""" from tenant_db import get_tenant_conn conn = get_tenant_conn(g.tenant_id) cur = conn.cursor() branch_id = g.branch_id cur.execute(""" SELECT i.id, i.part_number, i.barcode, i.name, i.brand, i.unit, i.price_1, i.price_2, i.price_3, i.tax_rate, COALESCE(s.stock, 0) AS stock FROM inventory i LEFT JOIN ( SELECT inventory_id, COALESCE(SUM(quantity), 0) AS stock FROM inventory_operations GROUP BY inventory_id ) s ON s.inventory_id = i.id WHERE i.is_active = true AND i.branch_id = %s ORDER BY i.name """, [branch_id]) items = [] for r in cur.fetchall(): items.append({ 'item_id': r[0], 'sku': r[1], 'barcode': r[2], 'name': r[3], 'brand': r[4], 'unit': r[5], 'price_1': float(r[6]) if r[6] else 0, 'price_2': float(r[7]) if r[7] else 0, 'price_3': float(r[8]) if r[8] else 0, 'tax_rate': float(r[9]) if r[9] else 0.16, 'stock': r[10] }) cur.close() conn.close() return jsonify({'items': items, 'count': len(items)}) return app if __name__ == '__main__': app = create_app() app.run(host='0.0.0.0', port=5001, debug=True)