From 6789f0899a16c8b798279e31bc78c2f8ed8bc1ce Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 23 Jan 2026 09:14:20 -0600 Subject: [PATCH] V1.0.0.5 - Refactor app.py: Split User, Session, and Admin logic into blueprints --- app.py | 90 +----------------- .../admin_locations.cpython-313.pyc | Bin 0 -> 3856 bytes blueprints/admin_locations.py | 89 +++++++++++++++++ database/scanlook.db | Bin 9826304 -> 9826304 bytes 4 files changed, 93 insertions(+), 86 deletions(-) create mode 100644 blueprints/__pycache__/admin_locations.cpython-313.pyc create mode 100644 blueprints/admin_locations.py diff --git a/app.py b/app.py index fa18fa6..be47068 100644 --- a/app.py +++ b/app.py @@ -16,11 +16,14 @@ from db import query_db, execute_db, get_db from blueprints.data_imports import data_imports_bp from blueprints.users import users_bp from blueprints.sessions import sessions_bp -from utils import login_required, role_required +from blueprints.admin_locations import admin_locations_bp +from utils import login_required +# Register Blueprints app.register_blueprint(data_imports_bp) app.register_blueprint(users_bp) app.register_blueprint(sessions_bp) +app.register_blueprint(admin_locations_bp) # V1.0: Use environment variable for production, fallback to demo key for development app.secret_key = os.environ.get('SCANLOOK_SECRET_KEY', 'scanlook-demo-key-replace-for-production') @@ -241,91 +244,6 @@ def complete_location(location_count_id): return jsonify({'success': True, 'message': 'Bin marked as complete'}) -@app.route('/location//reopen', methods=['POST']) -@login_required -def reopen_location(location_count_id): - """Reopen a completed location (admin/owner only)""" - # Check permissions - user = query_db('SELECT role FROM Users WHERE user_id = ?', [session['user_id']], one=True) - if not user or user['role'] not in ['owner', 'admin']: - return jsonify({'success': False, 'message': 'Permission denied'}), 403 - - # Verify location exists - loc = query_db('SELECT * FROM LocationCounts WHERE location_count_id = ?', [location_count_id], one=True) - - if not loc: - return jsonify({'success': False, 'message': 'Location not found'}) - - # Reopen the location - execute_db(''' - UPDATE LocationCounts - SET status = 'in_progress', end_timestamp = NULL - WHERE location_count_id = ? - ''', [location_count_id]) - - return jsonify({'success': True, 'message': 'Bin reopened for counting'}) - - -@app.route('/location//delete', methods=['POST']) -@login_required -def delete_location_count(location_count_id): - """Delete all counts for a location (soft delete)""" - # Verify ownership - loc = query_db('SELECT * FROM LocationCounts WHERE location_count_id = ?', [location_count_id], one=True) - - if not loc: - return jsonify({'success': False, 'message': 'Location not found'}) - - if loc['counted_by'] != session['user_id'] and session['role'] not in ['owner', 'admin']: - return jsonify({'success': False, 'message': 'Permission denied'}) - - # Soft delete all scan entries for this location - execute_db(''' - UPDATE ScanEntries - SET is_deleted = 1 - WHERE location_count_id = ? - ''', [location_count_id]) - - # Delete the location count record - execute_db(''' - DELETE FROM LocationCounts - WHERE location_count_id = ? - ''', [location_count_id]) - - return jsonify({'success': True, 'message': 'Bin count deleted'}) - - -@app.route('/location//scans') -@login_required -def get_location_scans(location_count_id): - """Get all scans for a specific location (admin/owner only)""" - # Check permissions - user = query_db('SELECT role FROM Users WHERE user_id = ?', [session['user_id']], one=True) - if not user or user['role'] not in ['owner', 'admin']: - return jsonify({'success': False, 'message': 'Permission denied'}), 403 - - try: - scans = query_db(''' - SELECT - se.*, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.location_count_id = ? - AND se.is_deleted = 0 - ORDER BY se.scan_timestamp DESC - ''', [location_count_id]) - - # Convert Row objects to dicts - scans_list = [dict(scan) for scan in scans] if scans else [] - - return jsonify({'success': True, 'scans': scans_list}) - - except Exception as e: - return jsonify({'success': False, 'message': str(e)}) - - @app.route('/count//location/') @login_required def count_location(session_id, location_count_id): diff --git a/blueprints/__pycache__/admin_locations.cpython-313.pyc b/blueprints/__pycache__/admin_locations.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3738e32f8650b9a56ede13a424e05da49ee94d40 GIT binary patch literal 3856 zcmcInO>7&-6`uVQDUuQ`QL-%6wI{Ayi$*NDu$0=0q*M~ASkw;%Et#|xQDQ|dH?v%dw52*g z(E<2&e%^cY=Dqpe8}4^?g%Gs2engGmbRqOP8~9CWboT!So%={ds<4Ps0ufRo5mOS8 zFq;z><&;8{R0rwcV`(vv3X&j3IpG3Q<)ru7um#uH)wJ_Qae9>K)|PI%@@iVmfXYBcw{KZRT4-3$&(Hx>g5UmQ zAotOFb4?Z28-7O+BNj^Vv82j3BvZ%=!!Tw|>qICks0v$GJ6;57?G!@me&mrFP=jjc zi0FGv4ULHxTXzX(kMxRJ(brq-enLy6RSRec@a@^IT}*?2M{w)jlG@!a6_4(WtZ}Dc z4HvA+R#~_8BKBQ}hq;qStvjYpuw|CF zO0`Y>i=Too53e_NzwB+j)QJ8Z3$bs5Z+$cH%$mbqpBI~!jY}X<+_@XVjh0f1Sq^(u9V0k6GwA2*zoN}x|oM&fn z)-bVmEy3?4i$E0MD2BNimkGOygxwq>Gr}Sa`005{ohvg>yAmu=c6H%Q?)Ab%PR(TJ z=X0xT=@bM^&8Y>=T(Yd!a~nPYY3>OSjcjeZQO}$vvIqv1Y;SrW$X#?0LXkeFZ~Auw zj|OWqs`K59^MfDO!uh+2KZT<|9(&O7lS${&je6|HC(+Xn$DQ$;wSk3tH1%lCiKd-k z`k(`aV@_<@S=F5MhI6Z23svsU{UzMpU|*;Wr0UT{C%6clN^}p&r-FMy6h7;8o;~Q4 zm2(FX6p1~sovVwr*it>b?8wXeU>N!;381L63w}J_{|Y+V@y?Mbs!}_`ITD$hc~fPU zZIAGLk)c1pg0{|r#+w>sq81?OI##9eD!ZCnNtzp9sir zvyve{!M%aCa@o6elwUxt1^(13*?>vrv^W_8V>2WQbe_OD5IGIR4e<-B7xNq21kQj+ z4tC#-R5ZJ zu5W;qZs`T1WE5IZ`&7{;@DN2DogRLVFgnjc%8^V4fk}G{Lf6C|6cXGhxNUQ6p6Vl) zpMPq0!zhfjTa*=_rBytiZ( zFs*HUDLJ3Psr14Mp4F&cHcWlNd;?6f;F&q+S%<-HoL*svDqD8mtX4MQ!866CTPE7H zdYOa^crVbl=SCar%u0f3ZcA`i+c?r|iR2od{XS!3!EJd_B$Db}lQ%9B_TG0DegjtG z@E3tamS-iFpIo_U6l}LM`DQ`iV$Wh%qBa3Go~ypjc)!Kjn7kX}TJmLs+OD8eriw+@ z&t$!ux_y+!I4`*ZJX7$azXtL@zXkT>m@@szsWW>hG!)tELs9H>;r;$l&xL!+KZelt zsoiY7|Jpm*hnb%hAB_F{wcYIR`>*}>!oAR+;b}4U?&aFRR6RQF1gHP?cLkju`{zLr zMf&%V07}{0<308Lo?pIF8/reopen', methods=['POST']) +@login_required +def reopen_location(location_count_id): + """Reopen a completed location (admin/owner only)""" + # Check permissions + user = query_db('SELECT role FROM Users WHERE user_id = ?', [session['user_id']], one=True) + if not user or user['role'] not in ['owner', 'admin']: + return jsonify({'success': False, 'message': 'Permission denied'}), 403 + + # Verify location exists + loc = query_db('SELECT * FROM LocationCounts WHERE location_count_id = ?', [location_count_id], one=True) + + if not loc: + return jsonify({'success': False, 'message': 'Location not found'}) + + # Reopen the location + execute_db(''' + UPDATE LocationCounts + SET status = 'in_progress', end_timestamp = NULL + WHERE location_count_id = ? + ''', [location_count_id]) + + return jsonify({'success': True, 'message': 'Bin reopened for counting'}) + + +@admin_locations_bp.route('/location//delete', methods=['POST']) +@login_required +def delete_location_count(location_count_id): + """Delete all counts for a location (soft delete)""" + # Verify ownership + loc = query_db('SELECT * FROM LocationCounts WHERE location_count_id = ?', [location_count_id], one=True) + + if not loc: + return jsonify({'success': False, 'message': 'Location not found'}) + + if loc['counted_by'] != session['user_id'] and session['role'] not in ['owner', 'admin']: + return jsonify({'success': False, 'message': 'Permission denied'}) + + # Soft delete all scan entries for this location + execute_db(''' + UPDATE ScanEntries + SET is_deleted = 1 + WHERE location_count_id = ? + ''', [location_count_id]) + + # Delete the location count record + execute_db(''' + DELETE FROM LocationCounts + WHERE location_count_id = ? + ''', [location_count_id]) + + return jsonify({'success': True, 'message': 'Bin count deleted'}) + + +@admin_locations_bp.route('/location//scans') +@login_required +def get_location_scans(location_count_id): + """Get all scans for a specific location (admin/owner only)""" + # Check permissions + user = query_db('SELECT role FROM Users WHERE user_id = ?', [session['user_id']], one=True) + if not user or user['role'] not in ['owner', 'admin']: + return jsonify({'success': False, 'message': 'Permission denied'}), 403 + + try: + scans = query_db(''' + SELECT + se.*, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.location_count_id = ? + AND se.is_deleted = 0 + ORDER BY se.scan_timestamp DESC + ''', [location_count_id]) + + # Convert Row objects to dicts + scans_list = [dict(scan) for scan in scans] if scans else [] + + return jsonify({'success': True, 'scans': scans_list}) + + except Exception as e: + return jsonify({'success': False, 'message': str(e)}) \ No newline at end of file diff --git a/database/scanlook.db b/database/scanlook.db index 5795be98ee5c8504a09ca417c414bb5849e0890a..72e0bcb391d60c8a655bcbc1e2e58b87f635b427 100644 GIT binary patch delta 1141 zcmaLTNl#Nz7{>8?u}E9E_kaq@T&;=<=xuKqtYR6&83932QK6Qq)lw=gASjcfh$A?} zg)!j^m}rP}VUYLP* z-Q|)^Gt(}&mMKY+Nm-CYk(g6|s_(>rI;J+N7G)tawK%F6Bh!B_jn2R0yOZ4XdykYmkl%ti?KHA`98bK`!!;4-3{~L*%Q=x+$1pO8K0i z7K(ahR(+^6sNcm$B`xK1BCLrG%?`~{Y-u=np~vrU3ts9E z`8;la+%IcoPQA>jIozJGuh(1W_5~`86j+xFyrJ-Nfv4Z&_qKVudII5p;-yYqi^b(Y zq+!n5kx-qQONY)9(bE-4ah-pcs;T*3r&cz;G;hU5Y{F&~U<6XK@Z~Xonl;;XwzyxPXhe1RpNr3jF9q09^>;D!LIu7(M7kAFiPv1GtVG t7{m~UF@jOt#4U{BHtt{?cQJu`xQ_>Th(~ygNlf7hp5j?#Y5uuc`wgH1_p$&0 delta 935 zcmZY2+f$5j9LMqJw|3h@Jz3qK{@ehO56Is+) zWxJ5y8}F$U!wg^jDLQjVZW$J z{WlHI`vnYG1|v*Jf*BTAVM8)fphAO0D$=kVE0B&1WMU<uNMMshfl``ei~dj=ql+R*A+$ zU|Mf8nCtR&tJ|$ay)*i@@ZzkFH7LSbtV1!@V*^T1iZX1(COF`P3vPJeML9NO3%0_C zrH$>_ft~PU7b>tDm8ilVRAVn{P>X%oj{`V}L#RUl^=QChG~x)3q6x=v94BxRr*Il) z(2N$e;w;Xg4edCO4qQMdF5(g{;|jXajUHS@FZyr|*Kq^=xQSZ`;x_JJ0CzEn5QcCM k_c4qIc!)3_VFZsciYFMuQ#`{sB6yA$c$rw7ePwg}0vvP6H~;_u