In the original module model (which I believe still will work) we created licenses attached to a line item (order item) when a licensable product was added to cart, and then activated it at checkout. This issue deals with the early-stage automation around unactivated licenses, which are essentially stubs:

1. Create a license when a licensable order item is saved with a licensable product (if the order item doesn't have an associated license yet) and attach the license to the order item.

2. When an order item is removed from the cart or deleted, deleted the associated license if it has not yet been activated.

3. When a cart-status order w/ licenses attached is canceled, delete all unactivated licenses associated with its order items (?)

I'm trying to think about if #3 will break anything.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Kazanir created an issue. See original summary.

Kazanir’s picture

Assigned: Kazanir » Unassigned
joachim’s picture

This should probably be done with event subscribers, as that's what Commerce is using internally for things that happen when an order changes state.

See OrderReceiptSubscriber, and CartEventSubscriber for relevant examples.

> 1. Create a license when a licensable order item is saved with a licensable product (if the order item doesn't have an associated license yet) and attach the license to the order item.

The event for this is CartEvents::CART_ENTITY_ADD.

> When an order item is removed from the cart or deleted, deleted the associated license if it has not yet been activated.

CartEvents::CART_ORDER_ITEM_REMOVE

> 3. When a cart-status order w/ licenses attached is canceled, delete all unactivated licenses associated with its order items (?)

commerce_order.cancel.post_transition

joachim’s picture

> In the original module model (which I believe still will work) we created licenses attached to a line item (order item) when a licensable product was added to cart, and then activated it at checkout

This is maybe a stupid question, but what's the reason for all the synchronizing on adding and removing to cart?

It seems to me it would be simpler to only create the license when an order reaches the 'completed' state, as there would then be only one event to react to.

joachim’s picture

Assigned: Unassigned » joachim

Assigning to myself, as I've made a start on the code for this.

joachim’s picture

Title: License creation/destruction logic » License creation/destruction logic in sync with cart/order lifecycle

I've hit a few bumps in the road:

1. For orders which are using a workflow with a fulfillment state, should the license be activated on reaching the 'fulfillment' state, or the 'completed' state? #2894796: clarify difference between 'fulfillment' and 'completed' order states
2. Commerce's order workflows use different transition names for the transitions that take the order to the 'completed' state. Because of this, and because State Machine fires events named for the transition rather than the state, we would need to subscribe to a lot of different events just for activating a license. #2894811: different workflows reach 'completed' state in different transition names, makes it hard to react to events, #2894810: Fire events named after the destination state rather than the transition name.

joachim’s picture

Status: Active » Needs work
FileSize
3.28 KB

Here's my work in progress. I think it's best to leave it here until #4 and #6 are resolved.

joachim’s picture

Ok so bojanz has replied at #2894796: clarify difference between 'fulfillment' and 'completed' order states, and says that we activate the license in the 'fulfillment' state, and also that in order to ensure the order gets to that state, we need to force the order to use one of the workflows that has it.

Questions:

- how does a product variation force an order to use a non-standard workflow?
- do we use the fulfillment workflow, or the fulfillment with validation?

chrisrockwell’s picture

[edit: hrm, I made a backwards interdiff] Just some clean-up on the work in progress.

do we use the fulfillment workflow, or the fulfillment with validation?

I think that's a site-specific question. The one I'm working on does not need validation before going into "Fulfillment" - but I can certainly see use-cases for having a review process (I think some hosting companies do this actually).

how does a product variation force an order to use a non-standard workflow?

By creating an order type (A) that uses the workflow, and then an order item type (B) that has Order Type (A), and then a Variation Type that uses order item type (B).

I'm just getting my feet wet here, but I feel like the best place to address this:

  • Good site builder documentation
  • Validation on the variation type Trait that is aware of the Order Item Type

Or I could be completely wrong :D.

chrisrockwell’s picture

FileSize
1.22 KB

A better interdiff

joachim’s picture

> - how does a product variation force an order to use a non-standard workflow?

I had a look at Commerce Shipping. It forces the order to use a non-standard workflow by telling the site builder in the README that they need to change workflow on the default order type... I don't think this is very robust!

joachim’s picture

See also #2894805: add a way to check content entities for bundle traits.

In the event subscriber methods, we need to check whether an order's items are for license products.

chrisrockwell’s picture

what's the reason for all the synchronizing on adding and removing to cart?

I'm poking around in here now. It seems like a license entity should be created in a subscriber to commerce_order.place.post_transition, which would _I think_ mean we're in the fulfillment state (or some place in-between place and fulfillment).

In a subscriber to commerce_order.fulfillment.post_transition the license is activated/synchronized and then StateItem::applyTransition is used to move the order to completed.

Does this sound right?

joachim’s picture

I've done some more work on this.

It's not complete, as we need to set the license ID on the order item, and getting a field onto the order item for that is #2879276: Licensable order item types & fields.

Also, my events are not currently triggering and I don't know why...

chrisrockwell’s picture

+++ b/commerce_license.services.yml
@@ -7,4 +7,9 @@ services:
+    arguments:
+      - '@entity_type.manager'

I believe this should be arguments: ['@entity_type.manager']. Might be why your event is subscribing.

joachim’s picture

Assigned: joachim » Unassigned

Unassigning myself, as I'm stuck on this.

chrisrockwell’s picture

+++ b/src/EventSubscriber/LicenseOrderSyncSubscriber.php
@@ -0,0 +1,139 @@
+      'commerce_order.fulfillment.post_transition' => ['onCartOrderFulfillment', -100],
+      'commerce_order.completed.post_transition' => ['onCartOrderComplete', -100],
+      'commerce_order.cancel.post_transition' => ['onCartOrderCancel', -100],

`fulfillment` should be `fulfill` ( I can update the patch later). I can't find an example of completed post transition, but that is not firing for me after "Fulfilling" the order.

joachim’s picture

Thanks for spotting that! Saved me probably half an hour of headdesking! :)

Here is an updated patch. This now creates a new license when I put an order through checkout! Hurrah!

However, I'm still really unclear on the whole picture of how a license's workflow syncs up with an order's workflow:

- I'm creating the license then activating it in the order's fulfillment state
-- how does a license plugin indicate that there is a problem with activating the license?
-- how do we indicate to the order that there is a problem?
-- how do we indicate to the order that the license was activated, and the order may now advance to the complete state? With my test orders, they stay in fulfillment state and the only way I can see to advance them is to set it manually in the order form. Surely with an order of only digital products that shouldn't be needed?

joachim’s picture

Updated patch.

I still have no idea about the questions in #18, but I am thinking I should commit this now so that licenses get created with orders and I can keep going with other issues (#2899878: add a method to License to set values from a configured plugin is next).

If there are parts of the synchronizing logic I've got wrong, that can be changed in follow-ups.

joachim’s picture

Status: Needs review » Fixed
FileSize
8.27 KB

Committing this so we have the basic functionality here, so I can move on and commit further patches. Filed #2900325: revisit and extend license creation/destruction logic in sync with cart/order lifecycle as a follow-on to resolve the questions from #18 and other matters such as synchronized licenses.

  • joachim committed 88af805 on 8.x-2.x
    Issue #2879263 by joachim, chrisrockwell: Added basic license creation/...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.