Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding of tracking code to fulfillment programmatically #529

Closed
chladog opened this issue Oct 27, 2020 · 5 comments
Closed

Adding of tracking code to fulfillment programmatically #529

chladog opened this issue Oct 27, 2020 · 5 comments

Comments

@chladog
Copy link
Contributor

chladog commented Oct 27, 2020

Is your feature request related to a problem? Please describe.
I'm using API of the delivery service. I create a "digital twin" packet via API and I receive a tracking code. This code is printed and sticked to the physical package - then either courier collects the package or we deliver it to delivery service collection point (physically being Shipped).
In the flow I need to have tracking code after order confirmation, but before shipping the goods (creation of fulfillment).

Describe the solution you'd like

  • similary to other strategies, new ShippingStrategy
  • this strategy should be hookable to any Order/Fulfillment state (EventBus)
  • having a hook for this strategy to be able to show/edit already generated tracking code in Fulfillment screen (maybe "Created" state for Fulfillment and Order state machines with immediate transition to currently initial states #510 if that would fire before showing Create fulfillment UI, or maybe introduce new event stream Custom (UI) Events (though I'm unsure how we would consume current state in UI side)
  • the strategy should be able to have access to Order and its contents => if API of delivery provider need info on dimensions/weight/value etc.
  • possibility to make AdminUI input read-only / disable manual updates by strategy config.
  • ShippingStrategy should be scoped to Order's shipping method. Implemented this way: Order shipping method having new config selection field Shipping Strategy (as strategy is dev-configured and shipping method admin-configured).
  • default ShippingStrategies for use out-of-the-box: ManualInput (the current behavior - manual input in Create Fulfillment screen, Disabled (disable/hide input for shipping methods that don't support tracking (Pick-on-site, providers without tracking features)

Describe alternatives you've considered
Currently I make API request on Pending to Shipped state transition.
But this has several problems:

  1. it doesn't exactly fit in the flow as user physically needs tracking code prior actual shipping (I know I can modify fulfillment(order) flow to something like Pending -> Packed -> Shipped. => but that adds unnecessary complexity to order processing) - this can be fixed by "Created" state for Fulfillment and Order state machines with immediate transition to currently initial states #510
  2. when user creates fulfillment the Tracking Code input is empty - it's confusing for users and I always overwrite whatever they write there by what I get from delivery provider API.
  3. I have unflexible checks for shippingmethodids to know which/whether API should be called
  4. In my current implementation I fetch tracking code - I update the fulfillment table col trackingCode and it's displayed in "Fulfillment widget" (obviously after extra admin screen refresh - very ux unfriendly) but the tracking code in History entries is empty string (even when the history entry was saved when already having tracking code, but I probably just missed something in my impl).
@jonyw4
Copy link
Contributor

jonyw4 commented Oct 27, 2020

I share the same issues that @chladog describes. This solution will be a nice feature to have in Vendure since every Order in my e-commerce needs a tracking code and everyone is generated by an external API, never is inputted by a user.

I had created a repo to maintain some packages to help in these shipping problems (like maintain a list of boxes with dimensions, add dimensional custom fields in product, a helper to calculate how many items you can put in a box, how many boxes will be necessary to create a box) and I use the same approach that @chladog wants in this issue. This solution will fix this UI / UX problems related to tracking code

@jonyw4
Copy link
Contributor

jonyw4 commented Oct 27, 2020

@chladog @michaelbromley Would be nice to have a field called like trackingCodeUrl (or maybe create an interface Tracking that have code and url as property) for carriers that save your labels in another website and even a way to save a generated label directly in Vendure using Assets Plugin

@chladog
Copy link
Contributor Author

chladog commented Oct 29, 2020

@chladog @michaelbromley Would be nice to have a field called like trackingCodeUrl (or maybe create an interface Tracking that have code and url as property) for carriers that save your labels in another website and even a way to save a generated label directly in Vendure using Assets Plugin

I see this as a case-based flow that should be implemented in new TrackingCodeStrategy. - the strategy (it being aware of Order and Fulfillment) would call provider's API creating a packet and return the tracking code, the trakcingcodeurl, or even barcode image url. Then your strategy code would handle saving it as fulfillment customfields (#525 + #464).

When I'm writing this it comes to my mind that this shipping-dependent code invocation feature does not have to be strictly related to tracking codes (e.g. provider supports creation of the packet via API, but not tracking feature).
Therefore maybe more general terminology would be suitable like ShippingStrategy / ShippingMethodStrategy, having trackingcode generation and saving in the docs as obvious use case. I updated inital post.

@michaelbromley michaelbromley added this to the v0.18.0 milestone Nov 16, 2020
@michaelbromley
Copy link
Member

Hi all,

I've been re-reading this thread and trying to get a clear picture of exactly what is needed here. Since this issue was opened we now have #510 implemented, so some of the issues may be a bit easier to deal with.

Let me try to state the problem succinctly and then propose a rough sketch of a solution. Then you can tell me whether that seems to cover your conceivable use-cases.

Problem

We need to be able to have control over how Fulfillments are created, so that we can programatically populate certain fields (tracking code, custom fields) of a Fulfillment without the need for manual input by an administrator.

Solution

  1. Create a new FulfillmentStrategy which can be thought of as a factory function for creating new Fulfillments. It would look something like this:
interface FulfillmentStrategy<Data = unknown> extends InjectableStrategy {
  readonly name: string;

  defineInputType(): DocumentNode;

  createFulfillment(
    ctx: RequestContext,
    order: Order,
    data: Data,
  ): Promise<Partial<Fulfillment>>;
}

This works in a similar way to the AuthenticationStrategy, where the defineInputType() method returns a GraphQL DocumentNode defining the type that is expected to be passed to the addFulfillmentToOrder mutation.

So for a purely programmatic strategy, the input can be empty.

Then, during the addFulfillmentToOrder mutation execution, the createFulfillment() method is called. This does whatever is needed (e.g. calls out to a 3rd party API) and returns a partial Fulfillment entity, with the relevant data (tracking code, custom fields) populated. This is then used to create the actual Fulfillment.

  1. As suggested, associate a ShippingMethod with a FulfillmentStrategy. This is how we know which strategy to use when executing addFulfillmentToOrder.

  2. Have some mechanism to tell the Admin UI what input controls (if any) are needed by the selected FulfillmentStrategy. I think this could be solved in a similar way to the args config e.g. as used by ShippingCalculator. That would even allow custom UI components to be used in the "create fulfillment" dialog. Could be really powerful.

Example

interface ShippoData {
  serviceType: string;
}

class ShippoFulfillmentStrategy implements FulfillmentStrategy<ShippoData> {
  readonly name = 'Shippo';

  private shippo;

  init() {
    this.shippo = require('shippo')('<YOUR_PRIVATE_KEY>');
  }

   defineInputType(): DocumentNode {
    return gql`
     input ShippoInput {
       serviceType: String!
     }
   `;
  }

  createFulfillment(
    ctx: RequestContext,
    order: Order,
    data: ShippoData,
  ): Promise<Partial<Fulfillment>> {
    const shipment = this.getShipmentFromOrder(order);
    const transaction = this.shippo.transaction.create({
	shipment,
	servicelevel_token: data.serviceType,
	carrier_account: "558c84bbc25a4f609f9ba02da9791fe4",
	label_file_type: "png"
    });
    return {
      trackingCode: transaction.tracking_number,
      customFields: {
         labelUrl: transaction.label_url,
      }
    };
  }

  private getShipmentFromOrder(order: Order): ShippoShipment {
   // ...
  }
}

@chladog
Copy link
Contributor Author

chladog commented Nov 25, 2020

I think your proposal fits my use-case.
If the fulfillment promp have such dynamic UI feature, maybe possiblity of editation of fulfillment is worth consideration.

Mind cancellation of fulfillment (#565) e.g. we cancel fulfillment so we want to call delivery api to cancel the packet.
I believe this can be handled by subscribing to FulfillmentStateTransition evetnbus and to "Cancelled" outside FulfillmentStrategy but would have fetch and check what shipping method is it.
It would be handy to have all code in one place, therefore extend FulfillmentStrategy with method onFulfillmentTransition(event: FulfillmentStateTransition) - passing only events of the fulfillment so we can react to any state changes right there seeing all other context of the strategy.

michaelbromley added a commit that referenced this issue Nov 26, 2020
Relates to #529. This commit introduces a new FulfillmentHandler class which can be used to define
how Fulfillments are created.

BREAKING CHANGE: The Fulfillment and ShippingMethod entities have new fields relating to
FulfillmentHandlers. This will require a DB migration, though no custom data migration will be
needed for this particular change.

The `addFulfillmentToOrder` mutation input has changed: the `method` & `trackingCode` fields
have been replaced by a `handler` field which accepts a FulfillmentHandler code, and any
expected arguments defined by that handler.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants