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 –
- Upload a Raw file
- 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.