diff --git a/apps/users/templates/users/purchases.html b/apps/users/templates/users/purchases.html index 51d74ea71a0..10c7fc9f245 100644 --- a/apps/users/templates/users/purchases.html +++ b/apps/users/templates/users/purchases.html @@ -15,8 +15,7 @@

{{ _('My Purchases') }}

{{ impala_addon_listing_header(url_base, filter.opts, sorting, filter.extras) }} {% endif %}
- {% for contribution in purchases.object_list %} - {% with addon=contribution.addon %} + {% for addon in addons.object_list %}

@@ -25,13 +24,17 @@

{{ addon.description|truncate(250)|nl2br }}

- {% trans date=addon.created, amt=contribution.get_amount_locale(), url=url('users.support', contribution.pk) %} -
Purchased {{ date }} for {{ amt }} - Request Support
- {% endtrans %} -
+ {% for contribution in contributions.get(addon.pk, []) %} + + {% endfor %} +
- {% endwith %} {% else %}

You have no purchases.

{% endfor %} diff --git a/apps/users/tests/test_views.py b/apps/users/tests/test_views.py index d2122289b20..50d8c9dd4cc 100644 --- a/apps/users/tests/test_views.py +++ b/apps/users/tests/test_views.py @@ -31,6 +31,7 @@ from users.models import BlacklistedPassword, UserProfile, UserNotification import users.notifications as email from users.utils import EmailResetCode, UnsubscribeCode +from webapps.models import Installed class UserViewBase(amo.tests.TestCase): @@ -879,6 +880,7 @@ def setUp(self): self.client.login(username='regular@mozilla.com', password='password') self.user = User.objects.get(email='regular@mozilla.com') + self.addon, self.con = None, None for x in range(1, 5): addon = Addon.objects.create(type=amo.ADDON_EXTENSION, name='t%s' % x) @@ -887,9 +889,8 @@ def setUp(self): type=amo.CONTRIB_PURCHASE) con.created = datetime.now() - timedelta(days=10 - x) con.save() - - self.con = con - self.addon = addon + if not self.addon and not self.con: + self.addon, self.con = addon, con def test_in_menu(self): doc = pq(self.client.get(self.url).content) @@ -912,31 +913,30 @@ def test_no_purchases(self): def test_purchase_list(self): res = self.client.get(self.url) eq_(res.status_code, 200) - eq_(len(res.context['purchases'].object_list), 4) + eq_(len(res.context['addons'].object_list), 4) def get_order(self, order): res = self.client.get('%s?sort=%s' % (self.url, order)) - return [str(c.addon.name) for c in - res.context['purchases'].object_list] + return [str(c.name) for c in + res.context['addons'].object_list] def test_ordering(self): eq_(self.get_order('name'), ['t1', 't2', 't3', 't4']) eq_(self.get_order('price'), ['t1', 't2', 't3', 't4']) - eq_(self.get_order('date'), ['t4', 't3', 't2', 't1']) def test_price(self): res = self.client.get(self.url) - assert '$4.00' in pq(res.content)('div.vitals').eq(0).text() + assert '$1.00' in pq(res.content)('div.vitals').eq(0).text() def test_price_locale(self): res = self.client.get(self.url.replace('/en-US', '/fr')) - assert u'4,00' in pq(res.content)('div.vitals').eq(0).text() + assert u'1,00' in pq(res.content)('div.vitals').eq(0).text() def test_receipt(self): res = self.client.get(reverse('users.purchases.receipt', args=[self.addon.pk])) - eq_(len(res.context['purchases'].object_list), 1) - eq_(res.context['purchases'].object_list[0].addon.pk, self.addon.pk) + eq_(len(res.context['addons'].object_list), 1) + eq_(res.context['addons'].object_list[0].pk, self.addon.pk) def test_receipt_404(self): url = reverse('users.purchases.receipt', args=[545]) @@ -1057,7 +1057,7 @@ def test_request_mails(self): email = mail.outbox[0] eq_(email.to, ['a@a.com']) eq_(email.from_email, 'regular@mozilla.com') - assert '$4.00' in email.body + assert '$1.00' in email.body def test_request_fails(self): self.addon.support_email = 'a@a.com' @@ -1066,3 +1066,23 @@ def test_request_fails(self): self.client.post(self.get_url('request'), {'remove': 1}) res = self.client.post(self.get_url('reason'), {}) eq_(res.status_code, 200) + + def test_free_shows_up(self): + Contribution.objects.all().delete() + res = self.client.get(self.url) + eq_(res.context['addons'][0].pk, self.addon.pk) + + def test_others_free_dont(self): + Contribution.objects.all().delete() + other = UserProfile.objects.get(pk=10482) + Installed.objects.all()[0].update(user=other) + res = self.client.get(self.url) + eq_(len(res.context['addons']), 3) + + def test_purchase_multiple(self): + Contribution.objects.create(user=self.user.get_profile(), + addon=self.addon, amount='1.00', + type=amo.CONTRIB_PURCHASE) + res = self.client.get(self.url) + addon_vitals = pq(res.content)('div.vitals').eq(0) + eq_(len(addon_vitals('div.purchase-byline')), 2) diff --git a/apps/users/views.py b/apps/users/views.py index 53ba1c52110..7d39f2231b2 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -569,19 +569,16 @@ def unsubscribe(request, hash=None, token=None, perm_setting=None): {'unsubscribed': unsubscribed, 'perm_settings': perm_settings}) -class ContributionsFilter(BaseFilter): - opts = (('date', _lazy(u'Purchase Date')), - ('price', _lazy(u'Price')), +class AddonsFilter(BaseFilter): + opts = (('price', _lazy(u'Price')), ('name', _lazy(u'Name'))) def filter(self, field): qs = self.base_queryset - if field == 'date': - return qs.order_by('-created') - elif field == 'price': - return qs.order_by('amount') + if field == 'price': + return qs.order_by('addonpremium__price') elif field == 'name': - return qs.order_by('addon__name') + return qs.order_by('name') @login_required @@ -590,21 +587,39 @@ def purchases(request, addon_id=None): if not waffle.switch_is_active('marketplace'): raise http.Http404 + # First get all the contributions. # TODO(ashort): this is where we'll need to get cunning about refunds. cs = Contribution.objects.filter(user=request.amo_user, type=amo.CONTRIB_PURCHASE) if addon_id: cs = cs.filter(addon=addon_id) - filter = ContributionsFilter(request, cs, key='sort', default='date') - purchases = amo.utils.paginate(request, filter.qs) + contributions = {} + for c in cs: + contributions.setdefault(c.addon_id, []).append(c) - if addon_id and not purchases.object_list: + # If you are asking for a receipt for just one item, only show that. + # Otherwise, we'll show all addons that have a contribution or are free. + if addon_id: + ids = [addon_id] + else: + free = (request.amo_user.installed_set + .exclude(addon__in=contributions.keys()) + .values_list('addon_id', flat=True)) + ids = contributions.keys() + list(free) + + filter = AddonsFilter(request, Addon.objects.filter(id__in=ids), + key='sort', default='name') + + if addon_id and not filter.qs: # User has requested a receipt for an addon they don't have. raise http.Http404 + return jingo.render(request, 'users/purchases.html', - {'purchases': purchases, 'filter': filter, + {'addons': amo.utils.paginate(request, filter.qs), + 'filter': filter, 'url_base': reverse('users.purchases'), + 'contributions': contributions, 'single': bool(addon_id)}) diff --git a/media/css/impala/listing.less b/media/css/impala/listing.less index 112481f5ec3..ae00feb2bdf 100644 --- a/media/css/impala/listing.less +++ b/media/css/impala/listing.less @@ -246,6 +246,9 @@ color: @note-gray; content: '\00B7'; } + &.purchase-byline { + display: block; + } } span.price { color: @green;