prettify refund page (bug 704961, bug 705871)
This commit is contained in:
Родитель
68d6c10475
Коммит
a02cb78d3e
|
@ -1,36 +1,49 @@
|
|||
{% extends "devhub/base.html" %}
|
||||
{% extends 'devhub/base_impala.html' %}
|
||||
|
||||
{% block title %}{{ dev_page_title(_('Issue Refund')) }}{% endblock %}
|
||||
{% set title = _('Issue Refund') %}
|
||||
{% block title %}{{ dev_page_title(title) }}{% endblock %}
|
||||
|
||||
{# TODO(apps): Finalize copy. #}
|
||||
|
||||
{% block content %}
|
||||
<header>
|
||||
<h1>{{ _('Issue Refund') }}</h1>
|
||||
{{ dev_breadcrumbs(addon) }}
|
||||
<h1>{{ title }}</h1>
|
||||
</header>
|
||||
<form method="post" id="issue-refund" class="item" action="">
|
||||
{% if transaction_id %}
|
||||
{{ csrf() }}
|
||||
<p>
|
||||
{% trans %}
|
||||
A refund was requested by {{ user }} for {{ addon_name }}.
|
||||
{% endtrans %}
|
||||
</p><p>
|
||||
{% trans %}
|
||||
Price: {{ price }}
|
||||
{% endtrans %}
|
||||
</p><p>
|
||||
{% trans %}
|
||||
Purchase date: {{ purchase_date }}
|
||||
{% endtrans %}
|
||||
</p><p>
|
||||
<button type="submit" name="issue">
|
||||
{{ _('Issue Refund') }}
|
||||
</button>
|
||||
<button type="submit" name="decline">
|
||||
{{ _('Decline Refund') }}
|
||||
</button>
|
||||
<input type="hidden" name="transaction_id" value="{{ transaction_id }}">
|
||||
{% else %}
|
||||
<p>{{ _('No refundable transaction found.') }}</p>
|
||||
{% endif %}
|
||||
<form method="post" action="" id="issue-refund" class="primary island full c">
|
||||
{% if transaction_id %}
|
||||
{{ csrf() }}
|
||||
<p>
|
||||
{% with user=contribution.user.display_name,
|
||||
user_url=contribution.user.get_url_path(),
|
||||
addon_url=addon.get_url_path(),
|
||||
addon_name=addon.name %}
|
||||
A refund was requested by
|
||||
<a href="{{ user_url }}" target="_blank">{{ user }}</a> for
|
||||
<a href="{{ addon_url }}" target="_blank">{{ addon_name }}</a>.
|
||||
{% endwith %}
|
||||
</p>
|
||||
<p>
|
||||
{% with price=contribution.get_amount_locale() %}
|
||||
<b>Price:</b> {{ price }}
|
||||
{% endwith %}
|
||||
</p>
|
||||
<p>
|
||||
{% with purchase_date=contribution.created|datetime %}
|
||||
<b>Purchase date:</b> {{ purchase_date }}
|
||||
{% endwith %}
|
||||
</p>
|
||||
<p>
|
||||
<button type="submit" class="good" name="issue">
|
||||
{{ _('Issue Refund') }}
|
||||
</button>
|
||||
<button type="submit" class="bad" name="decline">
|
||||
{{ _('Decline Refund') }}
|
||||
</button>
|
||||
<input type="hidden" name="transaction_id" value="{{ transaction_id }}">
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{{ loc('No refundable transaction found.') }}</p>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock content %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1260,60 +1260,71 @@ class TestIssueRefund(amo.tests.TestCase):
|
|||
self.paykey = u'fake-paykey'
|
||||
self.client.login(username='del@icio.us', password='password')
|
||||
self.user = UserProfile.objects.get(username='clouserw')
|
||||
self.url = reverse('devhub.issue_refund', args=[self.addon.slug])
|
||||
self.url = self.addon.get_dev_url('issue_refund')
|
||||
|
||||
def makePurchase(self, uuid='123456', type=amo.CONTRIB_PURCHASE):
|
||||
def make_purchase(self, uuid='123456', type=amo.CONTRIB_PURCHASE):
|
||||
return Contribution.objects.create(uuid=uuid, addon=self.addon,
|
||||
transaction_id=self.transaction_id,
|
||||
user=self.user, paykey=self.paykey,
|
||||
amount=Decimal('10'), type=type)
|
||||
|
||||
def test_request_issue(self):
|
||||
c = self.makePurchase()
|
||||
r = self.client.get(self.url,
|
||||
data={'transaction_id': c.transaction_id})
|
||||
c = self.make_purchase()
|
||||
r = self.client.get(self.url, {'transaction_id': c.transaction_id})
|
||||
doc = pq(r.content)
|
||||
eq_(doc('#issue-refund button')[0].text.strip(), 'Issue Refund')
|
||||
eq_(doc('#issue-refund button')[1].text.strip(), 'Decline Refund')
|
||||
eq_(doc('#issue-refund button').length, 2)
|
||||
eq_(doc('#issue-refund input[name=transaction_id]').val(),
|
||||
self.transaction_id)
|
||||
|
||||
def test_nonexistent_txn(self):
|
||||
r = self.client.get(self.url, data={'transaction_id': 'none'})
|
||||
r = self.client.get(self.url, {'transaction_id': 'none'})
|
||||
eq_(r.status_code, 404)
|
||||
|
||||
def test_nonexistent_txn_no_really(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 404)
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_issue(self, refund):
|
||||
c = self.makePurchase()
|
||||
r = self.client.post(self.url,
|
||||
data={'transaction_id': c.transaction_id,
|
||||
'issue': '1'})
|
||||
eq_(r.status_code, 302)
|
||||
def _test_issue(self, refund, destination):
|
||||
c = self.make_purchase()
|
||||
r = self.client.post(self.url, {'transaction_id': c.transaction_id,
|
||||
'issue': '1'})
|
||||
self.assertRedirects(r, reverse(destination), 302)
|
||||
refund.assert_called_with(self.transaction_id, self.paykey)
|
||||
eq_(len(mail.outbox), 1)
|
||||
assert 'approved' in mail.outbox[0].subject
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_decline(self, refund):
|
||||
c = self.makePurchase()
|
||||
r = self.client.post(self.url,
|
||||
data={'transaction_id': c.transaction_id,
|
||||
'decline': ''})
|
||||
eq_(r.status_code, 302)
|
||||
def test_addons_issue(self, refund):
|
||||
self._test_issue(refund, 'devhub.addons')
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_apps_issue(self, refund):
|
||||
self.addon.update(type=amo.ADDON_WEBAPP)
|
||||
self._test_issue(refund, 'devhub.apps')
|
||||
|
||||
def _test_decline(self, refund, destination):
|
||||
c = self.make_purchase()
|
||||
r = self.client.post(self.url, {'transaction_id': c.transaction_id,
|
||||
'decline': ''})
|
||||
self.assertRedirects(r, reverse(destination), 302)
|
||||
assert not refund.called
|
||||
eq_(len(mail.outbox), 1)
|
||||
assert 'declined' in mail.outbox[0].subject
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_addons_decline(self, refund):
|
||||
self._test_decline(refund, 'devhub.addons')
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_apps_decline(self, refund):
|
||||
self.addon.update(type=amo.ADDON_WEBAPP)
|
||||
self._test_decline(refund, 'devhub.apps')
|
||||
|
||||
@mock.patch('paypal.refund')
|
||||
def test_non_refundable_txn(self, refund):
|
||||
c = self.makePurchase('56789', amo.CONTRIB_VOLUNTARY)
|
||||
r = self.client.post(self.url,
|
||||
data={'transaction_id': c.transaction_id,
|
||||
'issue': ''})
|
||||
c = self.make_purchase('56789', amo.CONTRIB_VOLUNTARY)
|
||||
r = self.client.post(self.url, {'transaction_id': c.transaction_id,
|
||||
'issue': ''})
|
||||
eq_(r.status_code, 404)
|
||||
assert not refund.called
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ app_detail_patterns = patterns('',
|
|||
url('^profile$', views.profile, name='devhub.apps.profile'),
|
||||
url('^profile/remove$', views.remove_profile,
|
||||
name='devhub.apps.profile.remove'),
|
||||
url('^issue_refund$', views.issue_refund, name='devhub.apps.issue_refund'),
|
||||
)
|
||||
|
||||
# These will all start with /addon/<addon_id>/
|
||||
|
@ -81,7 +82,8 @@ detail_patterns = patterns('',
|
|||
url('^payments/permission/refund$', views.acquire_refund_permission,
|
||||
name='devhub.addons.acquire_refund_permission'),
|
||||
url('^payments/', include(marketplace_patterns('addons'))),
|
||||
url('^issue_refund$', views.issue_refund, name='devhub.issue_refund'),
|
||||
url('^issue_refund$', views.issue_refund,
|
||||
name='devhub.addons.issue_refund'),
|
||||
url('^profile$', views.profile, name='devhub.addons.profile'),
|
||||
url('^profile/remove$', views.remove_profile,
|
||||
name='devhub.addons.profile.remove'),
|
||||
|
|
|
@ -492,23 +492,19 @@ def issue_refund(request, addon_id, addon, webapp=False):
|
|||
if 'issue' in request.POST:
|
||||
paypal.refund(txn_id, contribution.paykey)
|
||||
contribution.mail_approved()
|
||||
paypal_log.error('Refund issued for transaction %r' % (txn_id,))
|
||||
paypal_log.error('Refund issued for transaction %r' % txn_id)
|
||||
messages.success(request, 'Refund issued.')
|
||||
return redirect('devhub.addons')
|
||||
else:
|
||||
contribution.mail_declined()
|
||||
paypal_log.error('Refund declined for transaction %r' % (txn_id,))
|
||||
paypal_log.error('Refund declined for transaction %r' % txn_id)
|
||||
messages.success(request, 'Refund declined.')
|
||||
return redirect('devhub.addons')
|
||||
return redirect('devhub.%s' % ('apps' if webapp else 'addons'))
|
||||
else:
|
||||
return jingo.render(request, 'devhub/payments/issue-refund.html',
|
||||
{'refund_issued': False,
|
||||
'user': contribution.user.display_name,
|
||||
'addon_name': addon.name,
|
||||
{'contribution': contribution,
|
||||
'addon': addon,
|
||||
'webapp': webapp,
|
||||
'price': contribution.amount,
|
||||
'transaction_id': txn_id,
|
||||
'purchase_date': contribution.created})
|
||||
'transaction_id': txn_id})
|
||||
|
||||
|
||||
@dev_required
|
||||
|
@ -1028,7 +1024,6 @@ def addons_section(request, addon_id, addon, section, editable=False,
|
|||
else:
|
||||
form = False
|
||||
|
||||
#import pdb; pdb.set_trace()
|
||||
data = {'addon': addon,
|
||||
'webapp': webapp,
|
||||
'form': form,
|
||||
|
|
|
@ -467,7 +467,6 @@ def profile(request, user_id):
|
|||
.filter(following__user=user)
|
||||
.order_by('-following__created'))[:10]
|
||||
|
||||
|
||||
edit_any_user = acl.action_allowed(request, 'Admin', 'EditAnyUser')
|
||||
own_profile = (request.user.is_authenticated() and
|
||||
request.amo_user.id == user.id)
|
||||
|
@ -760,11 +759,8 @@ def support_mozilla(request, contribution, wizard):
|
|||
def refund_request(request, contribution, wizard):
|
||||
addon = contribution.addon
|
||||
form = forms.RemoveForm(request.POST or None)
|
||||
if request.method == 'POST':
|
||||
if form.is_valid():
|
||||
return redirect(reverse('users.support',
|
||||
args=[contribution.pk, 'reason']))
|
||||
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
return redirect('users.support', contribution.pk, 'reason')
|
||||
return wizard.render(request, wizard.tpl('request.html'),
|
||||
{'addon': addon, 'webapp': addon.is_webapp(),
|
||||
'form': form, 'contribution': contribution})
|
||||
|
@ -773,35 +769,30 @@ def refund_request(request, contribution, wizard):
|
|||
def refund_reason(request, contribution, wizard):
|
||||
addon = contribution.addon
|
||||
if not 'request' in wizard.get_progress():
|
||||
return redirect(reverse('users.support',
|
||||
args=[contribution.pk, 'request']))
|
||||
return redirect('users.support', contribution.pk, 'request')
|
||||
|
||||
form = forms.ContactForm(request.POST or None)
|
||||
if request.method == 'POST':
|
||||
if form.is_valid():
|
||||
# if under 30 minutes, refund
|
||||
# TODO(ashort): add in the logic for under 30 minutes
|
||||
refund_url = absolutify(urlparams(
|
||||
reverse('devhub.issue_refund', args=[addon.slug]),
|
||||
transaction_id=contribution.transaction_id))
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
# TODO(ashort): Reject refund if purchase was more than 30 minutes ago.
|
||||
url = absolutify(urlparams(addon.get_dev_url('issue_refund'),
|
||||
transaction_id=contribution.transaction_id))
|
||||
template = jingo.render_to_string(request,
|
||||
wizard.tpl('emails/refund-request.txt'),
|
||||
context={'addon': addon,
|
||||
'form': form,
|
||||
'user': request.amo_user,
|
||||
'contribution': contribution,
|
||||
'refund_url': url})
|
||||
log.info('Refund request sent by user: %s for addon: %s' %
|
||||
(request.amo_user.pk, addon.pk))
|
||||
# L10n: %s is the addon name.
|
||||
send_mail(_(u'New Refund Request for %s' % addon.name),
|
||||
template, request.amo_user.email,
|
||||
[smart_str(addon.support_email)])
|
||||
return redirect(reverse('users.support',
|
||||
args=[contribution.pk, 'refund-sent']))
|
||||
|
||||
template = jingo.render_to_string(request,
|
||||
wizard.tpl('emails/refund-request.txt'),
|
||||
context={'addon': addon, 'form': form,
|
||||
'user': request.amo_user,
|
||||
'contribution': contribution,
|
||||
'refund_url': refund_url})
|
||||
log.info('Refund request sent by user: %s for addon: %s' %
|
||||
(request.amo_user.pk, addon.pk))
|
||||
# L10n: %s is the addon name.
|
||||
send_mail(_(u'New Refund Request for %s' % addon.name),
|
||||
template, request.amo_user.email,
|
||||
[smart_str(addon.support_email)])
|
||||
return redirect(reverse('users.support',
|
||||
args=[contribution.pk, 'refund-sent']))
|
||||
|
||||
return wizard.render(request, wizard.tpl('refund.html'),
|
||||
{'contribut': addon, 'form': form})
|
||||
return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
|
||||
|
||||
|
||||
class SupportWizard(Wizard):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import 'lib';
|
||||
@import '../impala/lib';
|
||||
|
||||
.devhub-form .tip,
|
||||
.addon-submission-process .tip,
|
||||
|
@ -92,3 +92,7 @@ a.remove:hover {
|
|||
.html-rtl .undo {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#issue-refund {
|
||||
font-size: 14px;
|
||||
}
|
|
@ -15,6 +15,17 @@
|
|||
.box-shadow(0 1px rgba(0, 0, 0, 0.1), 0 -2px rgba(0, 0, 0, 0.1) inset);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
button.good, .button.add { // Green
|
||||
background: #489615;
|
||||
.gradient-two-color(#84C63C, #489615);
|
||||
}
|
||||
|
||||
button.bad, .button.developer, .button.scary { // Red
|
||||
background: #bc2b1a;
|
||||
.gradient-two-color(#f84b4e, #bc2b1a);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
&.prominent {
|
||||
|
@ -23,9 +34,6 @@
|
|||
.box-shadow(0 3px rgba(0, 0, 0, 0.1), 0 -4px rgba(0, 0, 0, 0.1) inset);
|
||||
}
|
||||
&.add { // Green
|
||||
background: #489615;
|
||||
.gradient-two-color(#84C63C, #489615);
|
||||
color: #fff;
|
||||
span {
|
||||
padding-left: 16px;
|
||||
background: url(../../img/impala/button-icons.png) no-repeat 0 3px;
|
||||
|
@ -93,9 +101,6 @@
|
|||
}
|
||||
}
|
||||
&.developer, &.scary { // Red
|
||||
background: #bc2b1a;
|
||||
.gradient-two-color(#f84b4e, #bc2b1a);
|
||||
color: #fff;
|
||||
span {
|
||||
margin-left: -4px;
|
||||
padding-left: 24px;
|
||||
|
@ -106,7 +111,6 @@
|
|||
&.watch:not(.watching) { // Orange
|
||||
background: #ea0;
|
||||
.gradient-two-color(#ea0, darken(#ea0, 10%));
|
||||
color: #fff;
|
||||
}
|
||||
&.platform {
|
||||
display: none;
|
||||
|
|
|
@ -32,6 +32,11 @@ p.req {
|
|||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
/* CSRF token */
|
||||
form div[style]:first-child + p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.optional {
|
||||
color: @note-gray;
|
||||
font-size: 11px;
|
||||
|
@ -380,4 +385,3 @@ button.loading-submit:after {
|
|||
top: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
|
|
|
@ -506,7 +506,7 @@ MINIFY_BUNDLES = {
|
|||
'css/impala/devhub-popups.less',
|
||||
'css/impala/devhub-compat.less',
|
||||
'css/impala/formset.less',
|
||||
'css/impala/devhub-forms.less',
|
||||
'css/devhub/forms.less',
|
||||
),
|
||||
'zamboni/devhub_impala': (
|
||||
'css/impala/developers.less',
|
||||
|
@ -514,7 +514,7 @@ MINIFY_BUNDLES = {
|
|||
'css/impala/devhub-popups.less',
|
||||
'css/impala/devhub-compat.less',
|
||||
'css/impala/devhub-dashboard.less',
|
||||
'css/impala/devhub-forms.less',
|
||||
'css/devhub/forms.less',
|
||||
),
|
||||
'zamboni/editors': (
|
||||
'css/zamboni/editors.css',
|
||||
|
|
Загрузка…
Ссылка в новой задаче