OBPIH-6804 Build shipment history to display shipment events#4940
OBPIH-6804 Build shipment history to display shipment events#4940awalkowiak merged 4 commits intodevelopfrom
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #4940 +/- ##
=========================================
Coverage ? 7.61%
Complexity ? 810
=========================================
Files ? 600
Lines ? 42243
Branches ? 10268
=========================================
Hits ? 3216
Misses ? 38564
Partials ? 463 ☔ View full report in Codecov by Sentry. |
| T parentObject | ||
|
|
||
| // FIXME: Introduced this property to be able to determine which history item is pointing to e.g. received event, which one to shipped event etc. | ||
| EventCode eventCode |
There was a problem hiding this comment.
So the idea is that this HistoryItem is generic enough to be applied to all features that need history? What if we need the history item to contain some feature-specific data?
Maybe it'd be more flexible if each feature was forced to implement their own ShipmentHistoryItem which extends HistoryItem then the domains would be like class Shipment implements Historiable<ShipmentHistoryItem>
Or maybe that's not needed and I'm overthinking it. I suppose my question is what does the future of this class look like?
There was a problem hiding this comment.
So the idea is that this HistoryItem is generic enough to be applied to all features that need history?
For now I think this is good enough.
But yes, I think you're right that we'll probably have different history features (stock history vs shipment history status history vs revision history) that might require a different history item format. Each history provider will need to conform to the data required by that feature.
Note: I changed shipment history to status history above because that seems closer to what we're trying to achieve here.
The HistoryItem class is just the DTO so that the History tab can collect, sort, iterate, and display the events in chronological order. We need to produce all of the history items for a parent object before we can display them due to the need to sort them ahead of time. It's possible that could be accomplished by what you're suggesting.
I'm ok with a generic History Item DTO for now because we don't actually know all of the different features and types we'll want to support. The HistoryItem should be pretty generic like above. And then we can subclass it if we need to include more specific information for other features.
What I don't want to do (and what I've done in the past) is to use a generic object (history item) with an associated object (shipment, receipt, etc) that can be used to deeply fetch very implementation specific data.
For example the parentObject probably should be defined as more specific data elements that are important to the particular history feature we're talking about. Because I don't want conditional logic in the History tab that says
foreach history item {
if (it.isShipment) {
"Value deep in parent object: " +
${it.parentObject.children.find { /* complex expression */ }.obscureProperty }
}
}
Instead we'll probably want the object to include a name, identifier, and URL that will be used to link to the resource in the application. This object should be its own class because we might have one for the Transaction and one for the other object (Shipment, Receipt, etc) for the same row.
What if we need the history item to contain some feature-specific data?
And yes, as we start to build this out more we can refactor to replace or extend the generic HistoryItem with feature-specific history items (status history, stock history vs revision history / audit trail).
jmiranda
left a comment
There was a problem hiding this comment.
Adding comments ahead of final review.
| associatedLocation: origin, | ||
| parentObject: this, | ||
| // FIXME: Don't know what event code suits the best for CREATED | ||
| eventCode: EventCode.SCHEDULED |
There was a problem hiding this comment.
Think about EventCode.CREATED.
There was a problem hiding this comment.
Adding EventCode.CREATED sounds good to me.
| * Shipment #2 Arrived at Customs on 5/5/2010: | ||
| *{eventDate: 5/5/2010, eventLocation: Customs, eventType: ARRIVED}*/ | ||
| class Event implements Comparable, Serializable { | ||
| class Event implements Comparable, Serializable, Historiable { |
There was a problem hiding this comment.
Come up with better name with interface.
There was a problem hiding this comment.
Some additional candidates for the interface name.
Auditable: we're going to use- Loggable: not really applicable
- Historical: does not follow -able convention
- Historiable: probably not what we're looking for
- Historizable: best so far
- Historicable: not great
- Historicalable: points finger at template
- Longitudinal: nah
- EventLoggable: meh
- EventFeed: hmm
- EventLog: interesting
- EventProvider: um, ok
- HistoryProvider: not bad
- HistoryEventProvider:
- HistoryFeed: i like Feed, but not sure it's interface-worthy name.
- HistoryFeedProvider: that's a little better
Here's Historizable in the wild.
https://github.com/micromata/hibernate-history/blob/master/src/main/java/de/micromata/hibernate/history/Historizable.java
I'm sure we can find others in the wild as well.
There was a problem hiding this comment.
@kchelstowski @awalkowiak @ewaterman @alannadolny @drodzewicz Pick your favorite or add your own.
There was a problem hiding this comment.
Historizable seems fine to me
There was a problem hiding this comment.
Same. I think if we end up building more history-related features we can switch from a generic Historizable to a more feature-specific naming convention around *HistoryProvider so like StockHistoryProvider, EventHistoryProvider, AuditHistoryProvider each defining getHistory() methods that return a list of HistoryItem objects subclasses for each feature.
| } | ||
|
|
||
| Set<Event> getCustomEvents() { | ||
| Set<Event> customEvents = events.findAll { it.eventType?.eventCode?.name()?.startsWith("CUSTOMS") } |
There was a problem hiding this comment.
Need to come up with a better approach by changing the event / event type.
| // FIXME: I didn't know what name suits the best for the location | ||
| Location associatedLocation | ||
|
|
||
| T parentObject |
There was a problem hiding this comment.
We should probably concert this into more specific data elements. For the current History tab I assume we'll need a link (id, name, URL) for the transaction as well as a link for the reference document (shipment, receipt, etc).
| Date date | ||
|
|
||
| // FIXME: I didn't know what name suits the best for the location | ||
| Location associatedLocation |
There was a problem hiding this comment.
Let's just call this location for now. Or facility if we want to be specific about what we're talking about.
| package org.pih.warehouse.core | ||
|
|
||
| class HistoryItem<T> { | ||
| String identifier |
There was a problem hiding this comment.
The details from the "reference document" probably move into the HistoryItemLink HistoryItemDetails ReferenceDocument class (id, name, URL) discussed below.
Note
A reference document is a generic term used in ERPs and WMSs to generically define the object that
I'm also fine with those properties living in the HistoryItem object but at some point (when we do the same for stock history) we'll probably have two of these objects (one for the transaction, one for the reference document) so we'll want to refactor at that point anyway.
We probably don't need an identifier here, just the URL (which requires the UUID) and label (which requires the identifier) used for displaying the anchor tag. So here's how i would probably model the generic history item for now.
HistoryItem
Date date
Location location
ReferenceDocument referenceDocument
ReferenceDocument (alternatives: HistoryItemLink, HistoryItemDetails)
// Required to render a link
String label // label to display with URL
String url // URL for the link
// These might be useful, but probably aren't necessary
String id // uuid assigned by database
String identifier // human readable identifier
String name // long human unreadable name
String description // extra description if necessary
The actual reference document class (Shipment, Receipt) should be responsible for generating these objects because they will know how to generate a link to the correct resource (i.e. stock movement details page)
<a href="${historyItem.referenceDocument.url}">${historyItem.referenceDocument.label}</a>
We're already doing this, but just wanted to show why it's necessary and suggest there might be room for improvement here. For example, the Shipment.getHistory() is pretty gnarly (my fault) so maybe there's a way to delegate some of that work to the dependent objects.
|
It's ready for final review. {
"1": {
"date": "2023-11-23T13:40:00Z",
"eventCode": {
"enumType": "org.pih.warehouse.core.EventCode",
"name": "SHIPPED"
},
"identifier": "842ZYG",
"location": {
"id": "0186",
"name": "Imres",
"description": null,
"locationNumber": "",
"locationGroup": null,
"parentLocation": null,
"locationType": {
"id": "4",
"name": "Supplier|fr:Fournisseurs",
"description": "Supplier",
"locationTypeCode": "SUPPLIER"
},
"sortOrder": 0,
"hasBinLocationSupport": false,
"hasPackingSupport": false,
"hasPartialReceivingSupport": false,
"hasCentralPurchasingEnabled": false,
"organizationName": "Imres",
"organizationCode": "US-0186",
"backgroundColor": "f79ee1",
"zoneName": null,
"zoneId": null,
"active": true,
"organization": {
"id": "0186",
"name": "Imres",
"description": null,
"code": "US-0186",
"dateCreated": "2008-07-06T00:00:00Z",
"lastUpdated": "2008-07-06T00:00:00Z",
"defaultLocation": null,
"partyType": {
"id": "ORG",
"name": "Organization",
"code": "ORG",
"partyTypeCode": "ORGANIZATION"
},
"roles": [
{
"id": "0186S",
"roleType": "ROLE_SUPPLIER",
"startDate": "1980-01-01T00:00:00Z",
"endDate": null
}
],
"sequences": {
"PURCHASE_ORDER_NUMBER": ""
}
},
"manager": null,
"address": {
"address": "Larserpoortweg 26",
"address2": "PO Box 214",
"city": "8200 AE Lelystad",
"country": "",
"description": "PO Ship-to: Direct to Site\r\nPayment terms: Net 30",
"postalCode": "10003",
"stateOrProvince": ""
},
"supportedActivities": [
"FULFILL_ORDER",
"EXTERNAL",
"SEND_STOCK"
]
},
"referenceDocument": {
"description": "RWPO000021 po16",
"id": "cab2b1fa8bf9e8c3018bfc6917fd0124",
"identifier": "842ZYG",
"label": "842ZYG",
"name": "Imres-Rwink.Pharm-23Nov2023-RWPO000021po16",
"url": "/stockMovement/show/cab2b1fa8bf9e8c3018bfc6917fd0124"
}
}
} |
9fd66d1 to
47d2a27
Compare
| interface Historizable { | ||
| List<HistoryItem> getHistory() | ||
|
|
||
| ReferenceDocument getReferenceDocument() |
There was a problem hiding this comment.
Interesting idea. However, I'm not sure I would put the reference document here since reference document is associated with the HistoryItem. In fact, each HistoryItem could have one or more ReferenceDocuments. For now we only need to support one. But it could be different based on the event type i.e. the Received and Partially Received events might have a Receipt reference document, while Shipped might have a Shipment.
I think it works in this case because Shipment is the reference document for each of these events but that's only because we don't have a Receipt details page that we want to show to the user for the Received and Partially Received events.
There was a problem hiding this comment.
There is a ReferenceDocument in HistoryItem, so I see what you're doing here. Every class should decide how it should be rendered as a HistoryItem, including how we might render it as a link. So this method is just telling the class to provide an implementation for both levels (for the row and for a link within the row).
There was a problem hiding this comment.
Yeah, for now I assumed that a "workflow" - receipt, shipment would have only one type of reference document, especially that we do not have a receipt view page.
| date: dateCreated, | ||
| location: origin, | ||
| eventCode: EventCode.CREATED, | ||
| referenceDocument: shipmentReferenceDocument |
There was a problem hiding this comment.
This is going to be weird looking, but if you remove Line 720 and change this line to
referenceDocument: referenceDocument
it should work.
There was a problem hiding this comment.
yeah it will, I think I kept that, because initially I had a few reference documents in the getHistory() method, before I decided to move each creator to a proper domain (Shipment, Receipt).
5b64d82 to
e30e2ff
Compare
e30e2ff to
def782b
Compare
✨ Description of Change
Link to GitHub issue or Jira ticket:
Description:
📷 Screenshots & Recordings (optional)