app.logout();app.login('admina@example.test','Tenant test password 123!')
_,body,_=app.request(url(a,member))
check(b'data-club-step="member"'inbodyandb'data-club-step="charge"'inbody,'Club administrator gets member edit and dues dialogs')
check(save('charge',a,member,csrf_token='invalid',season_serial=season,amount='100.00')[0]==403,'Member summary writes require a valid CSRF token')
check(save('charge',a,member,membership_serial=neighbor,season_serial=season,amount='100.00')[0]==409,'Posted membership cannot differ from the selected summary')
check(status==200andresult['ok']and'Roster.memberSummary'inresult['redirect']and'membership_serial='+str(member)inresult['redirect'],'Dues save returns a same-member summary redirect')
check(save('charge',a,member,season_serial=season,amount='100.00')[0]==409,'Duplicate season dues remain rejected')
check(save('charge',a,member,season_serial=foreignseason,amount='10.00')[0]==409,'Dues cannot use another club season')
check(save('payment',a,member,due_serial=neighbor_due,amount='1.00',paid_on='2026-01-01',request_key=secrets.token_hex(32))[0]==404,'Payment cannot target a different member in the same club')
check(save('payment',a,member,due_serial=foreign_due,amount='1.00',paid_on='2026-01-01',request_key=secrets.token_hex(32))[0]==404,'Payment cannot target another club dues')
key=secrets.token_hex(32)
check(save('payment',a,member,due_serial=due,amount='25.00',paid_on='2026-01-01',request_key=key,payment_method='cash',payment_reference='Member summary receipt')[0]==200,'Payment is recorded for the selected membership')
check(status==200andnext(mforminapp.fixture('inspect')['club_memberships']ifm['membership_serial']==member)['joined_on']=='2026-02-01','Membership information saves from the summary')
check(save('member',a,member,membership_status='active',membership_role='administrator',joined_on='2026-02-01')[0]==403,'Club administrator cannot promote members to administrator')
check(save('member',a,member,membership_status='active',membership_role='member',joined_on='2026-03-01')[0]==403,'Membership edits require recent MFA on the new endpoint')
check(save('adjust',a,member,due_serial=neighbor_due,corrected_amount='80.00',reason='Wrong member',request_key=secrets.token_hex(32))[0]==404,'Correction rejects another member dues in the same club')
check(save('adjust',a,member,due_serial=foreign_due,corrected_amount='80.00',reason='Wrong club',request_key=secrets.token_hex(32))[0]==404,'Correction rejects another club dues')
check(save('adjust',a,member,due_serial=due,corrected_amount='24.00',reason='Below paid',request_key=secrets.token_hex(32))[0]==409,'Correction cannot reduce dues below received payments')
adjustment_key=secrets.token_hex(32)
status,result=save('adjust',a,member,due_serial=due,corrected_amount='80.00',reason='Corrected season rate',request_key=adjustment_key)
check(status==200and'Roster.memberSummary'inresult['redirect'],'Correction saves and returns to the selected member summary')
check(save('adjust',a,member,due_serial=due,corrected_amount='80.00',reason='Corrected season rate',request_key=adjustment_key)[0]==200andlen(app.fixture('inspect')['club_due_adjustments'])==1,'Correction retries create only one adjustment')
check(save('reverse',a,neighbor,payment_serial=payment,reason='Wrong member',request_key=secrets.token_hex(32))[0]==404,'Reversal rejects another member payment in the same club')
check(failed==500andlen(app.fixture('inspect')['club_payment_reversals'])==0,'Reversal rolls back completely when mandatory audit fails')
reversal_key=secrets.token_hex(32)
check(save('reverse',a,member,payment_serial=payment,reason='Wrong receipt',request_key=reversal_key)[0]==200,'Payment reverses from the member summary')
check(save('reverse',a,member,payment_serial=payment,reason='Wrong receipt',request_key=reversal_key)[0]==200andlen(app.fixture('inspect')['club_payment_reversals'])==1,'Reversal retries create only one reversal')
check(save('reverse',a,member,payment_serial=payment,reason='Repeat',request_key=secrets.token_hex(32))[0]==409,'Already reversed payment cannot be reversed again')
_,summary,_=app.request(url(a,member));check(b'$80.00'insummaryandb'$0.00'insummaryandb'Wrong receipt'insummaryandb'Corrected season rate'insummary,'Summary reflects corrected dues, reversed payment and preserved reasons')
app.logout();app.login('membera@example.test','Tenant test password 123!')
_,body,_=app.request(url(a,member));check(b'club-action-modal'notinbody,'Ordinary member receives no action dialogs')
check(save('payment',a,member,due_serial=due,amount='1.00',paid_on='2026-01-01',request_key=secrets.token_hex(32))[0]==403,'Ordinary member cannot forge a summary payment request')
check(save('charge',a,member,season_serial=season,amount='1.00')[0]==403,'Ordinary member cannot forge a dues request')
check(save('adjust',a,member,due_serial=due,corrected_amount='50.00',reason='Forged',request_key=secrets.token_hex(32))[0]==403,'Ordinary member cannot forge a dues correction')
check(save('reverse',a,member,payment_serial=payment,reason='Forged',request_key=secrets.token_hex(32))[0]==403,'Ordinary member cannot forge a payment reversal')
app.logout();app.login('adminb@example.test','Tenant test password 123!')
check(save('member',a,member,membership_status='active',membership_role='member',joined_on='2026-03-01')[0]==403,'Other club administrator cannot modify the membership')