All Blogs in this Series –

AI Powered Invoice Management with SAP RAP and ABAP on Cloud

AI Powered Invoice Management with SAP RAP and ABAP on Cloud – Part 1

AI Powered Invoice Management with SAP RAP and ABAP on Cloud – Part 2

AI Powered Invoice Management with SAP RAP and ABAP on Cloud – Part 3

 

ABAP on Cloud –

In this we will try to achieve a RAP enabled UI framework to upload Vendor Invoice and further Managements.

You must enable draft functionality and enable the MIME type for attachments in your RAP application.

Create Invoice Table and a Draft Table –

@EndUserText.label : 'Invoice Table' 
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY 
@AbapCatalog.tableCategory : #TRANSPARENT 
@AbapCatalog.deliveryClass : #A 
@AbapCatalog.dataMaintenance : #ALLOWED 
define table zinvtable_sab { 
 key client : abap.clnt not null; 
 key invoice : zinvoice_sab not null; 
 comments : abap.char(30); 
 refpurchaseorder : ebeln; 
 status : abap.char(1); 
 attachment : zattachment; 
 mimetype : zmimetype; 
 filename : zfilename; 
 receivercontact : abap.char(80); 
 grossamount : abap.char(80); 
 invoicedate : abap.char(15); 
 local_created_by : abp_creation_user; 
 local_created_at : abp_creation_tstmpl; 
 local_last_changed_by : abp_locinst_lastchange_user; 
 local_last_changed_at : abp_locinst_lastchange_tstmpl; 
 last_changed_at : abp_lastchange_tstmpl; 
}

@EndUserText.label : 'Draft table for entity ZI_INVTABLE_SAB' 
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY 
@AbapCatalog.tableCategory : #TRANSPARENT 
@AbapCatalog.deliveryClass : #A 
@AbapCatalog.dataMaintenance : #RESTRICTED 
define table zinvtabsab_draft { 
 key mandt : mandt not null; 
 key invoice : ebeln not null; 
 comments : abap.char(30); 
 refpurchaseorder : ebeln; 
 status : abap.char(1); 
 receivercontact : abap.char(80); 
 grossamount : abap.char(80); 
 invoicedate : abap.char(15); 
 attachment : zattachment; 
 mimetype : zmimetype; 
 filename : zfilename; 
 localcreatedby : abp_creation_user; 
 localcreatedat : abp_creation_tstmpl; 
 locallastchangedby : abp_locinst_lastchange_user; 
 locallastchangedat : abp_locinst_lastchange_tstmpl; 
 lastchangedat : abp_lastchange_tstmpl; 
 "%admin" : include sych_bdl_draft_admin_inc; 
}

 

Now Create Invoice Item Tables –

@EndUserText.label : 'Item Invoice Details' 
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY 
@AbapCatalog.tableCategory : #TRANSPARENT 
@AbapCatalog.deliveryClass : #A 
@AbapCatalog.dataMaintenance : #ALLOWED 
define table zinvitem_sab { 
 key client : abap.clnt not null; 
 key invoice : zinvoice_sab not null; 
 key material : abap.char(40) not null; 
 description : abap.char(40) not null; 
 quantity : abap.char(10) not null; 
 amount : abap.char(10) not null; 
 unitprice : abap.char(10) not null; 
} 
@EndUserText.label : 'Draft Table for invoice Items' 
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY 
@AbapCatalog.tableCategory : #TRANSPARENT 
@AbapCatalog.deliveryClass : #A 
@AbapCatalog.dataMaintenance : #RESTRICTED 
define table zinvitmsab_draft { 
 key client : abap.clnt not null; 
 key invoice : zinvoice_sab not null; 
 key material : abap.char(40) not null; 
 description : abap.char(40) not null; 
 quantity : abap.char(10) not null; 
 amount : abap.char(10) not null; 
 unitprice : abap.char(10) not null; 
 lastchangedat : abp_lastchange_tstmpl; 
 "%admin" : include sych_bdl_draft_admin_inc; 
} 

 

Lets create CDS Entities with association are needed between Invoice header and item tables –

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Invoice Table'
define root view entity ZI_INVTABLE_SAB 
 as select from zinvtable_sab 
 composition[1..*] of zi_invitem_sab as _invitm 
 
{
 key invoice as Invoice,
 comments as Comments,
 refpurchaseorder as RefPurchaseOrder,
 status as Status,
 receivercontact as ReceiverContact,
 grossamount as GrossAmount,
 
 invoicedate as InvoiceDate,
 
 @Semantics.largeObject:
 { 
 mimeType: 'MimeType',
 fileName: 'Filename',
 contentDispositionPreference: #INLINE 
 }
 attachment as Attachment, 
 @Semantics.mimeType: true
 mimetype as MimeType,
 filename as Filename,
 @Semantics.user.createdBy: true
 local_created_by as LocalCreatedBy,
 @Semantics.systemDateTime.createdAt: true
 local_created_at as LocalCreatedAt,
 @Semantics.user.lastChangedBy: true
 local_last_changed_by as LocalLastChangedBy,
 //local ETag field --> OData ETag
 @Semantics.systemDateTime.localInstanceLastChangedAt: true
 local_last_changed_at as LocalLastChangedAt,
 //total ETag field
 @Semantics.systemDateTime.lastChangedAt: true
 last_changed_at as LastChangedAt,
 
 _invitm 
}
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface for Invoice Items'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
 serviceQuality: #X,
 sizeCategory: #S,
 dataClass: #MIXED
}
define view entity zi_invitem_sab 
 as select from zinvitem_sab association 
 to parent ZI_INVTABLE_SAB as _invoice 
 on $projection.invoice = _invoice.Invoice 
{
 key zinvitem_sab.invoice,
 @EndUserText.label: 'Material'
 key zinvitem_sab.material as material,
 @EndUserText.label: 'Description'
 description,
 @EndUserText.label: 'Quantity'
 quantity,
 @EndUserText.label: 'Amount'
 amount,
 @EndUserText.label: 'unit Price'
 unitprice,
 _invoice.LastChangedAt,
 /* Associations */
 _invoice 
 
}

 

Now create Projection view of top of the CDS entities –

@EndUserText.label: 'Projection View for Items'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
define view entity ZP_INVITEM_SAB 
as projection on zi_invitem_sab 
{
 @UI.facet: [{
 id: 'Invoicedet', 
 purpose: #STANDARD,
 label: 'Item Data',
 type: #IDENTIFICATION_REFERENCE,
 position: 10
 }]
 
 @UI.hidden: true
 key invoice,
 
 @UI: { lineItem: [{ position: 20 } ],
 
 identification: [{ position: 20 }] }
 key material,
 @UI: { lineItem: [{ position: 30 , cssDefault.width: '25rem'}],
 identification: [{ position: 30 }] }
// , fieldGroup: [ { qualifier: 'Fieldgroup2'} ] } 
 description,
 @UI: { lineItem: [{ position: 40 }],
 identification: [{ position: 40 }] } 
 quantity,
 @UI: { lineItem: [{ position: 50 }],
 identification: [{ position: 50 }] } 
 amount,
 @UI: { lineItem: [{ position: 60 }],
 identification: [{ position: 60 }] } 
 unitprice,
 @UI.hidden: true 
 LastChangedAt,
 /* Associations */
 _invoice: redirected to parent ZP_INVTABLE_SAB 
}
@EndUserText.label: 'Invoice Table Data'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
define root view entity ZP_INVTABLE_SAB 
 provider contract transactional_query
 as projection on ZI_INVTABLE_SAB 
{
 @UI.facet: [{ label: 'General Information',
 id: 'GeneralInfo',
type: #COLLECTION,
position: 10 },
 { id: 'Invoicedet',
 purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Invoice Details',
parentId: 'GeneralInfo',
position: 10 },
 { id: 'Upload',
 purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
parentId: 'GeneralInfo',
label: 'Upload Invoice',
position: 20,
targetQualifier: 'Upload' },
 { id: 'extracteddata',
 purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
parentId: 'GeneralInfo',
label: 'Extracted Data',
position: 30,
targetQualifier: 'extracteddata' },
 { id: 'invoiceitems',
 purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Item ',
position: 50,
targetElement: '_invitm'}
 ]
 @UI: { lineItem: [ { position: 10, importance: #HIGH , label:
'Invoice Number'}
 ,{ type: #FOR_ACTION, dataAction:
'startpaypro', label: 'Start Payment Process',
invocationGrouping:#CHANGE_SET } 
 ] , identification: [ { position: 10 , label:
'Invoice Number' } ] }
 key Invoice,
 @UI: { lineItem: [ { position: 20, importance: #HIGH , label:
'Comments', cssDefault.width: '15rem' } ] ,
 identification: [ { position: 20 , label:
'Comments' }, { type: #FOR_ACTION, dataAction: 'analyzedoc', label:
'Analyze Data' } ] }
 @UI.multiLineText: true
 Comments,
 @UI: { lineItem: [ { position: 30, importance: #HIGH, label:
'Reference PO'} ] ,
 fieldGroup: [ { position: 30, qualifier: 'extracteddata',
label: 'Reference PO'}] } 
 RefPurchaseOrder as RefPurchaseOrder,
 @UI: { lineItem: [ { position: 40, importance: #HIGH, label:
'Status'} ] ,
 fieldGroup: [ { position: 40, qualifier: 'extracteddata',
label: 'Status'}] } 
 Status as Status, 
 @UI: { lineItem: [ { position: 50, importance: #HIGH, label:
'Reciever Contact'} ] ,
 fieldGroup: [ { position: 50, qualifier: 'extracteddata',
label: 'Reciever Contact'}] } 
 ReceiverContact,
 @UI: { lineItem: [ { position: 60, importance: #HIGH, label:
'Gross Amount' } ] ,
 fieldGroup: [ { position: 60, qualifier: 'extracteddata',
label: 'Gross Amount'} ] }
 GrossAmount,
 @UI: { lineItem: [ { position: 70, importance: #HIGH , label:
'Invoice Date' } ] ,
 fieldGroup: [ { position: 70, qualifier: 'extracteddata',
label: 'Invoice Date'} ] } 
 InvoiceDate,
 @UI: { fieldGroup: [ { position: 80, qualifier: 'Upload' ,
label: 'Attachment'} ]}
 Attachment,
 @UI.hidden: true
 MimeType,
 @UI.hidden: true 
 Filename,
 @UI.hidden: true 
 LocalLastChangedAt,
 _invitm: redirected to composition child ZP_INVITEM_SAB 
}

 

Create Behavior Definitions where you define new custom actions on top of the Managed scenario –

managed implementation in class zbp_i_invtable_sab unique;
strict ( 1 );
with draft;
define behavior for ZI_INVTABLE_SAB alias Invoice 
persistent table zinvtable_sab 
draft table ZINVTABSAB_DRAFT 
lock master
total etag LocalLastChangedAt 
authorization master ( global )
etag master LastChangedAt 
{
 field ( readonly ) LastChangedAt, LocalLastChangedBy,
LocalLastChangedAt , LocalCreatedBy ,
 LocalCreatedAt;
 field ( mandatory ) Attachment;
 create;
 update;
 delete;
 association _invitm { create; with draft; }
 draft action Edit ;
 draft action Activate;
 draft action Discard;
 draft action Resume;
 draft determine action Prepare ;
 action ( features : instance ) analyzedoc result [1] $self;
 action startpaypro result [1] $self;
// " result [1] $self;
// validation startpaypro on { }
}
define behavior for zi_invitem_sab alias Items 
persistent table zinvitem_sab 
draft table zinvitmsab_draft 
lock dependent by _invoice 
authorization dependent by _invoice 
{
 update;
 delete;
 field ( readonly ) invoice, LastChangedAt;
 association _invoice { with draft; }
}
/////For Projection///// 
projection;
strict ( 1 );
use draft;
define behavior for ZP_INVTABLE_SAB //alias <alias_name>
{
 use create;
 use update;
 use delete;
 use action analyzedoc;
 use action startpaypro;
 use action Edit;
 use action Activate;
 use action Discard;
 use action Resume;
 use action Prepare;
 use association _invitm { create; with draft; }
}
define behavior for zp_invitem_sab //alias Items
{
 use update;
 use delete;
// use action analyzedoc;
 use association _invoice { with draft; }
}

 

Now create metadata extensions to change the UI of the facet. You can introduce several functionalities to acheive further UI functionalities

@Metadata.layer: #CORE
@UI: { headerInfo: {
 typeName: 'Material',
 typeNamePlural: 'Materials',
 title: { type: #STANDARD, value: 'Material' },
 description: { type: #STANDARD, value: 'description' }
},
 presentationVariant: [{
 sortOrder: [{ by: 'material', direction: #ASC }],
 visualizations: [{type: #AS_CHART}] 
 }] }
annotate view ZP_INVITEM_SAB 
 with 
{
 @UI.lineItem: [{ position: 10, label: 'Desc' }] 
 description;
 @UI.lineItem: [{ position: 20, label: 'Amount' }]
 amount;
}
@Metadata.layer: #CORE
@UI: { headerInfo: {
typeName: 'Invoice',
typeNamePlural: 'Invoices',
title: { type: #STANDARD, value: 'Invoice' },
 description: { type: #STANDARD, value: 'Invoice' } },
 presentationVariant: [{
 sortOrder: [{ by: 'Invoice', direction: #ASC }],
 visualizations: [{type: #AS_LINEITEM}] }] }
annotate entity ZP_INVTABLE_SAB with {}

 

Now implement the Behavior Definition class –

CLASS lhc_Invoice DEFINITION INHERITING FROM cl_abap_behavior_handler.
 PUBLIC SECTION.
 CLASS-METHODS item_det 
 CHANGING ct_items TYPE zsaba_inv_process=>tt_items.
 PRIVATE SECTION.
 DATA: go_data TYPE REF TO lhc_Invoice.
 METHODS get_instance_features FOR INSTANCE FEATURES
 IMPORTING keys REQUEST requested_features FOR Invoice RESULT result.
 METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
 IMPORTING REQUEST requested_authorizations FOR Invoice RESULT result.
 METHODS analyzedoc FOR MODIFY
 IMPORTING keys FOR ACTION Invoice~analyzedoc RESULT result.
 METHODS startpaypro FOR MODIFY
 IMPORTING keys FOR ACTION Invoice~startpaypro RESULT result.
ENDCLASS.
CLASS lhc_Invoice IMPLEMENTATION.
 METHOD startpaypro.
 DATA messages TYPE /dmo/t_message.
* READ ENTITIES OF zi_invtable_sab IN LOCAL MODE
* ENTITY Invoice
* ALL FIELDS WITH CORRESPONDING #( keys )
* RESULT DATA(fetchdata).
* result = VALUE #( FOR line IN fetchdata
* ( %tky = line-%tky
* %param = line ) ).
* LOOP AT result ASSIGNING FIELD-SYMBOL(<lfs_invoices>).
 APPEND VALUE #( %msg = NEW zcl_sab_inv_msg(
 textid = 
zcl_sab_inv_msg=>sent_for_payment 
 severity = 
if_abap_behv_message=>severity-success )
 "%ELEMENT = IF_ABAP_BEHV=>MK-ON
 ) TO reported-invoice.
 MODIFY ENTITIES OF zi_invtable_sab IN LOCAL MODE
 ENTITY Invoice 
 UPDATE
 FIELDS ( Status )
 WITH VALUE #( FOR key IN keys 
 ( Invoice = key-Invoice 
 Status = 'W'
 ) ).
 ENDMETHOD.
 METHOD get_instance_features.
 LOOP AT keys INTO DATA(key).
 APPEND VALUE #(
 %tky = key-%tky 
 %action-analyzedoc = COND #( WHEN key-%is_draft = 
if_abap_behv=>mk-off 
 THEN if_abap_behv=>fc-o-enabled 
 ELSE if_abap_behv=>fc-o-disabled ) ) 
TO result.
 ENDLOOP.
 ENDMETHOD.
 METHOD get_global_authorizations.
 ENDMETHOD.
 METHOD item_det.
 ENDMETHOD.
 METHOD analyzedoc.
 DATA: lt_table TYPE TABLE OF zinvitem_sab.
 READ ENTITIES OF zi_invtable_sab IN LOCAL MODE
 ENTITY Invoice 
 ALL FIELDS WITH CORRESPONDING #( keys )
 RESULT DATA(fetchdata).
 result = VALUE #( FOR line IN fetchdata 
 ( %tky = line-%tky 
 %param = line ) ).
* IF LINES( result ) = 1.
 LOOP AT result ASSIGNING FIELD-SYMBOL(<lfs_line>).
 CALL METHOD zsaba_inv_process=>analyze_doc 
 "CALL METHOD zsaba_inv_process=>analyze_doc_dummy
 EXPORTING
 iv_inv_doc = <lfs_line>-Invoice 
 IMPORTING
 es_header = DATA(ls_header)
 et_items = DATA(lt_items).
 CALL METHOD item_det 
 CHANGING
 ct_items = lt_items.
 READ ENTITIES OF zi_invtable_sab IN LOCAL MODE
 ENTITY Invoice BY _invitm 
 ALL FIELDS WITH VALUE #( ( %tky = <lfs_line>-%tky ) )
 RESULT DATA(lt_line_read).
 MOVE-CORRESPONDING lt_items TO lt_table.
 DELETE ADJACENT DUPLICATES FROM lt_table COMPARING material.
 MODIFY ENTITIES OF zi_invtable_sab IN LOCAL MODE
 ENTITY Invoice 
 UPDATE
 FIELDS ( ReceiverContact GrossAmount InvoiceDate Comments
Status RefPurchaseOrder )
 WITH VALUE #( FOR key IN keys 
 ( Invoice = key-Invoice 
 ReceiverContact = ls_header-receivercontact
 GrossAmount = ls_header-grossamount 
 InvoiceDate = ls_header-invoicedate 
 Comments = ls_header-comments 
 Status = 'I'
RefPurchaseOrder = ls_headerrefpurchaseorder 
 ) ).
 LOOP AT lt_table ASSIGNING FIELD-SYMBOL(<lfs_table>).
 <lfs_table>-Invoice = <lfs_line>-invoice.
 <lfs_table>-client = sy-mandt.
 IF line_exists( lt_line_read[ material = <lfs_table>-material ] ).
 " Do not do anything. The Line might be manually created
 ELSE.
 MODIFY ENTITIES OF zi_invtable_sab IN LOCAL MODE
 ENTITY Invoice 
* UPDATE
* FIELDS ( ReceiverContact GrossAmount InvoiceDate Comments )
* WITH VALUE #( FOR key IN keys
* ( Invoice = key-Invoice
* ReceiverContact = ls_headerreceivercontact
* GrossAmount = ls_header-grossamount
* InvoiceDate = ls_header-invoicedate
* Comments = ls_header-comments
* Status = 'I'
* RefPurchaseOrder = ls_headerrefpurchaseorder
* ) )
 "CREATE
 CREATE
 BY _invitm 
 FIELDS ( amount description invoice material quantity
unitprice )
 WITH VALUE #( ( invoice = <lfs_line>-invoice 
 %target = VALUE #( (
 %is_draft = 
if_abap_behv=>mk-off 
 amount = <lfs_table>-
amount 
 description = 
<lfs_table>-description 
 invoice = <lfs_table>-
invoice 
 material = 
<lfs_table>-material 
 quantity = 
<lfs_table>-quantity 
 unitprice = 
<lfs_table>-unitprice 
 ) ) ) ) .
 ENDIF.
 ENDLOOP.
 ENDLOOP.
* ELSEIF LINES( result ) > 1.
* APPEND VALUE #( %msg = NEW zcl_sab_inv_msg(
* textid = 
zcl_sab_inv_msg=>select_only_one_for_analysis
* severity = 
if_abap_behv_message=>severity-error )
* "%ELEMENT = IF_ABAP_BEHV=>MK-ON
* ) TO reported-invoice.
* ENDIF.
 ENDMETHOD.
ENDCLASS.

All the steps are completed for RAP developments.

Here through this RAP Report what you are achieving –

  1. Upload a Raw file
  2. Deep Entity manipulation with EML(Entity Manipulation) for ABAP RAP

 

Next is Invoice Process Class. Here we need to create the integration aspects to this Development.

Create Service definition and Service Bindings accordingly.

Please visit Part 2 as a continuation.

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x