[#23019867] First pass at allow for pledges to be modified
parent
97f3d01ab4
commit
46ca784030
|
@ -0,0 +1,99 @@
|
||||||
|
{% extends "basepledge.html" %}
|
||||||
|
|
||||||
|
{% block title %}Pledge (Modify){% endblock %}
|
||||||
|
|
||||||
|
{% block extra_extra_head %}
|
||||||
|
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block doccontent %}
|
||||||
|
<div style="height:10px";></div>
|
||||||
|
<div class="book-detail">
|
||||||
|
<div class="book-detail-img">
|
||||||
|
<a href="#"><img src="{{ work.cover_image_thumbnail }}" alt="{{ work.title }}" title="{{ work.title }}" width="131" height="192" /></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="book-detail-info">
|
||||||
|
<h2 class="book-name">{{ work.title }}</h2>
|
||||||
|
<h3 class="book-author">{{ work.author }}</h3>
|
||||||
|
<h3 class="book-year">{{ work.publication_date }}</h3>
|
||||||
|
|
||||||
|
<div class="find-book">
|
||||||
|
<label>Find it here</label>
|
||||||
|
|
||||||
|
<div class="find-link">
|
||||||
|
<a class="find-google" href="{{ work.googlebooks_url }}"><img src="/static/images/supporter_icons/googlebooks_square.png" title="Find on Google Books" alt="Find on Google Books" /></a>
|
||||||
|
<a rel="nofollow" class="find-openlibrary" href="{% url work_openlibrary work.id %}"><img src="/static/images/supporter_icons/openlibrary_square.png" title="Find on OpenLibrary" alt="Find on OpenLibrary"></a>
|
||||||
|
|
||||||
|
{% if not request.user.is_anonymous %}
|
||||||
|
{% if request.user.profile.goodreads_user_link %}
|
||||||
|
<a rel="nofollow" class="find-goodreads" href="{% url work_goodreads work.id %}"><img src="/static/images/supporter_icons/goodreads_square.png" title="Find on GoodReads" alt="Find on GoodReads"></a>
|
||||||
|
{% endif %}
|
||||||
|
{% if request.user.profile.librarything_id %}
|
||||||
|
<a rel="nofollow" class="find-librarything" href="{% url work_librarything work.id %}"><img src="/static/images/supporter_icons/librarything_square.png" title="Find on LibraryThing" alt="Find on LibraryThing" /></a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pledged-info">
|
||||||
|
<div class="pledged-group">
|
||||||
|
{{ work.last_campaign.supporters.count }} Ungluers have pledged ${{ work.last_campaign.current_total }}
|
||||||
|
</div>
|
||||||
|
<div class="status">
|
||||||
|
<img src="/static/images/images/icon-book-37by25-{{ work.percent_unglued }}.png" title="book list status" alt="book list status" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="jsmodule rounded pledge">
|
||||||
|
<div class="jsmod-content">
|
||||||
|
${{ work.last_campaign.target }} needed by<br />
|
||||||
|
{{ work.last_campaign.deadline }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="jsmodule rounded">
|
||||||
|
<div class="jsmod-content">
|
||||||
|
|
||||||
|
<p>You modifying your current pledge.</p>
|
||||||
|
{% comment %}
|
||||||
|
Even there is a CampaignPledgeForm in frontend/forms.py , the "widget" for premium_id is implemented in HTML here for now.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<form method="POST" action="{% url pledge_modify work_id=work.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
{{ form.preapproval_amount.errors }}
|
||||||
|
<div class="pledge_amount">{{ form.preapproval_amount.label_tag }}: ${{ form.preapproval_amount }}</div>
|
||||||
|
{{ form.anonymous.errors }}
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
|
not supported yet; don't display
|
||||||
|
{{ form.anonymous.label_tag }}: {{ form.anonymous }}
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<ul class="support menu">
|
||||||
|
{% for premium in premiums %}
|
||||||
|
<label for="{{premium.id}}">
|
||||||
|
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
|
||||||
|
<input type="radio" name="premium_id" value="{{premium.id}}" id="{{premium.id}}" {% ifequal request.REQUEST.premium_id premium.id|stringformat:"s" %}checked="checked"{% endifequal %}" />
|
||||||
|
<span class="menu-item-price">
|
||||||
|
${{ premium.amount }}
|
||||||
|
</span>
|
||||||
|
<span class="menu-item-desc">
|
||||||
|
{{ premium.description }}
|
||||||
|
</span>
|
||||||
|
</a></li></label>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<input type="submit" value="Modify Pledge" id="pledgesubmit"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ $j(document).ready(function(){
|
||||||
</div>
|
</div>
|
||||||
{% if status == 'ACTIVE' %}
|
{% if status == 'ACTIVE' %}
|
||||||
{% if pledged %}
|
{% if pledged %}
|
||||||
<div class="btn_support modify"><form action="/stub/modify_pledge" method="get"><input type="submit" value="Change Pledge"/></form></div>
|
<div class="btn_support modify"><form action="{% url pledge_modify work_id=work.id %}" method="get"><input type="submit" value="Change Pledge"/></form></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="btn_support"><form action="{% url pledge work_id=work.id %}" method="get"><input type="submit" value="Support"/></form></div>
|
<div class="btn_support"><form action="{% url pledge work_id=work.id %}" method="get"><input type="submit" value="Support"/></form></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.conf import settings
|
||||||
|
|
||||||
from regluit.core.feeds import SupporterWishlistFeed
|
from regluit.core.feeds import SupporterWishlistFeed
|
||||||
from regluit.core.models import Campaign
|
from regluit.core.models import Campaign
|
||||||
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeCancelView, FAQView
|
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, FAQView
|
||||||
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
|
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
|
@ -48,7 +48,8 @@ urlpatterns = patterns(
|
||||||
url(r"^setup/work/(?P<work_id>\d+)/$", "work", {'action':'setup_campaign'}, name="setup_campaign"),
|
url(r"^setup/work/(?P<work_id>\d+)/$", "work", {'action':'setup_campaign'}, name="setup_campaign"),
|
||||||
url(r"^pledge/(?P<work_id>\d+)/$", login_required(PledgeView.as_view()), name="pledge"),
|
url(r"^pledge/(?P<work_id>\d+)/$", login_required(PledgeView.as_view()), name="pledge"),
|
||||||
url(r"^pledge/cancel/$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
|
url(r"^pledge/cancel/$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
|
||||||
url(r"^pledge/complete/$", PledgeCompleteView.as_view(), name="pledge_complete"),
|
url(r"^pledge/complete/$", login_required(PledgeCompleteView.as_view()), name="pledge_complete"),
|
||||||
|
url(r"^pledge/modify/(?P<work_id>\d+)$", login_required(PledgeModifyView.as_view()), name="pledge_modify"),
|
||||||
url(r"^subjects/$", "subjects", name="subjects"),
|
url(r"^subjects/$", "subjects", name="subjects"),
|
||||||
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),
|
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),
|
||||||
url(r"^librarything/load/$","librarything_load", name="librarything_load"),
|
url(r"^librarything/load/$","librarything_load", name="librarything_load"),
|
||||||
|
|
|
@ -330,7 +330,7 @@ class PledgeView(FormView):
|
||||||
embedded = False
|
embedded = False
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
# change https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129
|
# change the default behavior from https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129
|
||||||
# don't automatically bind the data to the form on GET, only on POST
|
# don't automatically bind the data to the form on GET, only on POST
|
||||||
# compare with https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L34
|
# compare with https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L34
|
||||||
form_class = self.get_form_class()
|
form_class = self.get_form_class()
|
||||||
|
@ -417,21 +417,117 @@ class PledgeView(FormView):
|
||||||
logger.info("PledgeView paypal: Error " + str(t.reference))
|
logger.info("PledgeView paypal: Error " + str(t.reference))
|
||||||
return HttpResponse(response)
|
return HttpResponse(response)
|
||||||
|
|
||||||
|
class PledgeModifyView(FormView):
|
||||||
|
"""
|
||||||
|
A view to handle request to change an existing pledge
|
||||||
|
"""
|
||||||
|
template_name="pledge_modify.html"
|
||||||
|
form_class = CampaignPledgeForm
|
||||||
|
embedded = False
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
|
||||||
|
context = super(PledgeModifyView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
# the following should be true since PledgeModifyView.as_view is wrapped in login_required
|
||||||
|
assert self.request.user.is_authenticated()
|
||||||
|
user = self.request.user
|
||||||
|
|
||||||
|
work = get_object_or_404(models.Work, id=self.kwargs["work_id"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
campaign = work.last_campaign()
|
||||||
|
premiums = campaign.effective_premiums()
|
||||||
|
|
||||||
|
# which combination of campaign and transaction status required?
|
||||||
|
# Campaign must be ACTIVE
|
||||||
|
assert campaign.status == 'ACTIVE'
|
||||||
|
|
||||||
|
transactions = campaign.transactions().filter(user=user, status=IPN_PAY_STATUS_ACTIVE)
|
||||||
|
assert transactions.count() == 1
|
||||||
|
transaction = transactions[0]
|
||||||
|
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PAY_STATUS_ACTIVE
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# what stuff do we need to pull out to populate form?
|
||||||
|
# preapproval_amount, premium_id (which we don't have stored yet)
|
||||||
|
premium_id = None
|
||||||
|
|
||||||
|
# is there a Transaction for an ACTIVE campaign for this
|
||||||
|
# should make sure Transaction is modifiable.
|
||||||
|
|
||||||
|
preapproval_amount = transaction.amount
|
||||||
|
data = {'preapproval_amount':preapproval_amount, 'premium_id':premium_id}
|
||||||
|
|
||||||
|
form_class = self.get_form_class()
|
||||||
|
|
||||||
|
# no validation errors, please, when we're only doing a GET
|
||||||
|
# to avoid validation errors, don't bind the form
|
||||||
|
|
||||||
|
if preapproval_amount is not None:
|
||||||
|
form = form_class(data)
|
||||||
|
else:
|
||||||
|
form = form_class()
|
||||||
|
|
||||||
|
context.update({'work':work,'campaign':campaign, 'premiums':premiums, 'form':form, 'premium_id':premium_id, 'faqmenu': 'pledge'})
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
|
||||||
|
# What are the situations we need to deal with?
|
||||||
|
# 2 main situations: if the new amount is less than max_amount, no need to go out to PayPal again
|
||||||
|
# if new amount is greater than max_amount...need to go out and get new approval.
|
||||||
|
# to start with, we can use the standard pledge_complete, pledge_cancel machinery
|
||||||
|
# might have to modify the pledge_complete, pledge_cancel because the messages are going to be
|
||||||
|
# different because we're modifying a pledge rather than a new one.
|
||||||
|
|
||||||
|
work_id = self.kwargs["work_id"]
|
||||||
|
preapproval_amount = form.cleaned_data["preapproval_amount"]
|
||||||
|
anonymous = form.cleaned_data["anonymous"]
|
||||||
|
|
||||||
|
assert self.request.user.is_authenticated()
|
||||||
|
user = self.request.user
|
||||||
|
|
||||||
|
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
|
||||||
|
campaign = models.Work.objects.get(id=int(work_id)).last_campaign()
|
||||||
|
assert campaign.status == 'ACTIVE'
|
||||||
|
|
||||||
|
transactions = campaign.transactions().filter(user=user, status=IPN_PAY_STATUS_ACTIVE)
|
||||||
|
assert transactions.count() == 1
|
||||||
|
transaction = transactions[0]
|
||||||
|
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PAY_STATUS_ACTIVE
|
||||||
|
|
||||||
|
p = PaymentManager(embedded=self.embedded)
|
||||||
|
status, url = p.modify_transaction(transaction, preapproval_amount)
|
||||||
|
|
||||||
|
logger.info("status: {0}, url:{1}".format(status, url))
|
||||||
|
|
||||||
|
if status and url is not None:
|
||||||
|
logger.info("PledgeModifyView paypal: " + url)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
elif status and url is None:
|
||||||
|
return HttpResponse("Your modification is noted. No need to go to PayPal")
|
||||||
|
else:
|
||||||
|
return HttpResponse("No mods made")
|
||||||
|
|
||||||
|
|
||||||
class PledgeCompleteView(TemplateView):
|
class PledgeCompleteView(TemplateView):
|
||||||
"""A callback for PayPal to tell unglue.it that a payment transaction has completed successfully.
|
"""A callback for PayPal to tell unglue.it that a payment transaction has completed successfully.
|
||||||
|
|
||||||
Possible things to implement:
|
Possible things to implement:
|
||||||
|
|
||||||
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
|
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
|
||||||
study other confirmation emails for their contents
|
study other confirmation emails for their contents
|
||||||
after pledging, supporters are returned to a thank-you screen
|
after pledging, supporters are returned to a thank-you screen
|
||||||
should have prominent "thank you" or "congratulations" message
|
should have prominent "thank you" or "congratulations" message
|
||||||
should have prominent share options
|
should have prominent share options
|
||||||
should suggest other works for supporters to explore (on what basis?)
|
should suggest other works for supporters to explore (on what basis?)
|
||||||
link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?)
|
link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?)
|
||||||
should note that a confirmation email has been sent to $email from $sender
|
should note that a confirmation email has been sent to $email from $sender
|
||||||
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
|
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template_name="pledge_complete.html"
|
template_name="pledge_complete.html"
|
||||||
|
|
|
@ -714,7 +714,7 @@ class PaymentManager( object ):
|
||||||
|
|
||||||
if amount > transaction.max_amount or expiry != transaction.date_expired:
|
if amount > transaction.max_amount or expiry != transaction.date_expired:
|
||||||
|
|
||||||
# Increase or expiuration change, cancel and start again
|
# Increase or expiration change, cancel and start again
|
||||||
self.cancel_transaction(transaction)
|
self.cancel_transaction(transaction)
|
||||||
|
|
||||||
# Start a new authorization for the new amount
|
# Start a new authorization for the new amount
|
||||||
|
|
Loading…
Reference in New Issue