Adding SalesOpportunity entities to Elastic search

I am working on a CRM component for Moqui and would like to create a new (or add to an existing) data feed and index.
I have reviewed all of the Mantle data document files and have some questions.

  • What is the pattern for deciding to add relationships in the same dataDocument vs. defining a separate one? Some examples:
    • MantleParty, which includes several entities with a one to many relationship such as identifications, contactMechs, etc. The are defined like this:
      <fields fieldSeqId="20" fieldPath="contactMechs:contactMechId"/>
      In this case the entity relationship is to PartyContactMech
    • MantleCommunicationEvents includes parties:
      <fields fieldSeqId="30" fieldPath="parties:partyId"/>
      In this case the relationship is to CommunicationEventParty
    • The dataDocuments related to orders and invoices are separate, meaning for example that MantleOrderPart does not include order lines. They are defined in a separate data document.
  • When relationships are included the pattern appears to be that the primaryEntityName would have to be prefixed, for example:
    fieldPath="contactMechs:contactMechId would be PartyContactMech. Is this correct?
  • How does the plural spelling work? (adding ‘s’ or changing ‘y’ to ‘ies’ in the case of ‘parties’)

I was able to get a simple setup working for just the SalesOpportunity entity. This includes a new DataDocument.xml file in my component data folder with a separate index and data feed name.
But when I tried to include SalesOpportunityParty I get an error. Note that I used the CommunicationEvent example as it seemed like the same pattern.

Here is the error:
Relationship not found with name [parties] on entity [mantle.sales.opportunity.SalesOpportunity]

Here is the entity xml file to create the records:


    <!-- Sales Opportunity -->
    <dataDocuments dataDocumentId="MantleSalesOpportunity" indexName="mantle_sales_oppy" documentName="Sales Opportunity"
            primaryEntityName="mantle.sales.opportunity.SalesOpportunity" documentTitle="${salesOpportunityId}:${opportunityName}">
        <fields fieldSeqId="01" fieldPath="salesOpportunityId"/>
        <fields fieldSeqId="02" fieldPath="typeEnumId"/>
        <fields fieldSeqId="03" fieldPath="accountPartyId"/>
        <fields fieldSeqId="04" fieldPath="opportunityName" fieldNameAlias="name"/>
        <fields fieldSeqId="05" fieldPath="description"/>
        <fields fieldSeqId="08" fieldPath="nextStep"/>
        <fields fieldSeqId="09" fieldPath="estimatedAmount"/>
        <fields fieldSeqId="10" fieldPath="currencyUomId"/>
        <fields fieldSeqId="11" fieldPath="marketingCampaignId"/>
        <fields fieldSeqId="12" fieldPath="dataSourceId"/>
        <fields fieldSeqId="13" fieldPath="opportunityStageId"/>

        <!-- followed the pattern for MantleCommuncationEvent in MantleDocumentData.xml -->
        <fields fieldSeqId="20" fieldPath="parties:partyId"/>
        <fields fieldSeqId="21" fieldPath="parties:roleTypeId"/>
        <fields fieldSeqId="22" fieldPath="parties:role:description" fieldNameAlias="role"/>
        <fields fieldSeqId="23" fieldPath="parties:person:firstName"/>
        <fields fieldSeqId="24" fieldPath="parties:person:lastName"/>
        <fields fieldSeqId="25" fieldPath="parties:userAccounts:userId"/>
    </dataDocuments>

    <!-- =============== -->
    <!-- Index Data Feed -->
    <moqui.entity.feed.DataFeed dataFeedId="MantleSalesOppy" dataFeedTypeEnumId="DTFDTP_RT_PUSH"
            feedName="Mantle Sales Opportunity Data" feedReceiveServiceName="org.moqui.search.SearchServices.index#DataDocuments"
            feedDeleteServiceName="org.moqui.search.SearchServices.delete#DataDocument">
        <documents dataDocumentId="MantleSalesOpportunity"/>
    </moqui.entity.feed.DataFeed>
</entity-facade-xml>

I also tried using mantle_sales_opportunity as the index name (instead of mantle_sales_oppy) but that gave me an error saying the index had to be unique. I assume it collided with the data document id MantleSalesOpportunity?

The fieldPath field is a literal path from the primary entity (like SalesOpportunity) following defined relationships. There is nothing special about the relationship short names, or in other words the patterns you’ve noticed are simply by convention because the action relationship names and short names are explicitly defined in the entity.relationship element. Most of what you’ll see in existing DataDocuments is the relationship.@short-alias values, because those are much more convenient than the full relationship name (related-entity prefixed by title if one is specified with a hash/number-sign to separate).

For examples from the Party entity the ‘contactMechs’ relationship is the short-alias for the relationship to the PartyContactMech entity. It is a literal mechanism that implements the pattern you noticed.

Thanks for the direction @jonesde, I got past the error. Here is the new file that includes SalesOpportunity, SalesOpportunityParty, and SalesOpportunityCompetitor.

<dataDocuments dataDocumentId="MantleSalesOpportunity" indexName="mantle_sales_oppy" documentName="Sales Opportunity"
            primaryEntityName="mantle.sales.opportunity.SalesOpportunity" documentTitle="${salesOpportunityId}:${name}">
        <fields fieldSeqId="01" fieldPath="salesOpportunityId"/>
        <fields fieldSeqId="02" fieldPath="typeEnumId"/>
        <fields fieldSeqId="03" fieldPath="accountPartyId"/>
        <fields fieldSeqId="04" fieldPath="opportunityName" fieldNameAlias="name"/>
        <fields fieldSeqId="05" fieldPath="description"/>
        <fields fieldSeqId="08" fieldPath="nextStep"/>
        <fields fieldSeqId="09" fieldPath="estimatedAmount"/>
        <fields fieldSeqId="10" fieldPath="currencyUomId"/>
        <fields fieldSeqId="11" fieldPath="marketingCampaignId"/>
        <fields fieldSeqId="12" fieldPath="dataSourceId"/>
        <fields fieldSeqId="13" fieldPath="opportunityStageId"/>
        <!--SalesOpportunityParty -->
        <!-- SalesOpportunity entities are not defined with relationship aliases so use entity names and full path of relationship -->
        <fields fieldSeqId="20" fieldPath="SalesOpportunityParty:partyId"/>
        <fields fieldSeqId="21" fieldPath="SalesOpportunityParty:roleTypeId"/>
        <fields fieldSeqId="22" fieldPath="SalesOpportunityParty:RoleType:description" fieldNameAlias="role"/>
        <fields fieldSeqId="23" fieldPath="SalesOpportunityParty:Party:Person:firstName"/>
        <fields fieldSeqId="24" fieldPath="SalesOpportunityParty:Party:Person:lastName"/>
        <fields fieldSeqId="25" fieldPath="SalesOpportunityParty:Party:UserAccount:userId"/>
        <!--SalesOpportunityCompetitor -->
        <fields fieldSeqId="30" fieldPath="SalesOpportunityCompetitor:competitorPartyId"/>
        <fields fieldSeqId="31" fieldPath="SalesOpportunityCompetitor:positionEnumId"/>
        <fields fieldSeqId="32" fieldPath="SalesOpportunityCompetitor:strengths"/>
        <fields fieldSeqId="33" fieldPath="SalesOpportunityCompetitor:weaknesses"/>
    </dataDocuments>

I added estimatedCloseDate to the index. However when I display it in a screen it just shows the exact value in the data document (see below). How to format it for display purposes? I also need to use it to search a range, for example show all opportunities closing between a from and to date.

{
    "_entity": "mantle.sales.opportunity.SalesOpportunity",
    "salesOpportunityId": "100053",
    "accountPartyId": "100051",
    "name": "Big Deal",
    "description": "Deal with date and amount",
    "estimatedCloseDate": 1655182800000,
    "estimatedAmount": 1000.25,
    "currencyUomId": "USD",
    "SalesOpportunityParty": [
        {
            "partyId": "100001",
            "roleTypeId": "SalesRepresentative",
            "role": "Sales Representative",
            "firstName": "John",
            "lastName": "Doe"
        }
    ]
}

Here is how I am trying to display it:

            <field name="estimatedCloseDate">
                <header-field title="Close Date" show-order-by="true"><date-time type="date"/></header-field>
                <default-field><display/></default-field>
            </field>

Hi Vince,

Within the default-field tag, you could try using:
<display format="yyyy-MM-dd" />

Thanks @marwand for the suggestion. I tried it and here is the output. As you can see it just concatenates the format with the value in the data document.

yyyy-MM-dd1655182800000

Here is how I defined the field in my screen xml file:

<field name="estimatedCloseDate">
      <header-field title="Close Date" show-order-by="true"><date-time type="date"/></header-field>
      <default-field><display format="yyyy-MM-dd" /></default-field>
</field>

There seems to be something different in how dates are returned from searching data documents vs. an entity find. I am looking at another possible example for how to format this that I found in the Status History section of the OrderDetail screen. This example comes from an entity find, not a data doc but maybe a clue on how to format the date.

<label text="${ec.l10n.format(statusHistory.changedDate, null)}" type="strong" style="text-nowrap"/>

It looks like its stored as a timestamp and is not automatically parsed as Java Date, so maybe try:

<field name="estimatedCloseDate" from="new Date(estimatedCloseDate)">
      <header-field title="Close Date" show-order-by="true"><date-time type="date"/></header-field>
      <default-field><display format="yyyy-MM-dd" /></default-field>
</field>

I’m not sure if this is the best way to do it, though.

That throws a template error. I am sure there is a way to do this, there just aren’t any examples I can find that show using a date from a data document.

I’m assuming the template errors might be due to null values, but it’s difficult to tell without looking at the error logs. Either way, here’s a safer way to do it that handles a null estimatedCloseDate.

<field name="estimatedCloseDate">
      <header-field title="Close Date" show-order-by="true"><date-time type="date"/></header-field>
      <conditional-field condition="estimatedCloseDate">
         <label text="${ec.l10n.format(new Date(estimatedCloseDate), 'yyyy-MM-dd')}" />
      </conditional-field>
</field>

@marwand that worked, thank you!