freightapis / ssc

Holds artifacts created by the SSC
https://www.freightapis.org/
Other
15 stars 3 forks source link

Provide a way to show/provide Additional Appointment Details #14

Open miller79 opened 1 month ago

miller79 commented 1 month ago

Intro

Currently there is no way to add additional appointment details to an appointment. The case that has come up multiple times is being able to differentiate appointments that are Live/Drop.

Goals

The following goals are expected for a Carrier calling the SSC API:

Suggestions

The following solutions could be used.

Appointments Contain Details

In this case, the fetch appointment would provide appointments with the details predefined. This means that when calling fetch appointments, it could potentially return multiple appointments to the same location for the same load with different appointment options. The following would be an example of a Fetch Available Appointments response:

{
    "status": "SUCCESS",
    "appointments": [
        {
            "id": "1",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T12:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "LIVE"
                    ]
                }
            ]
        },
        {
            "id": "2",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T12:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "DROP"
                    ]
                }
            ]
        },
        {
            "id": "3",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T13:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "LIVE"
                    ]
                }
            ]
        },
        {
            "id": "4",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T14:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "DROP"
                    ]
                }
            ]
        }
    ]
}

The fetch available appointments could also accept parameters to filter the appointment types to only specific types. A request to fetch available appointments could look like the following:

{
    "identifiers": {
        "primaryReferenceNumber": "LOAD1234",
        "stopSequenceNumber": 1
    },
    "options": [
        {
            "type": "loadingType",
            "values": [
                "LIVE"
            ]
        }
    ]
}

Pros

Cons

Appointments Details Provided During Scheduling

In this case, the fetch appointment would return the same results, but the client can add additional parameters to state which type of appointment they would like to pick. The following would be an example request to the Schedule Appointment endpoint:

{
    "identifiers": {
        "primaryReferenceNumber": "LOAD1234",
        "stopSequenceNumber": 1
    },
    "options": [
        {
            "type": "loadingType",
            "values": [
                "LIVE"
            ]
        }
    ]
}

Pros

Cons

Hybrid Approach

In this case, the fetch appointments will give all possible options for an appointment and the client can decide which options to select if important. The following could be the request to fetch available appointments to filter selection options if required:

{
    "identifiers": {
        "primaryReferenceNumber": "LOAD1234",
        "stopSequenceNumber": 1
    },
    "options": [
        {
            "type": "loadingType",
            "values": [
                "LIVE"
            ]
        }
    ]
}

The following could be the response to fetch available appointments.

{
    "status": "SUCCESS",
    "appointments": [
        {
            "id": "1",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T12:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "LIVE",
                        "DROP"
                    ]
                }
            ]
        },
        {
            "id": "2",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T13:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "LIVE"
                    ]
                }
            ]
        },
        {
            "id": "3",
            "appointmentType": "AUTOMATIC",
            "arrivalWindow": {
                "startDateTime": "2023-04-15T14:00:00.000-05:00",
                "duration": "PT1H"
            },
            "options": [
                {
                    "type": "loadingType",
                    "values": [
                        "DROP"
                    ]
                }
            ]
        }
    ]
}

The following could be the request to schedule appointments:

{
    "identifiers": {
        "primaryReferenceNumber": "LOAD1234",
        "stopSequenceNumber": 1
    },
    "preferredAppointment": {
        "id": "1234"
    },
    "options": [
        {
            "type": "loadingType",
            "values": [
                "LIVE"
            ]
        }
    ]
}

Pros

Cons

Conclusion

I would like to discuss these options with the community and get feedback on which options seem the most feasible not only for the current gap but also to help future proof the specification. The JSON structure shown above should support all 3 options and be backwards compatible.

A PR is attached that should address this issue.

E2openProductManagement commented 1 month ago

We second how critical this issue is for both shippers and carriers.

As a shipper, my appointment locations could have one or many docks. I have the ability to configure my docks as live-only, drop-only, or live and drop appointments. Our shippers are all configured based on their location's needs, a vast majority of them have a combination of the above across their networks.

miller79 commented 1 month ago

@E2openProductManagement I have created a PR with a suggested change that implements what was discussed above. Share it around and lets see if we can get some feedback to push this in as timely as possible.

kkuo commented 1 month ago

The SSC allows for the addition of custom properties not defined in the specification. This allows for a TMS to add additional identifiers or filter conditions to search on in the Identifiers object. The proposed Options object means that for scheduling, rescheduling, webhook - anywhere that we might need an Identifier object we will also need to add an Options object. The Options object seems like it is meant to be extended but we have already considered extensibility during the initial spec design and I'm not sure we need this.

On UF's API we have chosen to simply add an extra optional field for the loadingType. The loadingType is not included in the fetchAvailableAppointments response because the requester already knows the value for the loadingType since it provided it so there is no need to include it in the response. This avoids the need to change the spec on the response. We do modify the request in the scheduleAppointment request though.

If a client wants to get the results for DROP and LIVE, they will have to make two requests, one for each value for loadingType.

More so than schema changes I think we also need to consider allowable values. In our system we have several like LIVELOAD, DROPLOAD, PRESET, etc. So even if we standardize on schema, clients will have to deal with bespoke implementations.

To summarize:

Happy to discuss this more in person.

kkuo commented 1 week ago

@E2openProductManagement @miller79 Thanks for the discussion today.

In Anthony's PR here are a few additional comments and thoughts.

  1. I'd like to see if we can come up with a different name for the addition other than options. What about additionalAttributes?
  2. I'm considering the effort for clients to integrate with the new proposal. With appointmentItems having an array of loadingType values it changes how the client has to be coded. Right now once clients find the appointment they would like to book, they'd can turn around and put it into the booking request. With proposal the client has to choose one of the items in the array, it means the client has to modify the response before they can book an appointment. Scaling that out across a dozen or more clients interested in integration could add overhead for each.
//pseudo
// current client
AppointmentItem chosenAppointment = filter(response.appointmentTimes, a -> {
   return a.StartTime == "02:00";
});
bookAppointment(chosenAppointment);

// proposed
AppointmentItem chosenAppointment = filter(response.appointmentTimes, a -> {
   return a.StartTime == "02:00" && filter(a.options.loadingType, t -> {
      return t.value == "LIVE";
      });
});

// have to modify the item for the booking call.
chosenAppointment.options.loadingType = new ArrayList("LIVE");
bookAppointment(chosenAppointment);

This is just additional overhead that all clients integrating with E2Open has to do.

I'm wondering if E2Open can represent the appointments in a different way:

// instead of
{
   "appointments": [
      {
         "start": "02:00",
         "options": [
            {
               "name": "loadingType",
               "values": [
                  "LIVE",
                  "DROP"
                ],
            }
         ],
      }
   ]
}

// have one for each enumeration for loadingType
{
   "appointments": [
      {
         "start": "02:00",
         "options": [
            {
               "name": "loadingType",
               "values": [
                  "LIVE",
                ],
            }
         ],
      },
      {
         "start": "02:00",
         "options": [
            {
               "name": "loadingType",
               "values": [
                  "DROP",
                ],
            }
         ],
      }
   ]
}

// and even simpler - UF's approach
{
   "appointments": [
      {
         "start": "02:00",
         "loadingType": "LIVE"
      },
      {
         "start": "02:00",
         "loadingType": "DROP"
      }
   ]
}

The above is how Uber Freight added the loadingType to its SSC implementation.

Basically, trading off extra work on the server side for E2Open against work that each client has to do to integrate with E2Open.

  1. Instead of an array of optionItem where each item is a oneOf: optionValue, optionLoadingType, etc., could we consider a map of <string, object>? For example:
    options: {
    "loadingType": OptionLoadingType,
    "futureOption": OptionKeyValue,
    "option1": Option1Object,
    }
miller79 commented 2 days ago

@kkuo, here's my feedback on your suggestions:

  1. I'd like to see if we can come up with a different name for the addition other than options. What about additionalAttributes?

I don't have any strong opinions on the name so definitely looking forward to others opinions.

  1. I'm considering the effort for clients to integrate with the new proposal. With appointmentItems having an array of loadingType values it changes how the client has to be coded. Right now once clients find the appointment they would like to book, they'd can turn around and put it into the booking request. With proposal the client has to choose one of the items in the array, it means the client has to modify the response before they can book an appointment. Scaling that out across a dozen or more clients interested in integration could add overhead for each.

Not to speak for @E2openProductManagement but if I understood correctly, I believe we may be currently in front of the clients who are coding. Depending on how fast we can get this solution out, then the faster we can prevent clients from potentially having to re-code again since the gap was discovered early.

  1. Instead of an array of optionItem where each item is a oneOf: optionValue, optionLoadingType, etc., could we consider a map of <string, object>? For example:
options: {
  "loadingType": OptionLoadingType,
  "futureOption": OptionKeyValue,
  "option1": Option1Object,
}

I had considered this as an option as well, the only issue that came up is if someone wanted to add the "futureOption" or "option1" technically is an object change. Some languages, such as Java, if they are using auto generators or coding the object directly would need to rebuild their object for new fields. Using the type/value allows the object to be the same regardless of the values making it a bit more future proof. So I would vote to keep it as type and value to keep it more clear.