Private
Public Access
1
0

Merge branch 'feature/eol_2026' into develop

This commit is contained in:
2025-12-08 18:48:20 +01:00
14 changed files with 84 additions and 104 deletions

View File

@@ -418,35 +418,72 @@ def create_subscription(rower, data):
return False, 0 # pragma: no cover
def cancel_subscription(rower, id):
themessages = []
errormessages = []
# 1. Fetch subscription first so we can read paid_through_date
try:
_ = gateway.subscription.cancel(id)
themessages.append("Subscription canceled")
except: # pragma: no cover
subscription = gateway.subscription.find(id)
paid_through = subscription.paid_through_date # may be None
except Exception:
errormessages.append(
"We could not find the subscription record in our customer database."
" We have notified the site owner, who will contact you.")
" We have notified the site owner, who will contact you."
)
name = '{f} {l}'.format(f=rower.user.first_name,
l=rower.user.last_name)
name = f"{rower.user.first_name} {rower.user.last_name}"
_ = myqueue(queuehigh,
handle_send_email_failed_cancel,
name, rower.user.email, rower.user.username, id)
_ = myqueue(
queuehigh,
handle_send_email_failed_cancel,
name,
rower.user.email,
rower.user.username,
id,
)
return False, themessages, errormessages
# 2. Attempt cancellation
try:
_ = gateway.subscription.cancel(id)
themessages.append("Subscription canceled")
except Exception: # pragma: no cover
errormessages.append(
"We could not find the subscription record in our customer database."
" We have notified the site owner, who will contact you."
)
name = f"{rower.user.first_name} {rower.user.last_name}"
_ = myqueue(
queuehigh,
handle_send_email_failed_cancel,
name,
rower.user.email,
rower.user.username,
id,
)
return False, themessages, errormessages
# 3. Update rower object (using paid_through_date)
basicplans = PaidPlan.objects.filter(price=0, paymentprocessor='braintree')
rower.paidplan = basicplans[0]
# teamplanexpires stays "now" (as you had it)
rower.teamplanexpires = timezone.now()
rower.planexpires = timezone.now()
# planexpires becomes the user's real, fully paid end date
# fallback = now() if Braintree somehow returns None
rower.planexpires = paid_through or timezone.now()
rower.clubsize = 0
rower.rowerplan = 'basic'
rower.subscription_id = None
rower.save()
themessages.append("Your plan was reset to basic")
return True, themessages, errormessages

View File

@@ -93,15 +93,15 @@ class Command(BaseCommand):
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
dologging('processemail.log', ''.join('!! ' + line for line in lines))
rowers = Rower.objects.filter(rp3_auto_import=True)
for r in rowers: # pragma: no cover
try:
rp3_integration = RP3Integration(r.user)
_ = rp3_integration.get_workouts()
except: # pragma: no cover
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
dologging('processemail.log', ''.join('!! ' + line for line in lines))
#rowers = Rower.objects.filter(rp3_auto_import=True)
#for r in rowers: # pragma: no cover
# try:
# rp3_integration = RP3Integration(r.user)
# _ = rp3_integration.get_workouts()
# except: # pragma: no cover
# exc_type, exc_value, exc_traceback = sys.exc_info()
# lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
# dologging('processemail.log', ''.join('!! ' + line for line in lines))
rowers = Rower.objects.filter(nk_auto_import=True)
for r in rowers: # pragma: no cover

View File

@@ -44,14 +44,7 @@
{% if user.is_anonymous %}
<li class="grid_2">
</li>
<li style="text-align: center;">
<a class="button midden" href="/rowers/register">
<h2 class="midden rounder whiteborder">
<div>SIGN UP FREE</div>
</h1>
</a>
</li>
<li class="grid_2">
<li class="grid_2">
</li>
<li class="grid_2">
</li>

View File

@@ -209,13 +209,6 @@
</div>
{% if not user.is_authenticated %}
<div class="register">
<h1>SIGN UP</h1>
<p><a href="/rowers/register/">I am a rower</a></p>
<p><a href="/rowers/coachregister/">I am a rowing coach</a></p>
</div>
{% endif %}
<div class="bgimg-1">
<div class="caption">
@@ -353,9 +346,6 @@ using my NK empower oarlock for about a month now, and Im really
<li>
<a href="/rowers/email/">Contact</a>
</li>
<li>
<a href="/rowers/paidplans/">Paid Plans</a>
</li>
<li>
<a href="/rowers/legal/">Legal</a>
</li>

View File

@@ -557,6 +557,7 @@ class gatewayresult(): # pragma: no cover
self.transaction = vtransaction()
self.payment_method = vpayment_method()
self.subscription = vsubscription()
self.paid_through_date = datetime.datetime.now()+datetime.timedelta(days=365)
self.customer = kwargs.pop('customer',customer())
def __unicode__():
@@ -615,6 +616,7 @@ class transaction(): # pragma: no cover
'id': 12
}
self.created_at = datetime.datetime.now()
self.paid_through_date = datetime.datetime.now()+datetime.timedelta(days=365)
self.currency_iso_code = 'EUR'
class vtransaction(): # pragma: no cover
@@ -627,6 +629,7 @@ class vtransaction(): # pragma: no cover
'id': 12
}
self.created_at = datetime.datetime.now()
self.paid_through_date = datetime.datetime.now()+datetime.timedelta(days=365)
self.currency_iso_code = 'EUR'
class vsubscription(): # pragma: no cover
@@ -636,9 +639,13 @@ class vsubscription(): # pragma: no cover
def cancel(*args, **kwargs):
return gatewayresult(is_success=True)
def find(*args, **kwargs):
return subscription()
def __init__(self, *args, **kwargs):
self.id = '121'
self.billing_period_end_date = (datetime.datetime.now()+datetime.timedelta(days=365)).date()
self.paid_through_date = (datetime.datetime.now()+datetime.timedelta(days=365)).date()
self.status = 'Active'
self.plan_id = 12
self.price = 15
@@ -654,9 +661,13 @@ class subscription(): # pragma: no cover
def cancel(*args, **kwargs):
return gatewayresult(is_success=True)
def find(*args, **kwargs):
return subscription()
def __init__(self, *args, **kwargs):
self.id = '121'
self.billing_period_end_date = (datetime.datetime.now()+datetime.timedelta(days=365)).date()
self.paid_through_date = (datetime.datetime.now()+datetime.timedelta(days=365)).date()
self.transactions = [vtransaction()]
self.status = 'Active'
self.plan_id = 12

View File

@@ -79,7 +79,8 @@ class BraintreeUnits(TestCase):
@patch('rowers.idoklad.requests.post',side_effect=mocked_requests)
@patch('rowers.braintreestuff.gateway', side_effect=MockBraintreeGateway)
@patch('rowers.braintreestuff.myqueue')
def test_process_webhook(self,mock_token, mock_get,mockpost,mocked_gateway,mocked_myqueue):
@patch('rowers.braintreestuff.cancel_subscription', side_effect=mock_cancel_subscription)
def test_process_webhook(self,mock_token, mock_get,mockpost,mocked_gateway,mocked_myqueue,mock_cancel_subscription):
n = notification()
res = process_webhook(n)
self.assertEqual(res,1)

View File

@@ -880,8 +880,6 @@ class PermissionsViewTests(TestCase):
self.assertTrue(form.is_valid())
expected_url = reverse('paidplans_view')
response = self.c.post(url,form_data,follow=True)
# check that it does not redirect and status code is 200

View File

@@ -77,7 +77,6 @@ class URLTests(TestCase):
'/rowers/404/',
'/rowers/500/',
'/rowers/502/',
'/rowers/about/',
'/rowers/workout/addmanual/',
'/rowers/ajax_agegroup/45/hwt/male/1/',
'/rowers/analysis/',
@@ -119,8 +118,6 @@ class URLTests(TestCase):
'/rowers/me/workflowdefault/',
'/rowers/partners/',
'/rowers/physics/',
'/rowers/register/',
'/rowers/register/thankyou/',
'/rowers/sessions/',
'/rowers/sessions/coach/',
'/rowers/sessions/create/',

View File

@@ -137,23 +137,7 @@
178,225,sendmail,feedback form,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE,
181,233,errormessage_view,not used,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
182,237,payment_confirm_view,confirm payment,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
183,238,upgrade_confirm_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
184,240,downgrade_confirm_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
185,241,billing_view,confirm billing,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
186,242,upgrade_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
187,243,downgrade_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
188,244,payment_completed_view,payment completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
189,245,downgrade_completed_view,downgrade completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
191,247,plan_stop_view,stop plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
192,248,plan_tobasic_view,reset to basic,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
193,249,checkouts_view,checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
194,250,upgrade_checkouts_view,upgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
195,251,downgrade_checkouts_view,downgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
197,253,start_trial_view,payments,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
198,254,start_plantrial_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
201,259,workout_workflow_view,workout workflow vieq,TRUE,302,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE,
202,260,workout_flexchart3_view,flex chart,TRUE,302,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE,
203,264,rower_process_testcallback,test callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
1 id view function anonymous anonymous_response own own_response own_nonperm member member_response member_nonperm coachee coachee_response coachee_nonperm is_staff userid workoutid dotest realtest kwargs
137 178 225 sendmail feedback form TRUE 200 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
138 180 232 laboratory_view lab TRUE 302 basic 200 302 basic 403 403 coach 200 403 FALSE TRUE FALSE TRUE TRUE
139 181 233 errormessage_view not used TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
182 237 payment_confirm_view confirm payment TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
183 238 upgrade_confirm_view confirm upgrade TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
184 240 downgrade_confirm_view confirm downgrade TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
185 241 billing_view confirm billing TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
186 242 upgrade_view confirm upgrade TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
187 243 downgrade_view confirm downgrade TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
188 244 payment_completed_view payment completed TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
189 245 downgrade_completed_view downgrade completed TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
190 246 paidplans_view paid plans TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE TRUE TRUE
191 247 plan_stop_view stop plan TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
140 192 248 plan_tobasic_view reset to basic TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
193 249 checkouts_view checkout TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
194 250 upgrade_checkouts_view upgrade checkout TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
195 251 downgrade_checkouts_view downgrade checkout TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
197 253 start_trial_view payments TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
198 254 start_plantrial_view paid plans TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
199 256 rower_register_view register rower TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE TRUE TRUE
141 201 259 workout_workflow_view workout workflow vieq TRUE 302 basic 200 200 basic 200 200 coach 200 200 FALSE FALSE TRUE TRUE TRUE
142 202 260 workout_flexchart3_view flex chart TRUE 302 basic 200 403 basic 200 200 coach 200 200 FALSE FALSE TRUE TRUE TRUE
143 203 264 rower_process_testcallback test callback TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE

View File

@@ -832,7 +832,6 @@ urlpatterns = [
name='payment_completed_view'),
re_path(r'^downgradecompleted/$', views.downgrade_completed_view,
name='downgrade_completed_view'),
re_path(r'^paidplans/$', views.paidplans_view, name='paidplans_view'),
re_path(r'^me/cancelsubscriptions/$',
views.plan_stop_view, name='plan_stop_view'),
re_path(r'^me/cancelsubscription/(?P<id>[\w\ ]+.*)/$',
@@ -847,10 +846,6 @@ urlpatterns = [
re_path(r'^starttrial/$', views.start_trial_view, name='start_trial_view'),
re_path(r'^legal', TemplateView.as_view(
template_name='legal.html'), name='legal'),
re_path(r'^register/$', views.rower_register_view,
name='rower_register_view'),
re_path(r'^coachregister/$', views.coach_register_view,
name='coach_register_view'),
path('activate/<uidb64>/<token>/', views.useractivate, name='useractivate'),
re_path(r'^register/thankyou/$', TemplateView.as_view(
template_name='registerthankyou.html'), name='registerthankyou'),

View File

@@ -165,11 +165,6 @@
</a>
</li>
{% else %}
<li>
<a href="/rowers/register/" title="Sign Up">
<i class="fas fa-user-plus "></i>
</a>
</li>
<li>
<a href="{% url 'login' %}" title="Sign In">
<i class="fas fa-sign-in-alt "></i>
@@ -225,12 +220,18 @@
</p>
</li>
{% endif %}
<li class="grid_4">
<p class="message">
Rowsandall functionality will be transferred to <a href="https://intervals.icu/">Intervals.icu</a> during 2026.
For more details, read <a href="https://analytics.rowsandall.com/2025/12/07/the-last-year-rowsandall-will-disappear-what-now/">this announcement</a>.
</p>
</li>
{% if user.rower.planexpires and user.rower|notfree and user.rower.paymenttype == 'single'%}
{% if user.rower.planexpires|is_future_date %}
{% if user.rower.planexpires|date_dif|ddays < 4 %}
<li class="grid_4">
<p class="successmessage">
You have {{ user.rower.planexpires|date_dif|ddays }} days left of your one year subscription. Please renew on or before {{ user.rower.planexpires }} or your plan will be reset to Basic. Click <a href="/rowers/paidplans/">here</a> to renew your membership.</p>
You have {{ user.rower.planexpires|date_dif|ddays }} days left of your one year subscription. Please renew on or before {{ user.rower.planexpires }} or your plan will be reset to Basic.</p>
</li>
{% endif %}
{% endif %}
@@ -239,14 +240,14 @@
{% if user.rower.plantrialexpires and user.rower.plantrialexpires|is_future_date and user.rower.rowerplan != 'plan' %}
<li class="grid_4">
<p class="successmessage">
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to <a href="/rowers/paidplans/">upgrade now?</a>
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial </a>
</p>
</li>
{% else %}
{% if user.rower.rowerplan == 'basic' %}
<li class="grid_4">
<p class="successmessage">
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to <a href="/rowers/paidplans/">upgrade now?</a>
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial </a>
</p>
</li>
{% endif %}
@@ -255,7 +256,7 @@
{% if user.rower.coachtrialexpires and user.rower.coachtrialexpires|is_future_date and user.rower.rowerplan != 'coach' %}
<li class="grid_4">
<p class="successmessage">
{{ user.rower.coachtrialexpires|date_dif|ddays }} days left of your Coach trial - Would you like to <a href="/rowers/paidplans/">upgrade now?</a>
{{ user.rower.coachtrialexpires|date_dif|ddays }} days left of your Coach trial </a>
</p>
</li>
{% endif %}
@@ -336,13 +337,7 @@
</div>
<div id="id_blogs">
</div>
{% if user.is_authenticated and user.rower.rowerplan == 'basic' %}
<div class="site-announcement-box">
<div class="site-announcement-white">
<a href="/rowers/paidplans/">Support us and get more features!</a>
</div>
</div>
{% elif user.is_authenticated and user.rower.rowerplan == 'pro' %}
{% if user.is_authenticated and user.rower.rowerplan == 'pro' %}
<div class="site-announcement-box">
<div class="site-announcement-white">
<i class="fa-solid fa-user-astronaut "></i> Thank you for supporting Rowsandall.com!
@@ -409,14 +404,6 @@
</li>
</ul>
</li>
<li>
<h2>Support Us</h2>
<ul>
<li>
<a href="/rowers/paidplans/">Support Us</a>
</li>
</ul>
</li>
<li>
<h2>Legal</h2>
<ul>

View File

@@ -170,11 +170,6 @@
</a>
</li>
{% else %}
<li>
<a href="/rowers/register/" title="Sign Up">
<i class="fas fa-user-plus "></i>
</a>
</li>
<li>
<a href="{% url 'login' %}" title="Sign In">
<i class="fas fa-sign-in-alt "></i>
@@ -236,14 +231,14 @@
{% if user.rower.plantrialexpires and user.rower.rowerplan != 'plan' %}
<li class="grid_5">
<p class="successmessage">
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to <a href="/rowers/paidplans/">upgrade now?</a>
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial </a>
</p>
</li>
{% else %}
{% if user.rower.rowerplan == 'basic' %}
<li class="grid_5">
<p class="successmessage">
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to <a href="/rowers/paidplans/">upgrade now?</a>
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial </a>
</p>
</li>
{% endif %}
@@ -329,14 +324,6 @@
</li>
</ul>
</li>
<li>
<h1>Paid Plans</h1>
<ul>
<li>
<a href="/rowers/paidplans/">Paid Plans</a>
</li>
</ul>
</li>
<li>
<h1>Legal</h1>
<ul>