app.main.create_user()   C
last analyzed

Complexity

Conditions 9

Size

Total Lines 59
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 41
nop 0
dl 0
loc 59
rs 6.5626
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
from flask import request, redirect, url_for, send_from_directory, flash, send_file, Response
2
from flask_login import login_required, current_user
3
from flask import Blueprint, render_template
4
from .models import Schedule, Balance, User, Settings, Email, Hold, Skip, GlobalEmailSettings
5
from app import db
6
from datetime import datetime
7
import os
8
from sqlalchemy import desc, extract, asc
9
from werkzeug.security import generate_password_hash, check_password_hash
10
from .cashflow import update_cash, plot_cash
11
from .auth import admin_required, global_admin_required, account_owner_required
12
from .files import export, upload, version
13
from .getemail import send_account_activation_notification
14
15
16
main = Blueprint('main', __name__)
17
18
19
def get_effective_user_id():
20
    """
21
    Get the effective user ID for data filtering.
22
    For guest users, returns their account owner's ID.
23
    For account owners, returns their own ID.
24
    """
25
    if current_user.account_owner_id:
26
        # Guest user - return account owner's ID
27
        return current_user.account_owner_id
28
    else:
29
        # Account owner or standalone user - return their own ID
30
        return current_user.id
31
32
33
@main.route('/', methods=('GET', 'POST'))
34
@login_required
35
def index():
36
    # Get effective user ID (account owner for guests, self for owners)
37
    user_id = get_effective_user_id()
38
39
    # get today's date
40
    todaydate = datetime.today().strftime('%A, %B %d, %Y')
41
42
    # query the latest balance information for this user
43
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
44
45
    try:
46
        float(balance.amount)
47
        db.session.query(Balance).filter_by(user_id=user_id).delete()
48
        balance = Balance(amount=balance.amount, date=datetime.today(), user_id=user_id)
49
        db.session.add(balance)
50
        db.session.commit()
51
    except:
52
        balance = Balance(amount='0', date=datetime.today(), user_id=user_id)
53
        db.session.add(balance)
54
        db.session.commit()
55
56
    # Pre-filter data by user before passing to cashflow
57
    schedules = Schedule.query.filter_by(user_id=user_id).all()
58
    holds = Hold.query.filter_by(user_id=user_id).all()
59
    skips = Skip.query.filter_by(user_id=user_id).all()
60
61
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
62
63
    # plot cash flow results
64
    minbalance, graphJSON = plot_cash(run)
65
66
    if current_user.admin:
67
        return render_template('index.html', title='Index', todaydate=todaydate, balance=balance.amount,
68
                           minbalance=minbalance, graphJSON=graphJSON)
69
    else:
70
        return render_template('index_guest.html', title='Index', todaydate=todaydate, balance=balance.amount,
71
                           minbalance=minbalance, graphJSON=graphJSON)
72
73
74
@main.route('/refresh')
75
@login_required
76
def refresh():
77
78
    return redirect(url_for('main.index'))
79
80
81
@main.route('/settings')
82
@login_required
83
def settings():
84
    # get about info - available to all users
85
    about = version()
86
87
    if current_user.admin:
88
        return render_template('settings.html', about=about)
89
    else:
90
        return render_template('settings_guest.html', about=about)
91
92
93
@main.route('/schedule')
94
@login_required
95
@admin_required
96
def schedule():
97
    user_id = get_effective_user_id()
98
    schedule = Schedule.query.filter_by(user_id=user_id).order_by(asc(extract('day', Schedule.startdate)))
99
100
    return render_template('schedule_table.html', title='Schedule Table', schedule=schedule)
101
102
103
@main.route('/holds')
104
@login_required
105
@admin_required
106
def holds():
107
    user_id = get_effective_user_id()
108
    hold = Hold.query.filter_by(user_id=user_id)
109
    skip = Skip.query.filter_by(user_id=user_id)
110
111
    return render_template('holds_table.html', title='Holds Table', hold=hold, skip=skip)
112
113
114
@main.route('/create', methods=('GET', 'POST'))
115
@login_required
116
@admin_required
117
def create():
118
    # create a new schedule item
119
    user_id = get_effective_user_id()
120
    format = '%Y-%m-%d'
121
    if request.method == 'POST':
122
        name = request.form['name']
123
        amount = request.form['amount']
124
        frequency = request.form['frequency']
125
        startdate = request.form['startdate']
126
        type = request.form['type']
127
        schedule = Schedule(name=name,
128
                            type=type,
129
                            amount=amount,
130
                            frequency=frequency,
131
                            startdate=datetime.strptime(startdate, format).date(),
132
                            firstdate=datetime.strptime(startdate, format).date(),
133
                            user_id=user_id)
134
        existing = Schedule.query.filter_by(name=name, user_id=user_id).first()
135
        if existing:
136
            flash("Schedule already exists")
137
            return redirect(url_for('main.schedule'))
138
        db.session.add(schedule)
139
        db.session.commit()
140
        flash("Added Successfully")
141
142
        return redirect(url_for('main.schedule'))
143
144
    return redirect(url_for('main.schedule'))
145
146
147
@main.route('/update', methods=['GET', 'POST'])
148
@login_required
149
@admin_required
150
def update():
151
    # update an existing schedule item
152
    user_id = get_effective_user_id()
153
    format = '%Y-%m-%d'
154
155
    if request.method == 'POST':
156
        current = Schedule.query.filter_by(id=int(request.form['id']), user_id=user_id).first()
157
        existing = Schedule.query.filter_by(name=request.form['name'], user_id=user_id).first()
158
        if existing:
159
            if current.name != request.form['name']:
160
                flash("Schedule name already exists")
161
                return redirect(url_for('main.schedule'))
162
        my_data = Schedule.query.filter_by(id=int(request.form.get('id')), user_id=user_id).first()
163
        my_data.name = request.form['name']
164
        my_data.amount = request.form['amount']
165
        my_data.type = request.form['type']
166
        my_data.frequency = request.form['frequency']
167
        startdate = request.form['startdate']
168
        if (datetime.strptime(startdate, format).date() != my_data.startdate and my_data.startdate.day !=
169
                datetime.strptime(startdate, format).day):
170
            my_data.firstdate = datetime.strptime(startdate, format).date()
171
        my_data.startdate = datetime.strptime(startdate, format).date()
172
        db.session.commit()
173
        flash("Updated Successfully")
174
175
        return redirect(url_for('main.schedule'))
176
177
    return redirect(url_for('main.schedule'))
178
179
180
@main.route('/addhold/<id>')
181
@login_required
182
@admin_required
183
def addhold(id):
184
    # add a hold item from the schedule
185
    user_id = get_effective_user_id()
186
    schedule = Schedule.query.filter_by(id=int(id), user_id=user_id).first()
187
    hold = Hold(name=schedule.name, type=schedule.type, amount=schedule.amount, user_id=user_id)
188
    db.session.add(hold)
189
    db.session.commit()
190
    flash("Added Hold")
191
192
    return redirect(url_for('main.schedule'))
193
194
195
@main.route('/addskip/<id>')
196
@login_required
197
@admin_required
198
def addskip(id):
199
    # add a skip item from the schedule
200
    user_id = get_effective_user_id()
201
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
202
203
    # Pre-filter data by user
204
    schedules = Schedule.query.filter_by(user_id=user_id).all()
205
    holds = Hold.query.filter_by(user_id=user_id).all()
206
    skips = Skip.query.filter_by(user_id=user_id).all()
207
208
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
209
    transaction = trans.loc[int(id)]
210
    trans_type = ""
211
    if transaction[1] == "Expense":
212
        trans_type = "Income"
213
    elif transaction[1] == "Income":
214
        trans_type = "Expense"
215
    skip = Skip(name=transaction[0] + " (SKIP)", type=trans_type, amount=transaction[2], date=transaction[3], user_id=user_id)
216
    db.session.add(skip)
217
    db.session.commit()
218
    flash("Added Skip")
219
220
    return redirect(url_for('main.transactions'))
221
222
223
@main.route('/deletehold/<id>')
224
@login_required
225
@admin_required
226
def holds_delete(id):
227
    # delete a hold item
228
    user_id = get_effective_user_id()
229
    hold = Hold.query.filter_by(id=int(id), user_id=user_id).first()
230
231
    if hold:
232
        db.session.delete(hold)
233
        db.session.commit()
234
        flash("Deleted Successfully")
235
236
    return redirect(url_for('main.holds'))
237
238
239
@main.route('/deleteskip/<id>')
240
@login_required
241
@admin_required
242
def skips_delete(id):
243
    # delete a skip item
244
    user_id = get_effective_user_id()
245
    skip = Skip.query.filter_by(id=int(id), user_id=user_id).first()
246
247
    if skip:
248
        db.session.delete(skip)
249
        db.session.commit()
250
        flash("Deleted Successfully")
251
252
    return redirect(url_for('main.holds'))
253
254
255
@main.route('/clearholds')
256
@login_required
257
@admin_required
258
def clear_holds():
259
    # clear holds
260
    user_id = get_effective_user_id()
261
    db.session.query(Hold).filter_by(user_id=user_id).delete()
262
    db.session.commit()
263
264
    return redirect(url_for('main.holds'))
265
266
267
@main.route('/clearskips')
268
@login_required
269
@admin_required
270
def clear_skips():
271
    # clear skips
272
    user_id = get_effective_user_id()
273
    db.session.query(Skip).filter_by(user_id=user_id).delete()
274
    db.session.commit()
275
276
    return redirect(url_for('main.holds'))
277
278
279
@main.route('/delete/<id>')
280
@login_required
281
@admin_required
282
def schedule_delete(id):
283
    # delete a schedule item
284
    user_id = get_effective_user_id()
285
    schedule = Schedule.query.filter_by(id=int(id), user_id=user_id).first()
286
287
    if schedule:
288
        db.session.delete(schedule)
289
        db.session.commit()
290
        flash("Deleted Successfully")
291
292
    return redirect(url_for('main.schedule'))
293
294
295
@main.route('/favicon')
296
def favicon():
297
    return send_from_directory(os.path.join(main.root_path, 'static'),
298
                               'favicon.ico', mimetype='image/vnd.microsoft.icon')
299
300
301
@main.route('/appleicon')
302
def appleicon():
303
    return send_from_directory(os.path.join(main.root_path, 'static'),
304
                               'apple-touch-icon.png', mimetype='image/png')
305
306
307
@main.route('/balance', methods=('GET', 'POST'))
308
@login_required
309
@admin_required
310
def balance():
311
    # manually update the balance from the balance button
312
    user_id = get_effective_user_id()
313
    format = '%Y-%m-%d'
314
    if request.method == 'POST':
315
        amount = request.form['amount']
316
        dateentry = request.form['date']
317
        balance = Balance(amount=amount,
318
                          date=datetime.strptime(dateentry, format).date(),
319
                          user_id=user_id)
320
        db.session.add(balance)
321
        db.session.commit()
322
323
        return redirect(url_for('main.index'))
324
325
326
@main.route('/changepw', methods=('GET', 'POST'))
327
@login_required
328
def changepw():
329
    # change the users password from the settings page
330
    if request.method == 'POST':
331
        curr_user = current_user.id
332
        my_user = User.query.filter_by(id=curr_user).first()
333
        current = request.form['current']
334
        password = request.form['password']
335
        password2 = request.form['password2']
336
        if password == password2 and check_password_hash(my_user.password, current):
337
            my_user.password = generate_password_hash(password, method='scrypt')
338
            db.session.commit()
339
            flash('Password change successful')
340
        elif password != password2:
341
            flash('Passwords do not match')
342
        elif not check_password_hash(my_user.password, current):
343
            flash('Incorrect password')
344
345
        return redirect(url_for('main.settings'))
346
347
    return redirect(url_for('main.settings'))
348
349
350
@main.route('/signups', methods=('GET', 'POST'))
351
@login_required
352
@global_admin_required
353
def signups():
354
    # set the settings options, in this case disable signups, from the profile page
355
    if request.method == 'POST':
356
        signupsettingname = Settings.query.filter_by(name='signup').first()
357
358
        if signupsettingname:
359
            if request.form['signupvalue'] == "True":
360
                signupvalue = True
361
            else:
362
                signupvalue = False
363
            signupsettingname.value = signupvalue
364
            db.session.commit()
365
366
            return redirect(url_for('main.settings'))
367
368
        # store the signup option value in the database to check when the user clicks signup
369
        if request.form['signupvalue'] == "True":
370
            signupvalue = True
371
        else:
372
            signupvalue = False
373
        settings = Settings(name="signup", value=signupvalue)
374
        db.session.add(settings)
375
        db.session.commit()
376
377
        return redirect(url_for('main.settings'))
378
379
    return redirect(url_for('main.settings'))
380
381
382
@main.route('/transactions')
383
@login_required
384
@admin_required
385
def transactions():
386
    user_id = get_effective_user_id()
387
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
388
389
    # Pre-filter data by user
390
    schedules = Schedule.query.filter_by(user_id=user_id).all()
391
    holds = Hold.query.filter_by(user_id=user_id).all()
392
    skips = Skip.query.filter_by(user_id=user_id).all()
393
394
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
395
396
    return render_template('transactions_table.html', total=trans.to_dict(orient='records'))
397
398
399
@main.route('/email', methods=('GET', 'POST'))
400
@login_required
401
@admin_required
402
def email():
403
    # set the users email address, password, and server for the auto email balance update
404
    user_id = get_effective_user_id()
405
406
    if request.method == 'POST':
407
        emailsettings = Email.query.filter_by(user_id=user_id).first()
408
409
        if emailsettings:
410
            email = request.form['email']
411
            password = request.form['password']
412
            server = request.form['server']
413
            subjectstr = request.form['subject_str']
414
            startstr = request.form['start_str']
415
            endstr = request.form['end_str']
416
            emailsettings.email = email
417
            emailsettings.password = password
418
            emailsettings.server = server
419
            emailsettings.subjectstr = subjectstr
420
            emailsettings.startstr = startstr
421
            emailsettings.endstr = endstr
422
            db.session.commit()
423
424
            return redirect(url_for('main.settings'))
425
426
        email = request.form['email']
427
        password = request.form['password']
428
        server = request.form['server']
429
        subjectstr = request.form['subject_str']
430
        startstr = request.form['start_str']
431
        endstr = request.form['end_str']
432
        emailentry = Email(email=email, password=password, server=server, subjectstr=subjectstr, startstr=startstr,
433
                           endstr=endstr, user_id=user_id)
434
        db.session.add(emailentry)
435
        db.session.commit()
436
437
        return redirect(url_for('main.settings'))
438
439
    return redirect(url_for('main.settings'))
440
441
442
@main.route('/update_user', methods=['GET', 'POST'])
443
@login_required
444
@admin_required
445
def update_user():
446
    # update an existing user
447
    if request.method == 'POST':
448
        current = User.query.filter_by(id=int(request.form['id'])).first()
449
450
        # Check if the current user has permission to update this user
451
        if not current_user.is_global_admin and current.account_owner_id != current_user.id:
452
            flash("You don't have permission to update this user")
453
            if current_user.is_global_admin:
454
                return redirect(url_for('main.global_admin_panel'))
455
            else:
456
                return redirect(url_for('main.manage_guests'))
457
458
        existing = User.query.filter_by(email=request.form['email']).first()
459
        if existing:
460
            if current.email != request.form['email']:
461
                flash("Email already exists")
462
                if current_user.is_global_admin:
463
                    return redirect(url_for('main.global_admin_panel'))
464
                else:
465
                    return redirect(url_for('main.manage_guests'))
466
        my_data = User.query.get(request.form.get('id'))
467
        my_data.name = request.form['name']
468
        my_data.email = request.form['email']
469
470
        # Global admins can change roles, regular admins cannot
471
        if current_user.is_global_admin:
472
            # Handle role assignment
473
            role = request.form.get('role', 'user')
474
            if role == 'global_admin':
475
                my_data.admin = True
476
                my_data.is_global_admin = True
477
                # IMPORTANT: Global admins must always be active
478
                my_data.is_active = True
479
            elif role == 'admin':
480
                my_data.admin = True
481
                my_data.is_global_admin = False
482
            else:  # user
483
                my_data.admin = False
484
                my_data.is_global_admin = False
485
486
        db.session.commit()
487
        flash("Updated Successfully")
488
489
        # Redirect based on user role
490
        if current_user.is_global_admin:
491
            return redirect(url_for('main.global_admin_panel'))
492
        else:
493
            return redirect(url_for('main.manage_guests'))
494
495
    # Redirect based on user role
496
    if current_user.is_global_admin:
497
        return redirect(url_for('main.global_admin_panel'))
498
    else:
499
        return redirect(url_for('main.manage_guests'))
500
501
502
@main.route('/delete_user/<id>')
503
@login_required
504
@admin_required
505
def delete_user(id):
506
    # delete a user
507
    user = User.query.filter_by(id=int(id)).first()
508
509
    if user:
510
        # Global admins can delete any user (except themselves), regular admins can only delete their guests
511
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
512
            # Prevent deleting yourself
513
            if user.id == current_user.id:
514
                flash("You cannot delete your own account")
515
                if current_user.is_global_admin:
516
                    return redirect(url_for('main.global_admin_panel'))
517
                else:
518
                    return redirect(url_for('main.manage_guests'))
519
520
            # Manually delete all related data before deleting the user
521
            user_id = user.id
522
523
            # Delete all guest users for this admin user
524
            db.session.query(User).filter_by(account_owner_id=user_id).delete()
525
526
            # Delete all schedules for this user
527
            db.session.query(Schedule).filter_by(user_id=user_id).delete()
528
529
            # Delete all balances for this user
530
            db.session.query(Balance).filter_by(user_id=user_id).delete()
531
532
            # Delete all holds for this user
533
            db.session.query(Hold).filter_by(user_id=user_id).delete()
534
535
            # Delete all skips for this user
536
            db.session.query(Skip).filter_by(user_id=user_id).delete()
537
538
            # Delete all email configs for this user
539
            db.session.query(Email).filter_by(user_id=user_id).delete()
540
541
            # Now delete the user
542
            db.session.delete(user)
543
            db.session.commit()
544
            flash("Deleted Successfully")
545
        else:
546
            flash("You don't have permission to delete this user")
547
548
    # Redirect based on user role
549
    if current_user.is_global_admin:
550
        return redirect(url_for('main.global_admin_panel'))
551
    else:
552
        return redirect(url_for('main.manage_guests'))
553
554
555
@main.route('/activate_user/<id>')
556
@login_required
557
@admin_required
558
def activate_user(id):
559
    # activate a user account
560
    user = User.query.filter_by(id=int(id)).first()
561
562
    if user:
563
        # Global admins can activate any user, regular admins can only activate their guests
564
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
565
            user.is_active = True
566
            db.session.commit()
567
            flash(f"User {user.name} has been activated successfully")
568
569
            # Send activation notification email to the user
570
            try:
571
                send_account_activation_notification(user.name, user.email)
572
            except Exception as e:
573
                # Don't fail activation if email notification fails
574
                print(f"Failed to send activation notification to {user.email}: {e}")
575
        else:
576
            flash("You don't have permission to activate this user")
577
578
    # Redirect based on user role
579
    if current_user.is_global_admin:
580
        return redirect(url_for('main.global_admin_panel'))
581
    else:
582
        return redirect(url_for('main.manage_guests'))
583
584
585
@main.route('/deactivate_user/<id>')
586
@login_required
587
@admin_required
588
def deactivate_user(id):
589
    # deactivate a user account
590
    user = User.query.filter_by(id=int(id)).first()
591
592
    if user:
593
        # IMPORTANT: Global admins are always active and cannot be deactivated
594
        if user.is_global_admin:
595
            flash("Cannot deactivate a global admin. Global admins must always remain active.")
596
            if current_user.is_global_admin:
597
                return redirect(url_for('main.global_admin_panel'))
598
            else:
599
                return redirect(url_for('main.manage_guests'))
600
601
        # Global admins can deactivate any user, regular admins can only deactivate their guests
602
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
603
            user.is_active = False
604
            db.session.commit()
605
            flash(f"User {user.name} has been deactivated successfully")
606
        else:
607
            flash("You don't have permission to deactivate this user")
608
609
    # Redirect based on user role
610
    if current_user.is_global_admin:
611
        return redirect(url_for('main.global_admin_panel'))
612
    else:
613
        return redirect(url_for('main.manage_guests'))
614
615
616
@main.route('/create_user', methods=('GET', 'POST'))
617
@login_required
618
@admin_required
619
def create_user():
620
    # create a new user
621
    if request.method == 'POST':
622
        name = request.form['name']
623
        email = request.form['email']
624
        password = generate_password_hash(request.form['password'], method='scrypt')
625
626
        # Handle role assignment
627
        role = request.form.get('role', 'user')
628
629
        # Global admins can create any type of user
630
        if current_user.is_global_admin:
631
            if role == 'global_admin':
632
                admin = True
633
                is_global_admin = True
634
            elif role == 'admin':
635
                admin = True
636
                is_global_admin = False
637
            else:  # user
638
                admin = False
639
                is_global_admin = False
640
            account_owner_id = None
641
            # Users created by global admin are active by default
642
            is_active = True
643
        else:
644
            # Regular admins can only create guest users (non-admin users)
645
            admin = False
646
            is_global_admin = False
647
            account_owner_id = current_user.id
648
            # Guests created by regular admins are active by default
649
            is_active = True
650
651
        user = User(name=name, email=email, admin=admin, is_global_admin=is_global_admin,
652
                    is_active=is_active, password=password, account_owner_id=account_owner_id)
653
        existing = User.query.filter_by(email=email).first()
654
        if existing:
655
            flash("User already exists")
656
            if current_user.is_global_admin:
657
                return redirect(url_for('main.global_admin_panel'))
658
            else:
659
                return redirect(url_for('main.manage_guests'))
660
        db.session.add(user)
661
        db.session.commit()
662
        flash("Added Successfully")
663
664
        # Redirect based on user role
665
        if current_user.is_global_admin:
666
            return redirect(url_for('main.global_admin_panel'))
667
        else:
668
            return redirect(url_for('main.manage_guests'))
669
670
    # Redirect based on user role
671
    if current_user.is_global_admin:
672
        return redirect(url_for('main.global_admin_panel'))
673
    else:
674
        return redirect(url_for('main.manage_guests'))
675
676
677
@main.route('/export', methods=('GET', 'POST'))
678
@login_required
679
@admin_required
680
def export_csv():
681
    user_id = get_effective_user_id()
682
683
    csv_data = export(user_id)
684
685
    # Create a direct download response with the CSV data and appropriate headers
686
    response = Response(csv_data, content_type="text/csv")
687
    response.headers["Content-Disposition"] = "attachment; filename=schedule_export.csv"
688
689
    return response
690
691
692
@main.route('/import', methods=('GET', 'POST'))
693
@login_required
694
@admin_required
695
def import_csv():
696
    if request.method == 'POST':
697
        user_id = get_effective_user_id()
698
        csv_file = request.files.get('file')
699
700
        upload(csv_file, user_id)
701
702
        flash("Import Successful")
703
704
    return redirect(url_for('main.schedule'))
705
706
707
@main.route('/manage_guests')
708
@login_required
709
@account_owner_required
710
def manage_guests():
711
    """Account owners can manage their guest users"""
712
    guests = User.query.filter_by(account_owner_id=current_user.id).all()
713
    return render_template('manage_guests.html', guests=guests)
714
715
716
@main.route('/add_guest', methods=['POST'])
717
@login_required
718
@account_owner_required
719
def add_guest():
720
    """Create a guest user linked to current account owner"""
721
    email = request.form.get('email')
722
    name = request.form.get('name')
723
724
    # Check if user already exists
725
    existing_user = User.query.filter_by(email=email).first()
726
    if existing_user:
727
        flash('A user with this email already exists')
728
        return redirect(url_for('main.manage_guests'))
729
730
    # Generate a random password for guest (they can change it later)
731
    import secrets
732
    temp_password = secrets.token_urlsafe(16)
733
734
    new_guest = User(
735
        email=email,
736
        name=name,
737
        password=generate_password_hash(temp_password, method='scrypt'),
738
        admin=False,  # Guests are not admins
739
        is_global_admin=False,
740
        account_owner_id=current_user.id
741
    )
742
    db.session.add(new_guest)
743
    db.session.commit()
744
745
    flash(f'Guest user {name} added successfully. Temporary password: {temp_password}')
746
    return redirect(url_for('main.manage_guests'))
747
748
749
@main.route('/remove_guest/<int:guest_id>', methods=['POST'])
750
@login_required
751
@account_owner_required
752
def remove_guest(guest_id):
753
    """Remove a guest user (account owner only)"""
754
    guest = User.query.filter_by(id=int(guest_id), account_owner_id=current_user.id).first()
755
756
    if guest:
757
        db.session.delete(guest)
758
        db.session.commit()
759
        flash('Guest user removed successfully')
760
    else:
761
        flash('Guest user not found or you do not have permission to remove them')
762
763
    return redirect(url_for('main.manage_guests'))
764
765
766
@main.route('/global_admin')
767
@login_required
768
@global_admin_required
769
def global_admin_panel():
770
    """Global admin can see all users and accounts"""
771
    all_users = User.query.all()
772
773
    # Organize users by account owners
774
    account_owners = [u for u in all_users if u.account_owner_id is None and not u.is_global_admin]
775
    global_admins = [u for u in all_users if u.is_global_admin]
776
    standalone_users = [u for u in all_users if u.account_owner_id is None and not u.admin]
777
778
    return render_template('global_admin.html',
779
                         all_users=all_users,
780
                         account_owners=account_owners,
781
                         global_admins=global_admins,
782
                         standalone_users=standalone_users)
783
784
785
@main.route('/global_email_settings', methods=['GET', 'POST'])
786
@login_required
787
@global_admin_required
788
def global_email_settings():
789
    """Configure global email settings for all email notifications"""
790
    if request.method == 'POST':
791
        email = request.form.get('email')
792
        password = request.form.get('password')
793
        smtp_server = request.form.get('smtp_server')
794
795
        # Check if global settings already exist
796
        settings = GlobalEmailSettings.query.first()
797
798
        if settings:
799
            # Update existing settings
800
            settings.email = email
801
            settings.password = password
802
            settings.smtp_server = smtp_server
803
        else:
804
            # Create new settings
805
            settings = GlobalEmailSettings(
806
                id=1,  # Enforce single row
807
                email=email,
808
                password=password,
809
                smtp_server=smtp_server
810
            )
811
            db.session.add(settings)
812
813
        db.session.commit()
814
        flash('Global email settings saved successfully')
815
        return redirect(url_for('main.settings'))
816
817
    return redirect(url_for('main.settings'))
818
819
820
@main.route('/manifest.json')
821
def serve_manifest():
822
    return send_file('manifest.json', mimetype='application/manifest+json')
823
824
825
@main.route('/sw.js')
826
def serve_sw():
827
    return send_file('sw.js', mimetype='application/javascript')
828