← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:commercial-subscription-distribution into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:commercial-subscription-distribution into launchpad:master.

Commit message:
Add basic support for commercial subscriptions to distributions

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/415306

These are used to control the ability to use privacy features.

There isn't much in the way of tests here yet, because writing those needs privacy support in the `Distribution` model which is a rather more involved affair, but this gets some of the simple bits out of the way.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:commercial-subscription-distribution into launchpad:master.
diff --git a/lib/lp/_schema_circular_imports.py b/lib/lp/_schema_circular_imports.py
index 7c99b77..9f981c0 100644
--- a/lib/lp/_schema_circular_imports.py
+++ b/lib/lp/_schema_circular_imports.py
@@ -429,6 +429,11 @@ patch_reference_property(IBuildFarmJob, 'buildqueue_record', IBuildQueue)
 # IComment
 patch_reference_property(IComment, 'comment_author', IPerson)
 
+# ICommercialSubscription
+patch_reference_property(ICommercialSubscription, 'product', IProduct)
+patch_reference_property(
+    ICommercialSubscription, 'distribution', IDistribution)
+
 # IDistribution
 patch_collection_property(IDistribution, 'series', IDistroSeries)
 patch_collection_property(IDistribution, 'derivatives', IDistroSeries)
diff --git a/lib/lp/bugs/browser/tests/test_bugtarget_filebug.py b/lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
index 3a693b3..3d4928f 100644
--- a/lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
+++ b/lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
@@ -385,7 +385,7 @@ class TestFileBugViewBase(FileBugViewMixin, TestCaseWithFactory):
         if bug_sharing_policy:
             if not IProduct.providedBy(target):
                 raise ValueError("Only Product supports this.")
-            self.factory.makeCommercialSubscription(product=target)
+            self.factory.makeCommercialSubscription(pillar=target)
             with person_logged_in(owner):
                 target.setBugSharingPolicy(bug_sharing_policy)
         with person_logged_in(owner):
@@ -476,7 +476,7 @@ class TestFileBugViewBase(FileBugViewMixin, TestCaseWithFactory):
         # The vocabulary for information_type when filing a bug is created
         # correctly for a project with a proprietary sharing policy.
         product = self.factory.makeProduct(official_malone=True)
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(product.owner):
             product.setBugSharingPolicy(BugSharingPolicy.PROPRIETARY)
             view = create_initialized_view(
@@ -777,7 +777,7 @@ class TestFileBugForNonBugSupervisors(TestCaseWithFactory):
         }
         product = self.factory.makeProduct(official_malone=True)
         if bug_sharing_policy:
-            self.factory.makeCommercialSubscription(product=product)
+            self.factory.makeCommercialSubscription(pillar=product)
             with person_logged_in(product.owner):
                 product.setBugSharingPolicy(bug_sharing_policy)
         anyone = self.factory.makePerson()
diff --git a/lib/lp/bugs/mail/tests/test_handler.py b/lib/lp/bugs/mail/tests/test_handler.py
index 20dadf6..65033d2 100644
--- a/lib/lp/bugs/mail/tests/test_handler.py
+++ b/lib/lp/bugs/mail/tests/test_handler.py
@@ -249,7 +249,7 @@ class MaloneHandlerProcessTestCase(TestCaseWithFactory):
 
     def test_new_bug_with_sharing_policy_proprietary(self):
         project = self.factory.makeProduct(name='fnord')
-        self.factory.makeCommercialSubscription(product=project)
+        self.factory.makeCommercialSubscription(pillar=project)
         with person_logged_in(project.owner):
             project.setBugSharingPolicy(BugSharingPolicy.PROPRIETARY)
         transaction.commit()
diff --git a/lib/lp/code/browser/tests/test_branch.py b/lib/lp/code/browser/tests/test_branch.py
index ceaac7c..ac77d79 100644
--- a/lib/lp/code/browser/tests/test_branch.py
+++ b/lib/lp/code/browser/tests/test_branch.py
@@ -1225,7 +1225,7 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         # proprietary allow only embargoed and proprietary types.
         owner = self.factory.makePerson()
         product = self.factory.makeProduct(owner=owner)
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(owner):
             product.setBranchSharingPolicy(
                 BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
@@ -1240,7 +1240,7 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         # allow only the proprietary type.
         owner = self.factory.makePerson()
         product = self.factory.makeProduct(owner=owner)
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(owner):
             product.setBranchSharingPolicy(BranchSharingPolicy.PROPRIETARY)
             branch = self.factory.makeBranch(
diff --git a/lib/lp/code/browser/tests/test_gitrepository.py b/lib/lp/code/browser/tests/test_gitrepository.py
index 6b3e480..05c7043 100644
--- a/lib/lp/code/browser/tests/test_gitrepository.py
+++ b/lib/lp/code/browser/tests/test_gitrepository.py
@@ -1309,7 +1309,7 @@ class TestGitRepositoryEditViewInformationTypes(TestCaseWithFactory):
         # types.
         owner = self.factory.makePerson()
         project = self.factory.makeProduct(owner=owner)
-        self.factory.makeCommercialSubscription(product=project)
+        self.factory.makeCommercialSubscription(pillar=project)
         with person_logged_in(owner):
             project.setBranchSharingPolicy(
                 BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
@@ -1325,7 +1325,7 @@ class TestGitRepositoryEditViewInformationTypes(TestCaseWithFactory):
         # proprietary allow only the proprietary type.
         owner = self.factory.makePerson()
         product = self.factory.makeProduct(owner=owner)
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(owner):
             product.setBranchSharingPolicy(BranchSharingPolicy.PROPRIETARY)
             repository = self.factory.makeGitRepository(
diff --git a/lib/lp/code/model/tests/test_branchnamespace.py b/lib/lp/code/model/tests/test_branchnamespace.py
index d0dad62..aff13b1 100644
--- a/lib/lp/code/model/tests/test_branchnamespace.py
+++ b/lib/lp/code/model/tests/test_branchnamespace.py
@@ -391,7 +391,7 @@ class TestProjectBranchNamespacePrivacyWithInformationType(
         if person is None:
             person = self.factory.makePerson()
         product = self.factory.makeProduct()
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(product.owner):
             product.setBranchSharingPolicy(sharing_policy)
         namespace = ProjectBranchNamespace(person, product)
diff --git a/lib/lp/code/model/tests/test_gitnamespace.py b/lib/lp/code/model/tests/test_gitnamespace.py
index eb11aa9..ac469e9 100644
--- a/lib/lp/code/model/tests/test_gitnamespace.py
+++ b/lib/lp/code/model/tests/test_gitnamespace.py
@@ -620,7 +620,7 @@ class TestProjectGitNamespacePrivacyWithInformationType(TestCaseWithFactory):
         if person is None:
             person = self.factory.makePerson()
         project = self.factory.makeProduct()
-        self.factory.makeCommercialSubscription(product=project)
+        self.factory.makeCommercialSubscription(pillar=project)
         with person_logged_in(project.owner):
             project.setBranchSharingPolicy(sharing_policy)
         namespace = ProjectGitNamespace(person, project)
diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
index 63443a0..07fada1 100644
--- a/lib/lp/registry/browser/configure.zcml
+++ b/lib/lp/registry/browser/configure.zcml
@@ -654,7 +654,7 @@
     <browser:url
         for="lp.registry.interfaces.commercialsubscription.ICommercialSubscription"
         path_expression="string:+commercialsubscription/${id}"
-        attribute_to_parent="product"
+        attribute_to_parent="pillar"
         />
     <browser:url
         for="lp.registry.interfaces.jabber.IJabberID"
diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
index 4f11395..6ae28d7 100644
--- a/lib/lp/registry/browser/distribution.py
+++ b/lib/lp/registry/browser/distribution.py
@@ -196,6 +196,10 @@ class DistributionNavigation(
     def traverse_archive(self, name):
         return self.context.getArchive(name)
 
+    @stepthrough('+commercialsubscription')
+    def traverse_commercialsubscription(self, name):
+        return self.context.commercial_subscription
+
     def _resolveSeries(self, name):
         try:
             return self.context[name], False
diff --git a/lib/lp/registry/interfaces/commercialsubscription.py b/lib/lp/registry/interfaces/commercialsubscription.py
index 81b064a..62c1ec1 100644
--- a/lib/lp/registry/interfaces/commercialsubscription.py
+++ b/lib/lp/registry/interfaces/commercialsubscription.py
@@ -12,7 +12,10 @@ from lazr.restful.declarations import (
     exported_as_webservice_entry,
     )
 from lazr.restful.fields import ReferenceChoice
-from zope.interface import Interface
+from zope.interface import (
+    Attribute,
+    Interface,
+    )
 from zope.schema import (
     Bool,
     Datetime,
@@ -38,15 +41,30 @@ class ICommercialSubscription(Interface):
     product = exported(
         ReferenceChoice(
             title=_("Product which has commercial subscription"),
-            required=True,
+            required=False,
             readonly=True,
             vocabulary='Product',
-            # Really IProduct. See lp/registry/interfaces/product.py
+            # Really IProduct, patched in _schema_circular_imports.py.
             schema=Interface,
             description=_(
                 "Project for which this commercial subscription is "
                 "applied.")))
 
+    distribution = exported(
+        ReferenceChoice(
+            title=_("Distribution which has commercial subscription"),
+            required=False,
+            readonly=True,
+            vocabulary='Distribution',
+            # Really IDistribution, patched in _schema_circular_imports.py.
+            schema=Interface,
+            description=_(
+                "Distribution for which this commercial subscription is "
+                "applied.")))
+
+    pillar = Attribute(
+        "Pillar for which this commercial subscription is applied.")
+
     date_created = exported(
         Datetime(
             title=_('Date Created'),
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index 69d9540..6c9b00d 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -82,6 +82,9 @@ from lp.registry.enums import (
     VCSType,
     )
 from lp.registry.interfaces.announcement import IMakesAnnouncements
+from lp.registry.interfaces.commercialsubscription import (
+    ICommercialSubscription,
+    )
 from lp.registry.interfaces.distributionmirror import IDistributionMirror
 from lp.registry.interfaces.distroseries import DistroSeriesNameField
 from lp.registry.interfaces.karma import IKarmaContext
@@ -449,6 +452,13 @@ class IDistributionPublic(
             "to a different canonical URL."),
         readonly=False, required=False))
 
+    commercial_subscription = exported(Reference(
+        ICommercialSubscription,
+        title=_("Commercial subscriptions"),
+        description=_(
+            "An object which contains the timeframe and the voucher code of a "
+            "subscription.")))
+
     def getArchiveIDList(archive=None):
         """Return a list of archive IDs suitable for sqlvalues() or quote().
 
diff --git a/lib/lp/registry/interfaces/product.py b/lib/lp/registry/interfaces/product.py
index 120a9fe..2d0c179 100644
--- a/lib/lp/registry/interfaces/product.py
+++ b/lib/lp/registry/interfaces/product.py
@@ -1190,5 +1190,3 @@ from lp.registry.interfaces.distributionsourcepackage import (  # noqa: E402
 
 patch_reference_property(
     IDistributionSourcePackage, 'upstream_product', IProduct)
-
-patch_reference_property(ICommercialSubscription, 'product', IProduct)
diff --git a/lib/lp/registry/model/commercialsubscription.py b/lib/lp/registry/model/commercialsubscription.py
index 1620c27..fc467b9 100644
--- a/lib/lp/registry/model/commercialsubscription.py
+++ b/lib/lp/registry/model/commercialsubscription.py
@@ -21,7 +21,9 @@ from lp.registry.errors import CannotDeleteCommercialSubscription
 from lp.registry.interfaces.commercialsubscription import (
     ICommercialSubscription,
     )
+from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.person import validate_public_person
+from lp.registry.interfaces.product import IProduct
 from lp.services.database.constants import UTC_NOW
 from lp.services.database.stormbase import StormBase
 
@@ -33,9 +35,12 @@ class CommercialSubscription(StormBase):
 
     id = Int(primary=True)
 
-    product_id = Int(name='product', allow_none=False)
+    product_id = Int(name='product', allow_none=True)
     product = Reference(product_id, 'Product.id')
 
+    distribution_id = Int(name='distribution', allow_none=True)
+    distribution = Reference(distribution_id, 'Distribution.id')
+
     date_created = DateTime(tzinfo=pytz.UTC, allow_none=False, default=UTC_NOW)
     date_last_modified = DateTime(
         tzinfo=pytz.UTC, allow_none=False, default=UTC_NOW)
@@ -53,10 +58,17 @@ class CommercialSubscription(StormBase):
     sales_system_id = Unicode(allow_none=False)
     whiteboard = Unicode(default=None)
 
-    def __init__(self, product, date_starts, date_expires, registrant,
+    def __init__(self, pillar, date_starts, date_expires, registrant,
                  purchaser, sales_system_id, whiteboard):
         super().__init__()
-        self.product = product
+        if IProduct.providedBy(pillar):
+            self.product = pillar
+            self.distribution = None
+        elif IDistribution.providedBy(pillar):
+            self.product = None
+            self.distribution = pillar
+        else:
+            raise AssertionError("Unknown pillar: %r" % pillar)
         self.date_starts = date_starts
         self.date_expires = date_expires
         self.registrant = registrant
@@ -65,6 +77,11 @@ class CommercialSubscription(StormBase):
         self.whiteboard = whiteboard
 
     @property
+    def pillar(self):
+        return (
+            self.product if self.product_id is not None else self.distribution)
+
+    @property
     def is_active(self):
         """See `ICommercialSubscription`"""
         now = datetime.datetime.now(pytz.timezone('UTC'))
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index b993152..867c4df 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -120,6 +120,7 @@ from lp.registry.interfaces.role import IPersonRoles
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.interfaces.sourcepackagename import ISourcePackageName
 from lp.registry.model.announcement import MakesAnnouncements
+from lp.registry.model.commercialsubscription import CommercialSubscription
 from lp.registry.model.distributionmirror import (
     DistributionMirror,
     MirrorCDImageDistroSeries,
@@ -343,6 +344,11 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
         # Sharing policy for distributions is always PUBLIC.
         return SpecificationSharingPolicy.PUBLIC
 
+    @cachedproperty
+    def commercial_subscription(self):
+        return IStore(CommercialSubscription).find(
+            CommercialSubscription, distribution=self).one()
+
     @property
     def uploaders(self):
         """See `IDistribution`."""
diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py
index f213c1d..0d9235c 100644
--- a/lib/lp/registry/model/product.py
+++ b/lib/lp/registry/model/product.py
@@ -985,7 +985,7 @@ class Product(SQLBase, BugTargetBase, MakesAnnouncements,
                 "Complimentary 30 day subscription. -- Launchpad %s" %
                 now.date().isoformat())
             subscription = CommercialSubscription(
-                product=self, date_starts=now, date_expires=date_expires,
+                pillar=self, date_starts=now, date_expires=date_expires,
                 registrant=lp_janitor, purchaser=lp_janitor,
                 sales_system_id=sales_system_id, whiteboard=whiteboard)
             get_property_cache(self).commercial_subscription = subscription
diff --git a/lib/lp/registry/stories/webservice/xx-distribution.txt b/lib/lp/registry/stories/webservice/xx-distribution.txt
index f0748c2..4f98323 100644
--- a/lib/lp/registry/stories/webservice/xx-distribution.txt
+++ b/lib/lp/registry/stories/webservice/xx-distribution.txt
@@ -28,6 +28,7 @@ And for every distribution we publish most of its attributes.
     bug_reporting_guidelines: None
     bug_supervisor_link: None
     cdimage_mirrors_collection_link: 'http://.../ubuntu/cdimage_mirrors'
+    commercial_subscription_link: None
     current_series_link: 'http://.../ubuntu/hoary'
     date_created: '2006-10-16T18:31:43.415195+00:00'
     default_traversal_policy: 'Series'
diff --git a/lib/lp/registry/tests/test_product.py b/lib/lp/registry/tests/test_product.py
index d0eb5a6..c037549 100644
--- a/lib/lp/registry/tests/test_product.py
+++ b/lib/lp/registry/tests/test_product.py
@@ -1237,7 +1237,7 @@ class TestProductBugInformationTypes(TestCaseWithFactory):
 
     def makeProductWithPolicy(self, bug_sharing_policy):
         product = self.factory.makeProduct()
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(product.owner):
             product.setBugSharingPolicy(bug_sharing_policy)
         return product
@@ -1290,7 +1290,7 @@ class TestProductSpecificationPolicyAndInformationTypes(TestCaseWithFactory):
 
     def makeProductWithPolicy(self, specification_sharing_policy):
         product = self.factory.makeProduct()
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(product.owner):
             product.setSpecificationSharingPolicy(
                 specification_sharing_policy)
@@ -1658,7 +1658,7 @@ class BaseSharingPolicyTests:
 
     def test_commercial_admin_can_set_policy(self):
         # Commercial admins can set sharing policies for commercial projects.
-        self.factory.makeCommercialSubscription(product=self.product)
+        self.factory.makeCommercialSubscription(pillar=self.product)
         self.setSharingPolicy(self.public_policy, self.commercial_admin)
         self.assertEqual(self.public_policy, self.getSharingPolicy())
 
@@ -1686,7 +1686,7 @@ class BaseSharingPolicyTests:
     def test_proprietary_allowed_with_commercial_sub(self):
         # All policies are valid when there's a current commercial
         # subscription.
-        self.factory.makeCommercialSubscription(product=self.product)
+        self.factory.makeCommercialSubscription(pillar=self.product)
         for policy in self.enum.items:
             self.setSharingPolicy(policy, self.commercial_admin)
             self.assertEqual(policy, self.getSharingPolicy())
@@ -1695,7 +1695,7 @@ class BaseSharingPolicyTests:
         # Setting a policy that allows Proprietary creates a
         # corresponding access policy and shares it with the the
         # maintainer.
-        self.factory.makeCommercialSubscription(product=self.product)
+        self.factory.makeCommercialSubscription(pillar=self.product)
         self.assertEqual(
             [InformationType.PRIVATESECURITY, InformationType.USERDATA],
             [policy.type for policy in
@@ -1730,7 +1730,7 @@ class BaseSharingPolicyTests:
                 for ap in ap_source.findByPillar([pillar])]
 
         # Now change the sharing policies to PROPRIETARY
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(product.owner):
             product.setBugSharingPolicy(BugSharingPolicy.PROPRIETARY)
             # Just bug sharing policy has been changed so all previous policy
@@ -1817,7 +1817,7 @@ class ProductBranchSharingPolicyTestCase(BaseSharingPolicyTests,
         # Setting a policy that allows Embargoed creates a
         # corresponding access policy and shares it with the the
         # maintainer.
-        self.factory.makeCommercialSubscription(product=self.product)
+        self.factory.makeCommercialSubscription(pillar=self.product)
         self.assertEqual(
             [InformationType.PRIVATESECURITY, InformationType.USERDATA],
             [policy.type for policy in
diff --git a/lib/lp/registry/tests/test_product_webservice.py b/lib/lp/registry/tests/test_product_webservice.py
index 4f6e465..5808836 100644
--- a/lib/lp/registry/tests/test_product_webservice.py
+++ b/lib/lp/registry/tests/test_product_webservice.py
@@ -57,7 +57,7 @@ class TestProduct(TestCaseWithFactory):
         # branch_sharing_policy can be set via the API.
         product = self.factory.makeProduct()
         owner = product.owner
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         webservice = webservice_for_person(
             owner, permission=OAuthPermission.WRITE_PRIVATE)
         response = self.patch(
@@ -88,7 +88,7 @@ class TestProduct(TestCaseWithFactory):
         # bug_sharing_policy can be set via the API.
         product = self.factory.makeProduct()
         owner = product.owner
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         webservice = webservice_for_person(
             product.owner, permission=OAuthPermission.WRITE_PRIVATE)
         response = self.patch(
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 99f36cd..70384af 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -1361,7 +1361,7 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
         # in use by artifacts or allowed by the project sharing policy.
         switch_dbuser('testadmin')
         product = self.factory.makeProduct()
-        self.factory.makeCommercialSubscription(product=product)
+        self.factory.makeCommercialSubscription(pillar=product)
         self.factory.makeAccessPolicy(product, InformationType.PROPRIETARY)
         naked_product = removeSecurityProxy(product)
         naked_product.bug_sharing_policy = BugSharingPolicy.PROPRIETARY
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 0ef41bb..b098cba 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -4691,26 +4691,32 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             }
         return fileupload
 
-    def makeCommercialSubscription(self, product, expired=False,
+    def makeCommercialSubscription(self, pillar, expired=False,
                                    voucher_id='new'):
-        """Create a commercial subscription for the given product."""
+        """Create a commercial subscription for the given pillar."""
+        if IProduct.providedBy(pillar):
+            find_kwargs = {"product": pillar}
+        elif IDistribution.providedBy(pillar):
+            find_kwargs = {"distribution": pillar}
+        else:
+            raise AssertionError("Unknown pillar: %r" % pillar)
         if IStore(CommercialSubscription).find(
-                CommercialSubscription, product=product).one() is not None:
+                CommercialSubscription, **find_kwargs).one() is not None:
             raise AssertionError(
-                "The product under test already has a CommercialSubscription.")
+                "The pillar under test already has a CommercialSubscription.")
         if expired:
             expiry = datetime.now(pytz.UTC) - timedelta(days=1)
         else:
             expiry = datetime.now(pytz.UTC) + timedelta(days=30)
         commercial_subscription = CommercialSubscription(
-            product=product,
+            pillar=pillar,
             date_starts=datetime.now(pytz.UTC) - timedelta(days=90),
             date_expires=expiry,
-            registrant=product.owner,
-            purchaser=product.owner,
+            registrant=pillar.owner,
+            purchaser=pillar.owner,
             sales_system_id=voucher_id,
             whiteboard='')
-        del get_property_cache(product).commercial_subscription
+        del get_property_cache(pillar).commercial_subscription
         return commercial_subscription
 
     def grantCommercialSubscription(self, person):