Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
jmalla
Contributor
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 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

 
2 Comments
Labels in this area