Skip to content

OBPIH-6804 Build shipment history to display shipment events#4940

Merged
awalkowiak merged 4 commits intodevelopfrom
OBPIH-6804
Nov 19, 2024
Merged

OBPIH-6804 Build shipment history to display shipment events#4940
awalkowiak merged 4 commits intodevelopfrom
OBPIH-6804

Conversation

@kchelstowski
Copy link
Collaborator

✨ Description of Change

A concise summary of what is being changed. Please provide enough context for reviewers to be able to understand the change and why it is necessary. If the issue/ticket already provides enough information, you can put "See ticket" as the description.

Link to GitHub issue or Jira ticket:

Description:


📷 Screenshots & Recordings (optional)

If this PR contains a UI change, consider adding one or more screenshots here or link to a screen recording to help reviewers visualize the change. Otherwise, you can remove this section.

@kchelstowski kchelstowski self-assigned this Nov 13, 2024
@github-actions github-actions bot added the domain: backend Changes or discussions relating to the backend server label Nov 13, 2024
@codecov
Copy link

codecov bot commented Nov 13, 2024

Codecov Report

Attention: Patch coverage is 0% with 49 lines in your changes missing coverage. Please review.

Please upload report for BASE (develop@92a2d7a). Learn more about missing BASE report.

Files with missing lines Patch % Lines
.../domain/org/pih/warehouse/shipping/Shipment.groovy 0.00% 26 Missing ⚠️
.../domain/org/pih/warehouse/receiving/Receipt.groovy 0.00% 7 Missing ⚠️
...ils-app/domain/org/pih/warehouse/core/Event.groovy 0.00% 6 Missing ⚠️
...ain/groovy/org/pih/warehouse/core/EventCode.groovy 0.00% 5 Missing ⚠️
...vy/org/pih/warehouse/core/ReferenceDocument.groovy 0.00% 4 Missing ⚠️
...n/groovy/org/pih/warehouse/core/HistoryItem.groovy 0.00% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

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
Copy link
Member

@ewaterman ewaterman Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member

@jmiranda jmiranda Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Member

@jmiranda jmiranda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about EventCode.CREATED.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come up with better name with interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kchelstowski @awalkowiak @ewaterman @alannadolny @drodzewicz Pick your favorite or add your own.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Historizable seems fine to me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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") }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@kchelstowski kchelstowski marked this pull request as ready for review November 15, 2024 14:19
@kchelstowski
Copy link
Collaborator Author

It's ready for final review.
An example of history object:

{
	"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"
		}
	}
}

interface Historizable {
List<HistoryItem> getHistory()

ReferenceDocument getReferenceDocument()
Copy link
Member

@jmiranda jmiranda Nov 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to be weird looking, but if you remove Line 720 and change this line to

referenceDocument: referenceDocument

it should work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@github-actions github-actions bot added the domain: l10n Changes or discussions relating to localization & Internationalization label Nov 18, 2024
@awalkowiak awalkowiak merged commit 96e3580 into develop Nov 19, 2024
@awalkowiak awalkowiak deleted the OBPIH-6804 branch November 19, 2024 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: backend Changes or discussions relating to the backend server domain: l10n Changes or discussions relating to localization & Internationalization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants