Merge lp:~sinzui/launchpad/apply-commercial-subscriptins-1 into lp:launchpad

Proposed by Curtis Hovey on 2012-12-07
Status: Merged
Approved by: Curtis Hovey on 2012-12-07
Approved revision: no longer in the source branch.
Merged at revision: 16351
Proposed branch: lp:~sinzui/launchpad/apply-commercial-subscriptins-1
Merge into: lp:launchpad
Diff against target: 254 lines (+83/-67)
4 files modified
lib/lp/registry/browser/configure.zcml (+1/-1)
lib/lp/registry/browser/person.py (+3/-1)
lib/lp/registry/browser/tests/test_commercialsubscription.py (+69/-65)
lib/lp/security.py (+10/-0)
To merge this branch: bzr merge lp:~sinzui/launchpad/apply-commercial-subscriptins-1
Reviewer Review Type Date Requested Status
Abel Deuring (community) code 2012-12-07 Approve on 2012-12-07
Review via email: mp+138761@code.launchpad.net

Commit Message

Allow commercial admins to apply commercial subscription vouchers for users.

Description of the Change

Users are confused that buying a commercial subscription does not apply
it to their commercial project. Commercial Admins often quiz both the
buyer and ask Lp Admins to confirm the user has vouchers that can be
applied to a commercial project.

RULES

    Pre-implementation: czajkowski
    * Allow Commercial Admins to view another user's vouchers and apply them
      to the user's projects.

    Next branch
    * Show a link to the +vouchers page when the user has commercial
      subscription vouchers.
    * Show a link to the user's +voucher page when the user has
      commercial subscription vouchers and the project page says it
      needs a renewed commercial subscription.

QA

    As a CA
    * Visit https://qastaging.launchpad.net/~mrevell/+vouchers
      or https://qastaging.launchpad.net/~bac/+vouchers
    * Verify you can see they have vouchers and that you can apply one.

LINT

    lib/lp/security.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/tests/test_commercialsubscription.py

LoC

    I have more than 10,000 lines of credit this week.

TEST

    ./bin/test -vvc -t PersonVouchersViewTestCase -t vouchers lp.registry

IMPLEMENTATION

I added a security checker for IPerson and launchpad.Commercial, then
updated the view to ensure there was no change for users. I then added
tests and made one small adjustment to the view to Verify that commercial
admins could see and apply another user's vouchers. I removed a lot ot
duplication in the viuew tests.
    lib/lp/security.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/tests/test_commercialsubscription.py

To post a comment you must log in.
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/configure.zcml'
2--- lib/lp/registry/browser/configure.zcml 2012-11-08 03:55:11 +0000
3+++ lib/lp/registry/browser/configure.zcml 2012-12-07 15:50:29 +0000
4@@ -955,7 +955,7 @@
5 <browser:page
6 for="lp.registry.interfaces.person.IPerson"
7 name="+vouchers"
8- permission="launchpad.Edit"
9+ permission="launchpad.Commercial"
10 class="lp.registry.browser.person.PersonVouchersView"
11 template="../templates/person-vouchers.pt"/>
12 <browser:page
13
14=== modified file 'lib/lp/registry/browser/person.py'
15--- lib/lp/registry/browser/person.py 2012-11-29 16:17:01 +0000
16+++ lib/lp/registry/browser/person.py 2012-12-07 15:50:29 +0000
17@@ -177,6 +177,7 @@
18 from lp.registry.interfaces.pillar import IPillarNameSet
19 from lp.registry.interfaces.poll import IPollSubset
20 from lp.registry.interfaces.product import IProduct
21+from lp.registry.interfaces.role import IPersonRoles
22 from lp.registry.interfaces.ssh import (
23 ISSHKeySet,
24 SSHKeyAdditionError,
25@@ -1330,7 +1331,8 @@
26 project so the property is True always. Otherwise it is true if the
27 vocabulary is not empty.
28 """
29- if check_permission('launchpad.Commercial', self.context):
30+ role = IPersonRoles(self.user)
31+ if role.in_commercial_admin or role.in_admin:
32 return True
33 vocabulary_registry = getVocabularyRegistry()
34 vocabulary = vocabulary_registry.get(self.context,
35
36=== modified file 'lib/lp/registry/browser/tests/test_commercialsubscription.py'
37--- lib/lp/registry/browser/tests/test_commercialsubscription.py 2012-10-22 02:30:44 +0000
38+++ lib/lp/registry/browser/tests/test_commercialsubscription.py 2012-12-07 15:50:29 +0000
39@@ -26,114 +26,121 @@
40 class PersonVouchersViewTestCase(FakeAdapterMixin, TestCaseWithFactory):
41 layer = DatabaseFunctionalLayer
42
43+ def makeVouchers(self, user, number, voucher_proxy=None):
44+ if voucher_proxy is None:
45+ voucher_proxy = TestSalesforceVoucherProxy()
46+ self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
47+ vouchers = []
48+ for n in xrange(number):
49+ vouchers.append(voucher_proxy.grantVoucher(user, user, user, 12))
50+ return vouchers
51+
52 def test_init_without_vouchers_or_projects(self):
53 # The view provides common view properties, but the form is disabled.
54 user = self.factory.makePerson()
55 self.factory.makeProduct(owner=user)
56- voucher_proxy = TestSalesforceVoucherProxy()
57- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
58+ self.makeVouchers(user, 0)
59 user_url = canonical_url(user)
60- view = create_initialized_view(user, '+vouchers')
61+ with person_logged_in(user):
62+ view = create_initialized_view(user, '+vouchers')
63 self.assertEqual('Commercial subscription vouchers', view.page_title)
64 self.assertEqual(user_url, view.cancel_url)
65 self.assertIs(None, view.next_url)
66 self.assertEqual(0, len(view.redeemable_vouchers))
67
68+ def assertFields(self, view):
69+ self.assertEqual(1, len(view.redeemable_vouchers))
70+ self.assertEqual(
71+ ['project', 'voucher'], [f.__name__ for f in view.form_fields])
72+
73 def test_init_with_vouchers_and_projects(self):
74 # The fields are setup when the user hase both vouchers and projects.
75 user = self.factory.makePerson()
76 login_person(user)
77- voucher_proxy = TestSalesforceVoucherProxy()
78- voucher_proxy.grantVoucher(
79- user, user, user, 12)
80- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
81+ self.makeVouchers(user, 1)
82 self.factory.makeProduct(owner=user)
83 view = create_initialized_view(user, '+vouchers')
84- self.assertEqual(1, len(view.redeemable_vouchers))
85- self.assertEqual(
86- ['project', 'voucher'], [f.__name__ for f in view.form_fields])
87+ self.assertFields(view)
88
89 def test_init_with_commercial_admin_with_vouchers(self):
90 # The fields are setup if the commercial admin has vouchers.
91 commercial_admin = login_celebrity('commercial_admin')
92- voucher_proxy = TestSalesforceVoucherProxy()
93- voucher_proxy.grantVoucher(
94- commercial_admin, commercial_admin, commercial_admin, 12)
95- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
96+ self.makeVouchers(commercial_admin, 1)
97 view = create_initialized_view(commercial_admin, '+vouchers')
98- self.assertEqual(1, len(view.redeemable_vouchers))
99- self.assertEqual(
100- ['project', 'voucher'], [f.__name__ for f in view.form_fields])
101-
102- def test_redeem_with_commercial_admin(self):
103- # The fields are setup if the commercial admin has vouchers.
104- commercial_admin = login_celebrity('commercial_admin')
105- voucher_proxy = TestSalesforceVoucherProxy()
106- voucher_id = voucher_proxy.grantVoucher(
107- commercial_admin, commercial_admin, commercial_admin, 12)
108- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
109- project = self.factory.makeProduct()
110- form = {
111+ self.assertFields(view)
112+
113+ def test_with_commercial_admin_for_user_with_vouchers_and_projects(self):
114+ # A commercial admin can see another user's vouchers.
115+ user = self.factory.makePerson()
116+ login_person(user)
117+ self.makeVouchers(user, 1)
118+ self.factory.makeProduct(owner=user)
119+ login_celebrity('commercial_admin')
120+ view = create_initialized_view(user, '+vouchers')
121+ self.assertFields(view)
122+
123+ def assertRedeem(self, view, project, remaining=0):
124+ self.assertEqual([], view.errors)
125+ self.assertIsNot(None, project.commercial_subscription)
126+ self.assertEqual(remaining, len(view.redeemable_vouchers))
127+ self.assertEqual(
128+ remaining, len(view.form_fields['voucher'].field.vocabulary))
129+ self.assertEqual(
130+ remaining, len(view.widgets['voucher'].vocabulary))
131+
132+ def makeForm(self, project, voucher_id):
133+ return {
134 'field.project': project.name,
135 'field.voucher': voucher_id,
136 'field.actions.redeem': 'Redeem',
137 }
138+
139+ def test_redeem_with_commercial_admin_for_user(self):
140+ # A commercial admin can redeem a voucher for a user.
141+ project = self.factory.makeProduct()
142+ user = project.owner
143+ [voucher_id] = self.makeVouchers(user, 1)
144+ form = self.makeForm(project, voucher_id)
145+ login_celebrity('commercial_admin')
146+ view = create_initialized_view(user, '+vouchers', form=form)
147+ self.assertRedeem(view, project)
148+
149+ def test_redeem_with_commercial_admin(self):
150+ # The fields are setup if the commercial admin has vouchers.
151+ commercial_admin = login_celebrity('commercial_admin')
152+ [voucher_id] = self.makeVouchers(commercial_admin, 1)
153+ project = self.factory.makeProduct()
154+ form = self.makeForm(project, voucher_id)
155 view = create_initialized_view(
156 commercial_admin, '+vouchers', form=form)
157- self.assertEqual([], view.errors)
158- self.assertIsNot(None, project.commercial_subscription)
159- self.assertEqual(0, len(view.redeemable_vouchers))
160- self.assertEqual(
161- 0, len(view.form_fields['voucher'].field.vocabulary))
162- self.assertEqual(
163- 0, len(view.widgets['voucher'].vocabulary))
164+ self.assertRedeem(view, project)
165
166 def test_redeem_twice_with_commercial_admin(self):
167 # The fields are setup if the commercial admin has vouchers.
168 commercial_admin = login_celebrity('commercial_admin')
169 voucher_proxy = TestSalesforceVoucherProxy()
170- voucher_id_1 = voucher_proxy.grantVoucher(
171- commercial_admin, commercial_admin, commercial_admin, 12)
172- voucher_id_2 = voucher_proxy.grantVoucher(
173- commercial_admin, commercial_admin, commercial_admin, 12)
174- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
175+ voucher_id_1, voucher_id_2 = self.makeVouchers(
176+ commercial_admin, 2, voucher_proxy)
177 project_1 = self.factory.makeProduct()
178 project_2 = self.factory.makeProduct()
179- form = {
180- 'field.project': project_1.name,
181- 'field.voucher': voucher_id_1,
182- 'field.actions.redeem': 'Redeem',
183- }
184+ form = self.makeForm(project_1, voucher_id_1)
185 view = create_initialized_view(
186 commercial_admin, '+vouchers', form=form)
187- self.assertEqual([], view.errors)
188- self.assertIsNot(None, project_1.commercial_subscription)
189- self.assertEqual(1, len(view.redeemable_vouchers))
190+ self.assertRedeem(view, project_1, remaining=1)
191 # A job will notify Salesforce of the voucher redemption but here we
192 # will do it manually.
193 voucher_proxy.redeemVoucher(
194 voucher_id_1, commercial_admin, project_1)
195
196- form = {
197- 'field.project': project_2.name,
198- 'field.voucher': voucher_id_2,
199- 'field.actions.redeem': 'Redeem',
200- }
201+ form = self.makeForm(project_2, voucher_id_2)
202 view = create_initialized_view(
203 commercial_admin, '+vouchers', form=form)
204- self.assertEqual([], view.errors)
205- self.assertIsNot(None, project_2.commercial_subscription)
206- self.assertEqual(0, len(view.redeemable_vouchers))
207+ self.assertRedeem(view, project_2)
208
209 def test_pending_vouchers_excluded(self):
210 # Vouchers pending redemption in Salesforce are not included in choice.
211 commercial_admin = login_celebrity('commercial_admin')
212- voucher_proxy = TestSalesforceVoucherProxy()
213- voucher_id_1 = voucher_proxy.grantVoucher(
214- commercial_admin, commercial_admin, commercial_admin, 12)
215- voucher_id_2 = voucher_proxy.grantVoucher(
216- commercial_admin, commercial_admin, commercial_admin, 12)
217- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
218+ voucher_id_1, voucher_id_2 = self.makeVouchers(commercial_admin, 2)
219 project_1 = self.factory.makeProduct()
220 self.factory.makeCommercialSubscription(
221 project_1, False, 'pending-' + voucher_id_1)
222@@ -146,10 +153,7 @@
223 def test_redeem_twice_causes_error(self):
224 # If a voucher is redeemed twice, the second attempt is rejected.
225 commercial_admin = login_celebrity('commercial_admin')
226- voucher_proxy = TestSalesforceVoucherProxy()
227- voucher_id_1 = voucher_proxy.grantVoucher(
228- commercial_admin, commercial_admin, commercial_admin, 12)
229- self.registerUtility(voucher_proxy, ISalesforceVoucherProxy)
230+ voucher_id_1, voucher_id_2 = self.makeVouchers(commercial_admin, 2)
231 project_1 = self.factory.makeProduct(name='p1')
232 project_2 = self.factory.makeProduct(name='p2')
233 url = canonical_url(commercial_admin, view_name='+vouchers')
234
235=== modified file 'lib/lp/security.py'
236--- lib/lp/security.py 2012-12-04 13:52:02 +0000
237+++ lib/lp/security.py 2012-12-07 15:50:29 +0000
238@@ -895,6 +895,16 @@
239 return False
240
241
242+class AdminByCommercialTeamOrAdminsOrPerson(AdminByCommercialTeamOrAdmins):
243+ permission = 'launchpad.Commercial'
244+ usedfor = IPerson
245+
246+ def checkAuthenticated(self, user):
247+ """Users can manage their commericial data and admins can help."""
248+ base = super(AdminByCommercialTeamOrAdminsOrPerson, self)
249+ return self.obj.id == user.id or base.checkAuthenticated(user)
250+
251+
252 class EditPersonBySelfOrAdmins(AuthorizationBase):
253 permission = 'launchpad.Edit'
254 usedfor = IPerson