Merge branch 'feature/eol_2026' into develop
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 I‘m 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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/',
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
@@ -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'),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user