
This is a detailed step-by-step technical guide document to introduce a Developer Extensibility case followed by this blog.
We frequently hear inquiries from clients who have enabled batch management, asking whether there are reminders for nearing expiry materials and batch query reports for such materials. In standard products, retrieving relevant information often requires searching through multiple reports, such as inventory reports and batch details. To meet this demand, we have developed a Material Shelf Life Management App on S/4HANA public cloud through developer extensibility (also known as cloud ABAP development), aiming to help enterprises intelligently manage material Shelf Life and enhance supply chain management efficiency. Regarding the business value of this use case, you can refer to this blog for details.
Due to varying business management needs, each company may define different ranges for nearing expiry reminders. The Customizable Warning Threshold Configuration App allows users to set customized ranges for when to display red, yellow, or green warning lights, meeting the diverse customization needs of different enterprises for expiry management.
Create Data Definition: YCDS_DUE_MAT_LIST
@AbapCatalog.sqlViewName: 'YDUEMATMANGE'
@AbapCatalog.compiler.compareFilter: true
//@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Due Date List'
define view YCDS_DUE_MAT_LIST as select from I_Batch
inner join YCDS_SUM_STOCK_01 on YCDS_SUM_STOCK_01.Material = I_Batch.Material
and YCDS_SUM_STOCK_01.Plant = I_Batch.Plant
and YCDS_SUM_STOCK_01.Batch = I_Batch.Batch
association [1..1] to I_Product on I_Product.Product = I_Batch.Material
association [1..1] to I_ProductDescription on I_ProductDescription.Product = I_Batch.Material
and I_ProductDescription.Language = $session.system_language
association [1..1] to I_Plant on I_Plant.Plant = I_Batch.Plant
and I_Plant.Language = $session.system_language
association [1..1] to I_Supplier on I_Supplier.Supplier = I_Batch.Supplier
{
key I_Batch.Material,
key I_Batch.Batch,
key I_Batch.Plant,
key I_Batch.Supplier,
key I_Batch.BatchBySupplier,
// key YCDS_SUM_STOCK_01.InventoryStockType,
// key YCDS_SUM_STOCK_01.MatlDocLatestPostgDate,
YCDS_SUM_STOCK_01.StorageLocation,
YCDS_SUM_STOCK_01.MatlWrhsStkQtyInMatlBaseUnit,
I_Batch.MatlBatchIsInRstrcdUseStock,
I_Batch.ShelfLifeExpirationDate,
I_Batch.ManufactureDate,
I_Product.BaseUnit,
I_ProductDescription.ProductDescription,
I_Plant.PlantName,
I_Supplier.SupplierName,
case when I_Batch.MatlBatchIsInRstrcdUseStock = 'X' then 'Restricted' else 'Unrestricted' end as BatchStatus,
$session.system_date as curr_date
}
where I_Batch.ShelfLifeExpirationDate is not initial
and YCDS_SUM_STOCK_01.MatlWrhsStkQtyInMatlBaseUnit <> 0
Create Data Definition: YCDS_DUE_MAT_LIST2
@AbapCatalog.sqlViewName: 'YDUEMATMANGE2'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Material Due Date List'
define view YCDS_DUE_MAT_LIST2 as select from YCDS_DUE_MAT_LIST
association [1..1] to I_StorageLocation on I_StorageLocation.StorageLocation = YCDS_DUE_MAT_LIST.StorageLocation
and I_StorageLocation.Plant = YCDS_DUE_MAT_LIST.Plant
{
key YCDS_DUE_MAT_LIST.Material,
key YCDS_DUE_MAT_LIST.Plant,
key YCDS_DUE_MAT_LIST.StorageLocation,
key YCDS_DUE_MAT_LIST.Batch,
key YCDS_DUE_MAT_LIST.Supplier,
// key YCDS_DUE_MAT_LIST.InventoryStockType,
// key YCDS_DUE_MAT_LIST.MatlDocLatestPostgDate,
YCDS_DUE_MAT_LIST.ProductDescription,
YCDS_DUE_MAT_LIST.PlantName,
I_StorageLocation.StorageLocationName,
YCDS_DUE_MAT_LIST.BatchBySupplier,
YCDS_DUE_MAT_LIST.SupplierName,
MatlWrhsStkQtyInMatlBaseUnit,
YCDS_DUE_MAT_LIST.BaseUnit,
YCDS_DUE_MAT_LIST.ShelfLifeExpirationDate,
YCDS_DUE_MAT_LIST.ManufactureDate,
YCDS_DUE_MAT_LIST.BatchStatus,
dats_days_between(YCDS_DUE_MAT_LIST.curr_date,YCDS_DUE_MAT_LIST.ShelfLifeExpirationDate) as remaining_days
}
Create Data Definition: YCDS_SUM_STOCK_01
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sum of Stock 01'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity YCDS_SUM_STOCK_01 as select from I_MaterialStock_2
{
key Material,
key Plant,
key StorageLocation,
key Batch,
key MaterialBaseUnit,
@Semantics.quantity.unitOfMeasure: 'MaterialBaseUnit'
@Aggregation.default: #SUM
sum( MatlWrhsStkQtyInMatlBaseUnit ) as MatlWrhsStkQtyInMatlBaseUnit
}
where InventoryStockType = '01'
group by Material, Plant, StorageLocation, Batch, MaterialBaseUnit
Create new Metadata Extension: YC_DUE_MAT_LIST
@Metadata.layer: #CUSTOMER
@UI.headerInfo.title.type: #STANDARD
@UI.headerInfo.title.label: 'Material Shelf Life Management'
@UI.headerInfo.description.type: #STANDARD
@UI.headerInfo.typeName: 'Material Due List'
@UI.headerInfo.typeNamePlural: 'Material Shelf Life Management'
annotate view YC_DUE_MAT_LIST with
{
@UI.facet: [{ id: 'Material',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Material',
position: 10}
]
@UI:{ lineItem: [{ position: 10 }],
lineItem: [{label: 'Material'}],
identification: [{ position: 10 }],
selectionField: [{ position: 20 }]
}
Material;
@UI:{ lineItem: [{ position: 20 }],
lineItem: [{label: 'Material Description'} ],
identification: [{ position: 20 }]
}
@UI.lineItem: [{exclude: true}]
ProductDescription;
@UI:{ lineItem: [{ position: 30 }],
lineItem: [{label: 'Plant'}],
identification: [{ position: 30 }],
selectionField: [{ position: 10 }]
}
Plant;
@UI:{ lineItem: [{ position: 40 }],
lineItem: [{label: 'Plant Name'}],
identification: [{ position: 40 }]
}
@UI.lineItem: [{exclude: true}]
PlantName;
@UI.lineItem: [{ position: 50 }]
@UI.lineItem: [{label: 'Storage Location'}]
@UI.identification: [{ position: 50 }]
@UI.selectionField: [{position: 50}]
StorageLocation;
@UI.lineItem: [{ position: 60 }]
@UI.lineItem: [{label: 'Storage Location Name'}]
@UI.identification: [{ position: 60 }]
@UI.lineItem: [{exclude: true}]
StorageLocationName;
@UI.lineItem: [{ position: 70 }]
@UI.lineItem: [{label: 'Batch'}]
@UI.identification: [{ position: 70 }]
@UI.selectionField: [{position: 70}]
Batch;
@UI.lineItem: [{ position: 80 }]
@UI.lineItem: [{label: 'Supplier Batch'}]
@UI.identification: [{ position: 80 }]
@UI.selectionField: [{position: 80}]
BatchBySupplier;
@UI.lineItem: [{ position: 90 }]
@UI.lineItem: [{label: 'Supplier'}]
@UI.identification: [{ position: 90 }]
Supplier;
@UI.lineItem: [{ position: 100 }]
@UI.lineItem: [{label: 'Supplier Name'}]
@UI.identification: [{ position: 100 }]
@UI.lineItem: [{exclude: true}]
SupplierName;
@UI.lineItem: [{ position: 110 }]
@UI.lineItem: [{label: 'Unrestricted Inventory Qty'}]
@UI.identification: [{ position: 110 }]
MatlWrhsStkQtyInMatlBaseUnit;
@UI.lineItem: [{ position: 120 }]
@UI.lineItem: [{label: 'Unit'}]
@UI.identification: [{ position: 120 }]
// @UI.lineItem: [{exclude: true}]
@UI.hidden: true
BaseUnit;
@UI.lineItem: [{ position: 130 }]
@UI.lineItem: [{label: 'Expiration Date'}]
@UI.identification: [{ position: 130 }]
ShelfLifeExpirationDate;
@UI.lineItem: [{ position: 140 }]
@UI.lineItem: [{label: 'Manufacture Date'}]
@UI.identification: [{ position: 140 }]
@UI.lineItem: [{exclude: true}]
ManufactureDate;
@UI.lineItem: [{ position: 150 }]
@UI.lineItem: [{label: 'Batch Status'}]
@UI.identification: [{ position: 150 }]
@UI.lineItem: [{exclude: true}]
BatchStatus;
@UI.lineItem: [{ position: 160, importance: #HIGH }]
@UI.lineItem: [{label: 'Remaining Days'}]
@UI.identification: [{ position: 160 }]
remaining_days;
@UI.lineItem: [{ position: 170, value: 'due_range', criticality: 'Criticality' }]
@UI.lineItem: [{label: 'Due Date Range'}]
@UI.identification: [{ position: 170 }]
due_range;
// @UI.hidden: true
// InventoryStockType;
// @UI.hidden: true
// MatlDocLatestPostgDate;
@UI.hidden: true
Criticality;
}
And then we can preview the published service:
First create destination on BTP:
Then open BAS service, and choose SAP Fiori Worklist Application:
After publish, and authorization setup, we can see the App like below:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
6 | |
5 | |
5 | |
5 | |
4 | |
3 | |
3 | |
3 | |
3 |