Skip to Content
Technical Articles
Author's profile photo Jay Malla

SAP S/4 HANA custom Fiori Elements App for Stock Transfer Orders, Deliveries, Shipments – Tips and tricks

Hi Folks,

Over the past few years I have been developing SAP S/4 HANA custom Fiori apps in the OTC and PTP spaces – providing search and analytical views to sales orders, stock transfer orders (STOs), deliveries and shipments and the associated invoices, billing documents, etc.

In this blog, I would like to present some of the challenges and creative approaches I took to address them.  This blog will discuss the Stock Transfer Order application.

Here are some snapshots of the custom STO app:

Stock%20Transfer%20Order%20Custom%20Fiori%20App

Stock Transfer Order Custom Fiori App

 

You can see the outbound and inbound delivery and shipments with the associated inbound and associated outbound delivery and shipments.

 

And the drilldown to the details in an Object page:

 

Topic 1 – Purchase Order line item to delivery item join issue because of different data types

So we want to show Stock Transfer Orders, Deliveries and Shipments – join all those tables and present that to the user.  STOs are in the PO tables EKKO and EKPO.  Simple right?  But no…. slight issue.  You can’t simply joining the STO data to the deliveries because the purchase order line item is 5 digits and the delivery reference document field is 6 characters to the type does not match.

Let’s look at the main join in my CDS view:

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Author: Jay Malla
//Creation Date: June 2nd 2020
//Purpose: To combine stock transfer order lineitem and delivery information
//
//Changes:  12/17/2020 Adding the SOLI fields to the L level so that users running the report at L level can see the SOLI fields
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@AbapCatalog.sqlViewName: 'ZOTC_STOS'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Stock Transfer Orders'
@VDM.viewType: #CONSUMPTION

@UI.headerInfo.typeName: 'Result'
@UI.headerInfo.typeNamePlural: 'Results'

@OData.publish: true
define view ZOTC_C_STOCK_TRANSFER_ORD

  as select distinct from  I_PurchasingDocument   as purchaseOrderHeader

    // This view will create two records per order line item - one for header (order and line item combo)  and one for line (for joining with deliveries and shipments)
    left outer join ZOTC_I_PURCHASEORDER_LINE_HDR as _purchaseOrderItem        on purchaseOrderHeader.PurchasingDocument = _purchaseOrderItem.PurchasingDocument

    // This is to get the the aggregated shipped and open quantities at the Header order line item
    left outer join ZOTC_I_PO_LINE_SHIPMENT_AGGR  as _purchaseOrderItemShipped on  _purchaseOrderItem.PurchasingDocument            = _purchaseOrderItemShipped.ReferenceSDDocument
                                                                               and _purchaseOrderItem.PurchasingDocumentItemConvert = _purchaseOrderItemShipped.ReferenceSDDocumentItem
                                                                               and _purchaseOrderItem.linetype                      = 'H'


    // This is to get the address for the supplier plant
    left outer join  ZOTC_I_PLANT_ADDRESS as _SupplyingPlantCountry on purchaseOrderHeader.SupplyingPlant = _SupplyingPlantCountry.Plant

    // This is to get the address for the delivery plant
    left outer join  ZOTC_I_PLANT_ADDRESS as _DeliverToPlantCountry on _purchaseOrderItem.Plant = _DeliverToPlantCountry.Plant

    // This is to get the deliveries and tie them to th eorder line item with the line type is L for line level    
    left outer join  I_DeliveryDocumentItem as _DeliveryDocumentItem   on _purchaseOrderItem.PurchasingDocument            = _DeliveryDocumentItem.ReferenceSDDocument
                                                                      and _purchaseOrderItem.PurchasingDocumentItemConvert = _DeliveryDocumentItem.ReferenceSDDocumentItem
                                                                      and _purchaseOrderItem.linetype                      = 'L'

    //left outer join I_DeliveryDocument     as _DeliveryDocumentHeader on  _DeliveryDocumentItem.DeliveryDocument = _DeliveryDocumentHeader.DeliveryDocument
    left outer join I_DeliveryDocument as _DeliveryDocumentHeader on  _DeliveryDocumentItem.DeliveryDocument = _DeliveryDocumentHeader.DeliveryDocument



    left outer join ZOTC_I_SHIPMENTITEM as _shipmentItem     on _DeliveryDocumentHeader.DeliveryDocument = _shipmentItem.Delivery

    left outer join ZOTC_I_SHIPMENT     as _shipment         on _shipmentItem.ShipmentNumber = _shipment.ShipmentNumber


//  Need to correlate the outbound and inbound deliveries
  left outer join I_PurgDocSupplierConfirmation as _SupplierConfirmationOut on _purchaseOrderItem.PurchasingDocument = _SupplierConfirmationOut.PurchasingDocument
                                                                           and _purchaseOrderItem.PurchasingDocumentItem  = _SupplierConfirmationOut.PurchasingDocumentItem
                                                                           and _DeliveryDocumentItem.DeliveryDocument     = _SupplierConfirmationOut.ExternalReferenceDocumentID
                                                                           and _DeliveryDocumentItem.DeliveryDocumentItem = _SupplierConfirmationOut.DeliveryDocumentItem
                                                                           and _purchaseOrderItem.linetype                = 'L'


  left outer join ZOTC_I_SHIPMENTITEM as _AssociatedShipmentItemOut on _SupplierConfirmationOut.DeliveryDocument = _AssociatedShipmentItemOut.Delivery
                                                                   and _shipment.ShipmentNumber  <> ''
                                                                   and _purchaseOrderItem.linetype  = 'L'

  left outer join I_PurgDocSupplierConfirmation as _SupplierConfirmationInb on _purchaseOrderItem.PurchasingDocument = _SupplierConfirmationInb.PurchasingDocument
                                                                           and _purchaseOrderItem.PurchasingDocumentItem  = _SupplierConfirmationInb.PurchasingDocumentItem
                                                                           and _DeliveryDocumentItem.DeliveryDocument     = _SupplierConfirmationInb.DeliveryDocument
                                                                           and _DeliveryDocumentItem.DeliveryDocumentItem = _SupplierConfirmationInb.DeliveryDocumentItem
                                                                           and _purchaseOrderItem.linetype                = 'L'


  left outer join ZOTC_I_SHIPMENTITEM as _AssociatedShipmentItemInb on _SupplierConfirmationInb.ExternalReferenceDocumentID = _AssociatedShipmentItemInb.Delivery
                                                                   and _shipment.ShipmentNumber  <> ''
                                                                   and _purchaseOrderItem.linetype  = 'L'

 

Notice how I did the join from the STO in the purchase order item table to the delivery item table:

    left outer join  I_DeliveryDocumentItem as _DeliveryDocumentItem   on _purchaseOrderItem.PurchasingDocument            = _DeliveryDocumentItem.ReferenceSDDocument
                                                                      and _purchaseOrderItem.PurchasingDocumentItemConvert = _DeliveryDocumentItem.ReferenceSDDocumentItem
                                                                      and _purchaseOrderItem.linetype                      = 'L'

 

Notice that I had to user the converted PurchasingDocumentItem to the DeliveryDocumentItem ReferenceSDDocumentItem:

_purchaseOrderItem.PurchasingDocumentItemConvert = _DeliveryDocumentItem.ReferenceSDDocumentItem

 

So in order to have this join to work, I need to convert the PurchasingDocumentItem from 5 digits to 6 digits.  I do by appending one leading zero:

concat('0', ( cast(PurchasingDocumentItem as abap.char( 5 )))) as PurchasingDocumentItemConvert

 

Topic 2 – aggregating STO line item delivery and shipment count for outbound and inbound shipments.

We wanted a way to aggregate which delivery items are PGId and which are not…

Note that if ItemGeneralIncompletionStatus, GoodsMovementStatus, ItemGdsMvtIncompletionSts area all ‘C’ then this is PGId.

Also, we make a distinction between Outbound and Inbound shipped quantities.  If SDDocumentCategory is 7 then this is for an Inbound Shipment and if it’s J then this is for Outbound shipment):

//---------------------------------------------------------------------*
// Author        : Jay Malla                                           *
// Creation Date : June 12th 2020                                      *
// Description   : Purchase Order Lineitem aggregation for shipped qty *
//---------------------------------------------------------------------*

@AbapCatalog.sqlViewName: 'ZOTC_PODELVDAN'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purchase Order Line Shipment Aggregation'

define view ZOTC_I_PO_LINE_SHIPMENT_AGGR
  as select from ZOTC_I_DELIVERYITEM_ANALYTICS
{

  key ReferenceSDDocument,
  key ReferenceSDDocumentItem,
  key ReferenceSDDocumentCategory,


      sum(ActualDeliveryQuantity)    as ShippedQuantityForSOLine,

   
      sum(case SDDocumentCategory
               when '7' then ActualDeliveryQuantity
               end)    as InbShippedQuantityForSOLine,

      sum(case SDDocumentCategory
               when 'J' then ActualDeliveryQuantity
               end)    as OutShippedQuantityForSOLine,

      ItemGeneralIncompletionStatus  as SOLIGeneralIncompletionStatus,
      ItemDeliveryIncompletionStatus as SOLIDeliveryIncompletionStatus,
      GoodsMovementStatus            as SOLIGoodsMovementStatus,
      ItemGdsMvtIncompletionSts      as SOLIGdsMvtIncompletionSts
      
}
where
  (
        ReferenceSDDocumentCategory   <> 'C'   //not C - not sales order
    and ItemGeneralIncompletionStatus = 'C'
    and GoodsMovementStatus           = 'C'
    and ItemGdsMvtIncompletionSts     = 'C'
  )
group by
  ReferenceSDDocument,
  ReferenceSDDocumentItem,
  ReferenceSDDocumentCategory,
  //SDProcessStatus,
  ItemGeneralIncompletionStatus,
  ItemDeliveryIncompletionStatus,
  GoodsMovementStatus,
  ItemGdsMvtIncompletionSts

 

Note that I have a tweaked version of I_DeliveryItem_Analytics with the I_SalesDocumentItem commented out.

//---------------------------------------------------------------------*
// Author        : Jay Malla                                           *
// Creation Date : June 12th 2020                                      *
// Description   : Delivery Lineitem analytics used by STO & Returns   *
// Note that this does not have the join to the I_SalesDocumentItem    *
// since we need these deliveries to work with POs and Returns         *
//---------------------------------------------------------------------*

@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.usageType.dataClass: #MIXED
@ObjectModel.usageType.serviceQuality: #D
@ObjectModel.usageType.sizeCategory: #XL
@EndUserText.label: 'Core Analytics View for Delivery Items'
@VDM.viewType: #COMPOSITE
@AccessControl.authorizationCheck:#CHECK
@AccessControl.personalData.blocking: #BLOCKED_DATA_EXCLUDED
@AbapCatalog.sqlViewName: 'ZOTCDELDOCITMANA'

define view ZOTC_I_DELIVERYITEM_ANALYTICS

as select from  I_DeliveryDocumentItem as DDI
//inner join I_SalesDocumentItem as SDI on DDI.ReferenceSDDocument = SDI.SalesDocument
//                                     and DDI.ReferenceSDDocumentItem = SDI.SalesDocumentItem

association [0..1] to I_Currency  as _StatisticsCurrency on  $projection.StatisticsCurrency = _StatisticsCurrency.Currency
{
// Key
  @ObjectModel.foreignKey.association: '_DeliveryDocument'
  key DDI.DeliveryDocument,
  key DDI.DeliveryDocumentItem,

//Dimensions

  //category
  @ObjectModel.foreignKey.association: '_SDDocumentCategory'
  DDI.SDDocumentCategory,
  @ObjectModel.foreignKey.association: '_ItemCategory'
  DeliveryDocumentItemCategory,
  @ObjectModel.foreignKey.association: '_SalesDocumentItemType'
  DDI.SalesDocumentItemType,

  //Admin
  DDI.CreatedByUser,
  @Semantics.systemDate.createdAt: true
  DDI.CreationDate,
  DDI.CreationTime,
  @Semantics.systemDate.lastChangedAt: true
  DDI.LastChangeDate,

  //Organization  (Team View,..)
  @ObjectModel.foreignKey.association: '_SalesOrganization'
  _DeliveryDocument.SalesOrganization,
  @ObjectModel.foreignKey.association: '_DistributionChannel'
  DDI.DistributionChannel,
  @ObjectModel.foreignKey.association: '_Division'
  DDI.Division,
  @ObjectModel.foreignKey.association: '_SalesGroup'
  SalesGroup,
  @ObjectModel.foreignKey.association: '_SalesOffice'
  SalesOffice,
  DepartmentClassificationByCust,

  //Product
  @ObjectModel.foreignKey.association: '_Material'
  DDI.Material,
  DDI.MaterialByCustomer,
  @ObjectModel.foreignKey.association: '_OriginallyRequestedMaterial'
  DDI.OriginallyRequestedMaterial,
  DDI.InternationalArticleNumber,
  DDI.Batch,
  DDI.BatchClassification,
  DDI.BatchBySupplier,
  DDI.MaterialIsIntBatchManaged,
  DDI.MaterialIsBatchManaged,
  @ObjectModel.foreignKey.association: '_MaterialGroup'
  DDI.MaterialGroup,
  @ObjectModel.foreignKey.association: '_MaterialFreightGroup'
  DDI.MaterialFreightGroup,
  @ObjectModel.foreignKey.association: '_AdditionalMaterialGroup1'
  DDI.AdditionalMaterialGroup1,
  @ObjectModel.foreignKey.association: '_AdditionalMaterialGroup2'
  DDI.AdditionalMaterialGroup2,
  @ObjectModel.foreignKey.association: '_AdditionalMaterialGroup3'
  DDI.AdditionalMaterialGroup3,
  @ObjectModel.foreignKey.association: '_AdditionalMaterialGroup4'
  DDI.AdditionalMaterialGroup4,
  @ObjectModel.foreignKey.association: '_AdditionalMaterialGroup5'
  DDI.AdditionalMaterialGroup5,
  @ObjectModel.foreignKey.association: '_Plant'
  DDI.Plant,
  DDI._Plant.PlantName,
  @ObjectModel.foreignKey.association: '_StorageLocation'
  DDI.StorageLocation,
  DDI._StorageLocation.StorageLocationName,
  @ObjectModel.foreignKey.association: '_Warehouse'
  DDI.Warehouse,
  StorageBin,
  StorageType,
  @ObjectModel.foreignKey.association: '_InventorySpecialStockType'
  DDI.InventorySpecialStockType,
  ShelfLifeExpirationDate,
  NumberOfSerialNumbers,
  DDI.ProductConfiguration,
  DDI.ProductHierarchyNode,
  ManufactureDate,

  //delivery
  DeliveryDocumentItemText,
  DDI.HigherLevelItem,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'DeliveryQuantityUnit'
  ActualDeliveryQuantity,
  DDI.QuantityIsFixed,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'DeliveryQuantityUnit'
  OriginalDeliveryQuantity,
  @Semantics.unitOfMeasure: true
  @ObjectModel.foreignKey.association: '_DeliveryQuantityUnit'
  DeliveryQuantityUnit,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'BaseUnit'
  ActualDeliveredQtyInBaseUnit,
//   @Semantics.unitOfMeasure: true                             //See Below in //Measures
//   @ObjectModel.foreignKey.association: '_BaseUnit'
//   DDI.BaseUnit,
  DeliveryToBaseQuantityDnmntr,
  DeliveryToBaseQuantityNmrtr,
  ProductAvailabilityDate,
  ProductAvailabilityTime,
  DDI.DeliveryGroup,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'ItemWeightUnit'
  DDI.ItemGrossWeight,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'ItemWeightUnit'
  DDI.ItemNetWeight,
  @Semantics.unitOfMeasure: true
  @ObjectModel.foreignKey.association: '_ItemWeightUnit'
  DDI.ItemWeightUnit,
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'ItemVolumeUnit'
  DDI.ItemVolume,
  @Semantics.unitOfMeasure: true
  @ObjectModel.foreignKey.association: '_ItemVolumeUnit'
  DDI.ItemVolumeUnit,
  InspectionLot,
  InspectionPartialLot,
  DDI.PartialDeliveryIsAllowed,
  DDI.UnlimitedOverdeliveryIsAllowed,
  DDI.OverdelivTolrtdLmtRatioInPct,
  DDI.UnderdelivTolrtdLmtRatioInPct,
  WarehouseStorageBin,
  @ObjectModel.foreignKey.association: '_BOMExplosion'
  BOMExplosion,
  CustEngineeringChgStatus,
  @ObjectModel.foreignKey.association: '_WarehouseStagingArea'
  WarehouseStagingArea,
  WarehouseStockCategory,
  IssgOrRcvgStockCategory,
  IssgOrRcvgMaterial,
  IssuingOrReceivingPlant,
  IssuingOrReceivingStorageLoc,
  IssgOrRcvgBatch,
  IssgOrRcvgValuationType,
  IssgOrRcvgSpclStockInd,
  PrimaryPostingSwitch,
  StockType,
  IsNotGoodsMovementsRelevant,
  GLAccount,
  GoodsMovementReasonCode,
  SubsequentMovementType,
  IsCompletelyDelivered,
  AlternateProductNumber,

  //pick pack load
  @ObjectModel.foreignKey.association: '_PickingControl'
  PickingControl,
  @ObjectModel.foreignKey.association: '_LoadingGroup'
  LoadingGroup,
  @ObjectModel.foreignKey.association: '_GoodsMovementType'
  GoodsMovementType,

  //shipping
  @ObjectModel.foreignKey.association: '_TransportationGroup'
  TransportationGroup,
  ReceivingPoint,
  FixedShipgProcgDurationInDays,
  VarblShipgProcgDurationInDays,
  ProofOfDeliveryRelevanceCode,

  //billing
  @ObjectModel.foreignKey.association: '_ItemIsBillingRelevant'
  DDI.ItemIsBillingRelevant,
  @ObjectModel.foreignKey.association: '_ItemBillingBlockReason'
  DDI.ItemBillingBlockReason,
  @ObjectModel.foreignKey.association: '_PaymentGuaranteeForm'
  PaymentGuaranteeForm,
  IntercompanyBillingStatus,

  //accounting
  @ObjectModel.foreignKey.association: '_BusinessArea'
  DDI.BusinessArea,
  @ObjectModel.foreignKey.association: '_ControllingArea'
  ControllingArea,
  DDI.ProfitabilitySegment,
  DDI.ProfitCenter,
  --@ObjectModel.foreignKey.association: '_InventoryValuationType' -- AT 24.8.16 commented due to false positive ATC check POC_ANNOTA, OMFK
  InventoryValuationType,
  IsSeparateValuation,
  ConsumptionPosting,
  @ObjectModel.foreignKey.association: '_OrderID'
  DDI.OrderID,
  OrderItem,
  CostCenter,

  //reference
  @ObjectModel.foreignKey.association: '_ReferenceSDDocument'
  DDI.ReferenceSDDocument,
  @ObjectModel.foreignKey.association: '_ReferenceSalesDocumentItem'
  DDI.ReferenceSDDocumentItem,
  @ObjectModel.foreignKey.association: '_ReferenceSDDocumentCategory'
  DDI.ReferenceSDDocumentCategory,
  @ObjectModel.foreignKey.association: '_LogicalSystem'
  ReferenceDocumentLogicalSystem,

  //sales
  @ObjectModel.foreignKey.association: '_AdditionalCustomerGroup1'
  AdditionalCustomerGroup1,
  @ObjectModel.foreignKey.association: '_AdditionalCustomerGroup2'
  AdditionalCustomerGroup2,
  @ObjectModel.foreignKey.association: '_AdditionalCustomerGroup3'
  AdditionalCustomerGroup3,
  @ObjectModel.foreignKey.association: '_AdditionalCustomerGroup4'
  AdditionalCustomerGroup4,
  @ObjectModel.foreignKey.association: '_AdditionalCustomerGroup5'
  AdditionalCustomerGroup5,
  DDI.RetailPromotion,
  StatisticsDate,

  //status
  @ObjectModel.foreignKey.association: '_SDProcessStatus'
  DDI.SDProcessStatus,
  @ObjectModel.foreignKey.association: '_PickingConfirmationStatus'
  PickingConfirmationStatus,
  @ObjectModel.foreignKey.association: '_PickingStatus'
  PickingStatus,
  @ObjectModel.foreignKey.association: '_WarehouseActivityStatus'
  WarehouseActivityStatus,
  @ObjectModel.foreignKey.association: '_PackingStatus'
  PackingStatus,
  @ObjectModel.foreignKey.association: '_GoodsMovementStatus'
  GoodsMovementStatus,
  @ObjectModel.foreignKey.association: '_DeliveryRelatedBillingStatus'
  DeliveryRelatedBillingStatus,
  @ObjectModel.foreignKey.association: '_ProofOfDeliveryStatus'
  ProofOfDeliveryStatus,
  @ObjectModel.foreignKey.association: '_ItemGeneralIncompletionStatus'
  DDI.ItemGeneralIncompletionStatus,
  @ObjectModel.foreignKey.association: '_ItemDelivIncompletionSts'
  DDI.ItemDeliveryIncompletionStatus,
  @ObjectModel.foreignKey.association: '_ItemPickingIncompletionStatus'
  ItemPickingIncompletionStatus,
  @ObjectModel.foreignKey.association: '_ItemGdsMvtIncompletionSts'
  ItemGdsMvtIncompletionSts,
  @ObjectModel.foreignKey.association: '_ItemPackingIncompletionStatus'
  ItemPackingIncompletionStatus,
  @ObjectModel.foreignKey.association: '_ItemBillingIncompletionStatus'
  DDI.ItemBillingIncompletionStatus,

  //Customer
  @ObjectModel.foreignKey.association: '_SoldToParty'
  _DeliveryDocument.SoldToParty,
  cast(_DeliveryDocument._SoldToParty.CustomerName as vdm_sold_to_name) as SoldToPartyName,
  @ObjectModel.foreignKey.association: '_CustomerGroup'
  _DeliveryDocument.CustomerGroup,
  _DeliveryDocument._SoldToParty.CustomerClassification   as SoldToPartyClassification,

  //Geographics
  @ObjectModel.foreignKey.association: '_SalesDistrict'
  _DeliveryDocument.SalesDistrict,

  // Misc Attributes (for DCL, I_CalenderDate-join)
  @ObjectModel.foreignKey.association: '_ShippingPoint'
  _DeliveryDocument.ShippingPoint,        -- DCL Check for DeliveryDocument
  _DeliveryDocument.BillingDocumentDate,  -- For Join with I_CalendarDate and CurrencyConversion


//Measures
//  @DefaultAggregation: #SUM
//  @Semantics.amount.currencyCode: 'TransactionCurrency'
//  case when SDI.IsReturnsItem = '' and
//           ( DDI.DeliveryRelatedBillingStatus = 'A' or
//             DDI.DeliveryRelatedBillingStatus = 'B'
//           )
//
//  then
//    case when SDI.RequestedQuantityInBaseUnit > 0
//    then
//    cast(division(abs(SDI.NetAmount) * DDI.ActualDeliveredQtyInBaseUnit, SDI.RequestedQuantityInBaseUnit,3 ) as opn_dlv_for_inv_net_amt)
//    else cast(0 as opn_dlv_for_inv_net_amt )
//    end
//  end
//  as OpnOutbDelivsForInvcNetAmt,

//  @Semantics.currencyCode: true
//  @ObjectModel.foreignKey.association: '_TransactionCurrency'
//  SDI.TransactionCurrency,

  @Semantics.currencyCode: true
  @ObjectModel.foreignKey.association: '_StatisticsCurrency'
  _DeliveryDocument._SalesOrganization.SalesOrganizationCurrency as StatisticsCurrency, --Note: take statistical currency from central tvko (versus document)

  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'BaseUnit'
  case when 
             ( DDI.DeliveryRelatedBillingStatus = 'A' or
               DDI.DeliveryRelatedBillingStatus = 'B'
             )
  then
    cast( ActualDeliveredQtyInBaseUnit as opn_dlv_for_inv_qty)
  end
  as OpnOutbDelivsForInvcQty,

  @Semantics.unitOfMeasure: true
  @ObjectModel.foreignKey.association: '_BaseUnit'
  DDI.BaseUnit,

//Associations
  @ObjectModel.association.type: [#TO_COMPOSITION_PARENT, #TO_COMPOSITION_ROOT]
  DDI._DeliveryDocument,
  DDI._Partner,
  DDi._SDDocumentCategory,
  DDI._ItemCategory,
  DDI._SalesDocumentItemType,
  DDI._CreatedByUser,
  DDI._DistributionChannel,
  DDI._Division,
  DDI._SalesGroup,
  DDI._SalesOffice,
  DDI._Material,
  DDI._OriginallyRequestedMaterial,
  DDI._ProductHierarchyNode,
  DDI._MaterialGroup,
  DDI._MaterialFreightGroup,
  DDI._AdditionalMaterialGroup1,
  DDI._AdditionalMaterialGroup2,
  DDI._AdditionalMaterialGroup3,
  DDI._AdditionalMaterialGroup4,
  DDI._AdditionalMaterialGroup5,
  DDI._Plant,
  DDI._Warehouse,
  DDI._StorageLocation,
  DDI._InventorySpecialStockType,
  DDI._DeliveryQuantityUnit,
  DDI._BaseUnit,
  DDI._ItemWeightUnit,
  DDI._ItemVolumeUnit,
  DDI._BOMExplosion,
  DDI._WarehouseStagingArea,
  DDI._PickingControl,
  DDI._LoadingGroup,
  DDI._GoodsMovementType,
  DDI._TransportationGroup,
  DDI._ItemIsBillingRelevant,
  DDI._ItemBillingBlockReason,
  DDI._PaymentGuaranteeForm,
  DDI._BusinessArea,
  DDI._ControllingArea,
  DDI._ProfitCenter,
  DDI._InventoryValuationType,
  DDI._OrderID,
  DDI._CostCenter,
  DDI._ReferenceSDDocument,
  DDI._ReferenceSalesDocumentItem,
  DDI._ReferenceSDDocumentCategory,
  DDI._LogicalSystem,
  DDI._AdditionalCustomerGroup1,
  DDI._AdditionalCustomerGroup2,
  DDI._AdditionalCustomerGroup3,
  DDI._AdditionalCustomerGroup4,
  DDI._AdditionalCustomerGroup5,
  DDI._SDProcessStatus,
  DDI._PickingConfirmationStatus,
  DDI._PickingStatus,
  DDI._WarehouseActivityStatus,
  DDI._PackingStatus,
  DDI._GoodsMovementStatus,
  DDI._DeliveryRelatedBillingStatus,
  DDI._ProofOfDeliveryStatus,
  DDI._ItemGeneralIncompletionStatus,
  DDI._ItemDelivIncompletionSts,
  DDI._ItemPickingIncompletionStatus,
  DDI._ItemGdsMvtIncompletionSts,
  DDI._ItemPackingIncompletionStatus,
  DDI._ItemBillingIncompletionStatus,
// some further associations (historical reasons, don't use them !)
  DDI._DeliveryDocument._SalesOrganization,
  DDI._DeliveryDocument._CustomerGroup,
  DDI._DeliveryDocument._SalesDistrict,
  DDI._DeliveryDocument._ShippingPoint,
  DDI._DeliveryDocument._SoldToParty,
  DDI._DeliveryDocument._SoldToParty._CustomerClassification
  //_StatisticsCurrency,
  //SDI._TransactionCurrency
}
//where SDI.StatisticalValueControl = ''


 

Topic 3 – User status for the line item.  This is to capture a simple status for the line:

check out the logic for determining PGId Delivery Shipment, Zero picked shipment, Shipment not PGId yet, Rejected, and Open:

      @Consumption.valueHelp: '_UserQueryStatus'
      @ObjectModel.text.element: ['User Query']
      @UI.fieldGroup: [ { qualifier: 'OrderHeader', position: 80 } ]
      @EndUserText.label: 'User Query'
      case 
        when _purchaseOrderItem.linetype = 'L' and 
             _shipmentItem.ShipmentNumber <> '' and 
             _DeliveryDocumentItem.GoodsMovementStatus = 'C' and 
             _DeliveryDocumentItem.ItemGeneralIncompletionStatus = 'C' and 
             _DeliveryDocumentItem.ItemDeliveryIncompletionStatus = 'C' and
             ActualDeliveryQuantity > 0 then 'P' //PGId Delivery shipment             
        when _purchaseOrderItem.linetype = 'L' and 
             _shipmentItem.ShipmentNumber <> '' and 
             _DeliveryDocumentItem.GoodsMovementStatus = 'C' and 
             _DeliveryDocumentItem.ItemGeneralIncompletionStatus = 'C' and 
             _DeliveryDocumentItem.ItemDeliveryIncompletionStatus = 'C' and
             ActualDeliveryQuantity = 0 then 'Z' //Zero picked
        when _purchaseOrderItem.linetype = 'L' and 
             _shipmentItem.ShipmentNumber <> '' then 'S'  //Shipment not PGId yet
        when _purchaseOrderItem.linetype = 'H' and 
             purchaseOrderHeader.PurchasingProcessingStatus = '08' then 'R'  //Rejected sales order line item
        when _purchaseOrderItem.linetype = 'H' and 
               (_purchaseOrderItem.OrderQuantity = _purchaseOrderItemShipped.OutShippedQuantityForSOLine) and
               (_purchaseOrderItem.OrderQuantity = _purchaseOrderItemShipped.InbShippedQuantityForSOLine) then 'C'  //Completed - Shipped quantity = order quantity
        else 'O' //Let's treat all these others as open
    end as UserQueryStatus,

 

Topic 4 – Outbound and Inbound open quantity logic:

      @UI.fieldGroup: [ { qualifier: 'OrderItem', position: 10 } ]
      @EndUserText.label: 'ORLI Outbound Open Quantity'
      @Semantics.quantity.unitOfMeasure: 'OrderQuantityUnit'
      case 
        when _purchaseOrderItem.linetype = 'H' then 
                case 
                    when (_purchaseOrderItemShipped.OutShippedQuantityForSOLine > 0)
                        then (_purchaseOrderItem.OrderQuantity - _purchaseOrderItemShipped.OutShippedQuantityForSOLine)
                    else
                        _purchaseOrderItem.OrderQuantity
                    end
        when _purchaseOrderItem.linetype = 'L' then 
                case 
                    when (_purchaseOrderItemShipped.OutShippedQuantityForSOLine > 0)
                        then (_purchaseOrderItem.OrderQuantity - _purchaseOrderItemShipped.OutShippedQuantityForSOLine)
                    else
                        _purchaseOrderItem.OrderQuantity
                    end
      end as SOLIOutboundOpenQuantity,

      @UI.fieldGroup: [ { qualifier: 'OrderItem', position: 10 } ]
      @EndUserText.label: 'ORLI Inbound Shipped Quantity'
      @Semantics.quantity.unitOfMeasure: 'OrderQuantityUnit'
      _purchaseOrderItemShipped.InbShippedQuantityForSOLine, 
      
      @UI.fieldGroup: [ { qualifier: 'OrderItem', position: 10 } ]
      @EndUserText.label: 'ORLI Inbound Open Quantity'
      @Semantics.quantity.unitOfMeasure: 'OrderQuantityUnit'
      case 
        when _purchaseOrderItem.linetype = 'H' then 
                case 
                    when (_purchaseOrderItemShipped.InbShippedQuantityForSOLine > 0)
                        then (_purchaseOrderItem.OrderQuantity - _purchaseOrderItemShipped.InbShippedQuantityForSOLine)
                    else
                        _purchaseOrderItem.OrderQuantity
                    end
        when _purchaseOrderItem.linetype = 'L' then 
                case 
                    when (_purchaseOrderItemShipped.InbShippedQuantityForSOLine > 0)
                        then (_purchaseOrderItem.OrderQuantity - _purchaseOrderItemShipped.InbShippedQuantityForSOLine)
                    else
                        _purchaseOrderItem.OrderQuantity
                    end
      end as SOLIInboundOpenQuantity,

 

Topic 5: Logic to get Associated Inbound delivery and Associated Inbound Shipment for an Outbound delivery and likewise an Associated Outbound delivery and Associated Outbound Shipment for Inbound delivery

      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 20 } ]
      @EndUserText.label: 'DLV InboundOutboundFlag'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when '7' then 'INB'
        when 'T' then  'INB'
        when 'J' then 'OTB'
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  DeliveryInboundOutboundFlag,

      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 30 } ]
      @EndUserText.label: 'DLV Outbound Delivery'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when 'J' then _DeliveryDocumentHeader.DeliveryDocument
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  OutboundDelivery,


      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 35 } ]
      @EndUserText.label: 'DLV Associated Inbound Delivery'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when 'J' then _SupplierConfirmationOut.DeliveryDocument
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  AssociatedInboundDelivery,


      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 37 } ]
      @EndUserText.label: 'DLV Associated Inbound Shipment'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when 'J' then _AssociatedShipmentItemOut.ShipmentNumber
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  AssociatedInboundShipment,



      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 40 } ]
      @EndUserText.label: 'SHP Outbound Shipment'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when 'J' then _shipmentItem.ShipmentNumber
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  OutboundShipment,


      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 50 } ]
      @EndUserText.label: 'DLV Inbound Delivery'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when '7' then _DeliveryDocumentHeader.DeliveryDocument
        when 'T' then _DeliveryDocumentHeader.DeliveryDocument
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  InboundDelivery,

      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 60 } ]
      @EndUserText.label: 'DLV Inbound Shipment'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when '7' then _shipmentItem.ShipmentNumber
        when 'T' then  _shipmentItem.ShipmentNumber
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  InboundShipment,

      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 50 } ]
      @EndUserText.label: 'DLV Associated Outbound Delivery'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when '7' then _SupplierConfirmationInb.ExternalReferenceDocumentID
        when 'T' then _SupplierConfirmationInb.ExternalReferenceDocumentID
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  AssociatedOutboundDelivery,

      @UI.fieldGroup: [ { qualifier: 'DeliveryDocument', position: 60 } ]
      @EndUserText.label: 'DLV Associated Outbound Shipment'
      case (_DeliveryDocumentHeader.SDDocumentCategory)
        when '7' then _AssociatedShipmentItemInb.ShipmentNumber
        when 'T' then  _AssociatedShipmentItemInb.ShipmentNumber
        else '' // instead of 'OTB'  //everything else is OTB
      end
       as  AssociatedOutboundShipment,

 

Those are some of the topics that I had to work out while developing this application.  The main thing was to capture the outbound and inbound deliveries and shipments and the associated deliveries and shipments to correlate the outbound and inbound information.  Plus, we want to show the order quantity, open quantity, and shipped quantity to see the breakdown of the numbers.

 

I hope these topics will help you for writing your own custom app around STOs, deliveries and shipments.

Happy Coding!

All the best,

Jay Malla

Licensed To Code

 

You can connect with me at – Jay Malla on LinkedIn

 

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Naveen Jain
      Naveen Jain

      This is very nice..

      Author's profile photo Jay Malla
      Jay Malla
      Blog Post Author

      Thanks Naveen - I m glad you liked it.  You might like my other blog that is the overall application: SAP S/4 HANA Custom Fiori app for Project & WBS using CDS View Table Function, SmartFilter & Tree table