Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save js1972/03c33774e5fb535f4ad7 to your computer and use it in GitHub Desktop.
Save js1972/03c33774e5fb535f4ad7 to your computer and use it in GitHub Desktop.
Example ABAP class with local classes and test cases showing the use of test doubles, etc.
*"* use this source file for any type of declarations (class
*"* definitions, interfaces or type declarations) you need for
*"* components in the private section
types:
begin of event_rec,
evt_name type string,
sort_order type i,
end of event_rec.
types:
events_tab type standard table of event_rec with default key.
*----------------------------------------------------------------------*
* CLASS lcx_lifex_not_unique DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcx_lifex_not_unique definition inheriting from cx_static_check final.
endclass. "lcx_lifex_not_unique DEFINITION
*----------------------------------------------------------------------*
* CLASS lcx_uom_conversion_exception DEFINITION
*----------------------------------------------------------------------*
* Exception class for UoM conversion issues.
*----------------------------------------------------------------------*
class lcx_uom_conversion_exception definition inheriting from cx_no_check final.
public section.
data: iso_uom type isocd_unit.
methods: constructor importing iso_uom type isocd_unit,
get_iso_uom returning value(iso_uom) type isocd_unit.
endclass. "lcx_uom_conversion_exception DEFINITION
*----------------------------------------------------------------------*
* INTERFACE lif_system_api DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
interface lif_system_api.
methods:
unit_of_measure_iso_to_sap
importing iso_uom type isocd_unit
returning value(local_uom) type meins,
event_codes_config_read
returning value(event_codes) type events_tab.
endinterface. "lif_system_api DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_system_api DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_system_api definition create private.
public section.
class-methods: get_instance returning value(r_instance) type ref to lcl_system_api.
interfaces: lif_system_api.
private section.
class-data: instance type ref to lcl_system_api.
constants: lc_event_config_template type tsegtempla value 'ZZIDHDR'.
endclass. "lcl_system_api DEFINITION
*"* use this source file for the definition and implementation of
*"* local helper classes, interface definitions and type
*"* declarations
class lcx_uom_conversion_exception implementation.
method constructor.
super->constructor( ).
me->iso_uom = iso_uom.
endmethod. "constructor
method get_iso_uom.
iso_uom = me->iso_uom.
endmethod. "get_iso_uom
endclass. "lcx_uom_conversion_exception IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS lcl_system_api IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_system_api implementation.
method get_instance.
if instance is not bound.
create object instance.
endif.
r_instance = instance.
endmethod. "get_instance
method lif_system_api~unit_of_measure_iso_to_sap.
call function 'UNIT_OF_MEASURE_ISO_TO_SAP'
exporting
iso_code = iso_uom
importing
sap_code = local_uom
exceptions
not_found = 1
others = 2.
if sy-subrc <> 0.
raise exception type lcx_uom_conversion_exception
exporting
iso_uom = iso_uom.
endif.
endmethod. "unit_of_measure_is_to_sap
method lif_system_api~event_codes_config_read.
" Read the configured list of Time Segment event codes
" and their sort sequence numbers.
types:
begin of ty_time_segment_events,
even type tsegevttyp,
even_sor type tsegevtsrt,
end of ty_time_segment_events.
data: time_segment_events type table of ty_time_segment_events.
field-symbols: <event_code> like line of event_codes,
<event> like line of time_segment_events.
select even even_sor
into corresponding fields of table time_segment_events
from ttsegtple
where tepl = lc_event_config_template.
loop at time_segment_events assigning <event>.
append initial line to event_codes assigning <event_code>.
"call function 'CONVERSION_EXIT_EVTYA_OUTPUT'
" exporting
" input = <event>-even
" importing
" output = <event>-even.
"select even
" into <event_code>-evt_name
" from ttsegevty up to 1 rows
" where even_ali = <event>-even.
"endselect.
"call function 'CONVERSION_EXIT_EVTYA_INPUT'
" exporting
" input = <event>-even
" importing
" output = <event_code>-evt_name.
<event_code>-evt_name = <event>-even.
<event_code>-sort_order = <event>-even_sor.
endloop.
endmethod. "lif_system_api~event_codes_config_read
endclass. "lcl_system_api IMPLEMENTATION
*"* use this source file for your ABAP unit test classes
" Make the class under test a friend so we can access private members.
" Normally you would only test the public interface to your class, but
" in this case we do not have a full test suite (yet) and wish to
" simply test new code.
class ltc_uom_conversion definition deferred.
class zcl_advance_shipg_notice_maint definition local friends ltc_uom_conversion.
class ltc_time_segment_config definition deferred.
class zcl_advance_shipg_notice_maint definition local friends ltc_time_segment_config.
*----------------------------------------------------------------------*
* CLASS ltd_system_api DEFINITION
*----------------------------------------------------------------------*
* This is a local mock class for the system API.
*----------------------------------------------------------------------*
class ltd_system_api definition.
public section.
interfaces: lif_system_api.
endclass. "ltd_system_api DEFINITION
*----------------------------------------------------------------------*
* CLASS ltd_system_api IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class ltd_system_api implementation.
method lif_system_api~unit_of_measure_iso_to_sap.
case iso_uom.
when 'EA '.
local_uom = 'EA '.
when 'KGM'.
local_uom = 'KG'.
when 'MTQ'.
local_uom = 'M3'.
when others.
raise exception type lcx_uom_conversion_exception
exporting
iso_uom = iso_uom.
endcase.
endmethod. "lif_system_api~unit_of_measure_iso_to_sap
method lif_system_api~event_codes_config_read.
field-symbols: <event_code> like line of event_codes.
clear event_codes.
append initial line to event_codes assigning <event_code>.
<event_code>-evt_name = 'ZZCODE_ARR'.
<event_code>-sort_order = '960'.
append initial line to event_codes assigning <event_code>.
<event_code>-evt_name = 'ZZCODE_CCD'.
<event_code>-sort_order = '970'.
append initial line to event_codes assigning <event_code>.
<event_code>-evt_name = 'ZZCODE_CFD'.
<event_code>-sort_order = '955'.
append initial line to event_codes assigning <event_code>.
<event_code>-evt_name = 'ZZCODE_CTA'.
<event_code>-sort_order = '950'.
append initial line to event_codes assigning <event_code>.
<event_code>-evt_name = 'ZZCODE_ETA'.
<event_code>-sort_order = '930'.
endmethod. "lif_system_api~event_codes_config_read
endclass. "ltd_system_api IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS ltc_uom_conversion DEFINITION
*----------------------------------------------------------------------*
* Local test class for UoM conversion using private mock
* injection of the system API class.
*----------------------------------------------------------------------*
class ltc_uom_conversion definition for testing risk level harmless duration short
inheriting from cl_aunit_assert.
private section.
methods: convert_with_empty_val for testing,
convert_with_invalid_val for testing,
convert_with_good_val for testing.
class-data: cut type ref to zcl_advance_shipg_notice_maint,
mock_system_api type ref to ltd_system_api.
class-methods: class_setup.
endclass. "ltc_uom_conversion DEFINITION
*----------------------------------------------------------------------*
* CLASS ltc_time_segment_config DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class ltc_time_segment_config definition for testing risk level harmless duration short
inheriting from cl_aunit_assert.
private section.
methods: test_events_read for testing,
test_for_valid_events for testing.
class-data: cut type ref to zcl_advance_shipg_notice_maint,
mock_system_api type ref to ltd_system_api.
class-methods: class_setup.
endclass. "ltc_time_segment_config DEFINITION
*----------------------------------------------------------------------*
* CLASS ltc_uom_conversion IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class ltc_uom_conversion implementation.
method class_setup.
create object cut.
" PRIVATE MOCK INJECTION of system api class!
create object mock_system_api.
cut->mo_system_api = mock_system_api.
endmethod. "class_setup
method convert_with_empty_val.
try.
cut->uom_iso_to_local( '' ).
fail( 'UoM conversion exception did not occur' ).
catch lcx_uom_conversion_exception.
endtry.
endmethod. "convert_with_empty_val
method convert_with_invalid_val.
try.
cut->uom_iso_to_local( 'ZZZ' ).
fail( 'UoM conversion exception did not occur' ).
catch lcx_uom_conversion_exception.
endtry.
endmethod. "convert_with_invalid_val
method convert_with_good_val.
data: internal_val type meins.
try.
internal_val = cut->uom_iso_to_local( 'EA ' ).
assert_equals( act = internal_val exp = 'EA ' msg = 'Invalid internal UoM determined for EA' ).
internal_val = cut->uom_iso_to_local( 'MTQ' ).
assert_equals( act = internal_val exp = 'M3 ' msg = 'Invalid internal UoM determined for MTQ' ).
internal_val = cut->uom_iso_to_local( 'KGM' ).
assert_equals( act = internal_val exp = 'KG ' msg = 'Invalid internal UoM determined for KGM' ).
catch lcx_uom_conversion_exception.
fail( 'Unexpected exception occurred' ).
endtry.
endmethod. "convert_with_good_val
endclass. "ltc_uom_conversion IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS ltc_time_segment_config IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class ltc_time_segment_config implementation.
method class_setup.
create object cut.
" PRIVATE MOCK INJECTION of system api class!
create object mock_system_api.
cut->mo_system_api = mock_system_api.
endmethod. "class_setup
method test_for_valid_events.
assert_equals( act = cut->valid_event_name( `ZZCODE_ARR` ) exp = abap_true msg = `ZZCODE_ARR not found in mt_events` ).
assert_equals( act = cut->valid_event_name( `ZZCODE_CCD` ) exp = abap_true msg = `ZZCODE_CCD not found in mt_events` ).
assert_equals( act = cut->valid_event_name( `ZZCODE_CFD` ) exp = abap_true msg = `ZZCODE_CFD not found in mt_events` ).
assert_equals( act = cut->valid_event_name( `ZZCODE_CTA` ) exp = abap_true msg = `ZZCODE_CTA not found in mt_events` ).
assert_equals( act = cut->valid_event_name( `ZZCODE_ETA` ) exp = abap_true msg = `ZZCODE_ETA not found in mt_events` ).
assert_equals( act = cut->valid_event_name( `ZZCODE_ZZZ` ) exp = abap_false msg = `ZZCODE_ZZZ should not be in mt_events` ).
endmethod. "test_for_valid_events
method test_events_read.
cut->event_codes_read_and_cache( ).
assert_equals( act = lines( cut->mt_events ) exp = 5 msg = 'Error reading event codes' ).
endmethod. "test_events_read
endclass. "ltc_time_segment_config IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS ZCL_ADVANCE_SHIPG_NOTICE_MAINT DEFINITION
*----------------------------------------------------------------------*
* Advanced Shipping Notice = Inbound Delivery in SAP.
* Proxy implementation for PI interface.
*
* Proxy is meant to be a enterprise service but DB Schenker requirements
* have resulted in this being tied fairly closely to DBS / INPEX
* customisations.
*
* This interface ignores any standard processing for header and line
* item gross, net weights and volumes.
* An enhancement (Z_GN_DELIVERY_CREATE_VOLUM) has been made to posting
* function: GN_DELIVERY_CREATE to cater for this by overwriting the
* gross, net weights and volume with those provided by the interface.
*
* !!! ENSURE CHANGES ARE COVERED BY ABAP UNIT TESTS !!!
*
*----------------------------------------------------------------------*
class zcl_advance_shipg_notice_maint definition
public
create public .
public section.
interfaces zii_advance_shipg_notice_maint .
interfaces if_ech_action .
methods constructor .
protected section.
class-data go_ech_action type ref to zcl_advance_shipg_notice_maint.
private section.
data mt_events type events_tab .
data:
mt_events_to_write type standard table of tseg_evenwritewa .
data mo_gdt_conv type ref to zif_gdt_conversion .
constants: mc_inbd_del_type type lfart value 'EL', "#EC NOTEXT
mc_eta_final_dest type string value 'ZZCODE_EFD', "#EC NOTEXT
mc_eta_final_dest_changed type string value 'ZZCODE_CFD', "#EC NOTEXT
mc_eta_port type string value 'ZZCODE_ETA', "#EC NOTEXT
mc_eta_port_changed type string value 'ZZCODE_CTA'. "#EC NOTEXT
data: mo_system_api type ref to lif_system_api.
methods process_in
importing
!input type zadvance_shipping_notice_main8
!feh_registration type ref to cl_feh_registration
exporting
!r_result type bapiret2_tab .
methods get_delivery_from_vendorid
importing
!i_lifex type lifex
returning
value(r_vbeln) type vbeln_vl
raising
lcx_lifex_not_unique .
methods create_delivery
importing
!i_payload type zadvance_shipping_notice_main8
exporting
!e_vbeln type vbeln_vl
!e_result type bapiret2_tab .
methods update_delivery
importing
!i_payload type zadvance_shipping_notice_main8
!i_vbeln type vbeln_vl
exporting
!e_result type bapiret2_tab .
methods add_event
importing
!i_eventname type string
!i_tmstp type timestamp
!i_version type tsegvertyp default '1' .
methods write_events
importing
!i_vbeln type vbeln_vl .
methods get_event_sort
importing
!event_code type string
returning
value(r_sort) type i .
methods get_evt_hdr_for_mod
importing
!i_handle type tsegguid_likp
returning
value(r_header) type tseg_headwritetab .
methods add_relationship
importing
!i_vbeln type vbeln_vl .
methods init_events
importing
!i_vbeln type vbeln_vl .
methods get_likp_handle
importing
!i_vbeln type vbeln_vl
returning
value(rv_handle) type tsegguid_likp .
methods get_ech_error_cat
importing
!i_bapiret type bapiret2
returning
value(r_ech_cat) type ech_dte_error_category .
methods validate_incoterms
importing
!i_incoterms type inco1
!i_po type ebeln
returning
value(r_messages) type bapiret2_tab .
methods calc_delivery_date
importing
!i_etadate type dats
returning
value(r_deldate) type dats .
methods map_proxy_to_likp
importing
!i_msg type zadvance_shipping_notice_maint
returning
value(r_likp) type likp .
methods valid_event_name
importing
event_name type string
returning
value(valid) type abap_bool.
methods uom_iso_to_local
importing
iso_uom type isocd_unit
returning
value(local_uom) type meins.
methods process_change_events
importing
tseg_hdl type tsegguid_likp
value type timestamp
message_event type string
event type string
change_event type string.
methods event_codes_read_and_cache.
endclass. "zcl_advance_shipg_notice_maint DEFINITION
*----------------------------------------------------------------------*
* CLASS ZCL_ADVANCE_SHIPG_NOTICE_MAINT IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class zcl_advance_shipg_notice_maint implementation.
method add_event.
data: event like line of me->mt_events_to_write.
event-even = i_eventname.
event-even_verty = i_version.
event-even_vernu = 0.
event-even_zonfr = sy-zonlo.
event-even_zonto = sy-zonlo.
event-even_sor = get_event_sort( event_code = i_eventname ).
event-even_tstfr = i_tmstp.
event-even_tstto = i_tmstp.
event-diag_modif = 'I'.
append event to me->mt_events_to_write.
endmethod. "write_event
method add_relationship.
data: object_id type sibfboriid,
msg_id type sxmsmguid,
msg_util type ref to zif_messaging_util.
create object msg_util type zcl_messaging_util.
object_id = i_vbeln.
try.
msg_id = msg_util->get_msg_id_inbound( ).
msg_util->add_relationship(
exporting
i_object_id = object_id
i_bor_obj = 'BUS2015'
i_msg_id = msg_id
).
catch cx_ai_system_fault ##no_handler.
"Not going to fail the whole msg if issue with relationship being written
endtry.
endmethod. "add_relationship
method calc_delivery_date.
data: eta_date type dats,
add_days type i,
tvarv_val type tvarv_val,
retcode like sy-subrc.
constants: c_fac_calendar type wfcid value 'AU',
c_tvarv_obj type rvari_vnam value 'ZZ_DBS_SEAPORT_DAYS'.
"Get the DBS processing time TVARV. This is the number of BUSINESS days that have to be added to ETA
select low into (tvarv_val)
from tvarvc up to 1 rows
where name = c_tvarv_obj
order by name type numb. "Keeps SLIN and ATC happy without pragmas
endselect.
try.
add_days = tvarv_val.
catch cx_sy_conversion_no_number.
add_days = 0.
endtry.
if add_days = 0.
"Return the original value, business confirmed if tvarv is zero don't shift to next business day
r_deldate = i_etadate.
else.
"Add factory calendar business days to the ETA date.
call function 'BKK_ADD_WORKINGDAY'
exporting
i_date = i_etadate
i_days = add_days
i_calendar1 = c_fac_calendar
importing
e_date = r_deldate
e_return = retcode.
if retcode <> 0.
"Just fall back to the original date if issues with factory calendar
r_deldate = i_etadate.
endif.
endif.
endmethod. "calc_delivery_date
method constructor.
"constants: c_evt_list type string value `SOC|HVR|ETA|DEP|CTA|ARR|CCD|POD`.
"data: event type event_rec,
" event_list type stringtab,
" evt_seq type i.
"field-symbols <e> like line of event_list.
create object mo_gdt_conv type zcl_gdt_conversion.
"Build a list of event codes like 'ZZCODE_SOC', sort order starts at 900 and goes up by increments of 10
"split c_evt_list at '|' into table event_list.
"evt_seq = 890.
"loop at event_list assigning <e>.
" evt_seq = evt_seq + 10.
" concatenate 'ZZCODE_' <e> into event-evt_name.
" event-sort_order = evt_seq.
" append event to mt_events.
"endloop.
mo_system_api = lcl_system_api=>get_instance( ).
event_codes_read_and_cache( ).
endmethod. "constructor
method create_delivery.
data: komdlgn_t type standard table of komdlgn,
vbfs_t type standard table of vbfs,
vbls_t type standard table of vbls,
komdlgn like line of komdlgn_t,
vbsk type vbsk,
vbfs like line of vbfs_t,
conf_errors type standard table of wuebs,
vbls like line of vbls_t,
delivery like i_payload-advance_shipping_notice_mainte-advance_shipping_notification,
gdt_conv type ref to zif_gdt_conversion,
hvr_tstmp type timestamp,
ret_line like line of e_result,
po type ebeln,
soc_tmstmp type timestamp,
auto_gr type ref to zcl_mle_auto_po_gr,
uom_exception type ref to lcx_uom_conversion_exception.
field-symbols: <itm> like line of delivery-item,
<result> like line of e_result,
<conf_err> like line of conf_errors.
clear: e_result, e_vbeln.
create object gdt_conv type zcl_gdt_conversion.
delivery = i_payload-advance_shipping_notice_mainte-advance_shipping_notification.
"Fill header data
komdlgn-lifex = delivery-id-content.
komdlgn-verur = komdlgn-lifex.
komdlgn-rfbel = komdlgn-lifex.
komdlgn-spe_lifex_type = 'V'.
komdlgn-vgtyp = 'V'.
komdlgn-vbtyv = '7'.
komdlgn-lfart = mc_inbd_del_type.
komdlgn-kzazu = 'X'. "So delivery doesn't split by line item
komdlgn-bolnr = delivery-way_bill_id-content.
data temp_quan type p length 16 decimals 3.
temp_quan = delivery-gross_weight_measure-content.
komdlgn-brgew_hdr = temp_quan.
komdlgn-gewei_hdr = uom_iso_to_local( delivery-gross_weight_measure-unit_code ).
temp_quan = delivery-gross_volume_measure-content.
komdlgn-volum_hdr = temp_quan.
komdlgn-voleh_hdr = uom_iso_to_local( delivery-gross_volume_measure-unit_code ).
temp_quan = delivery-net_weight_measure-content.
komdlgn-ntgew_hdr = temp_quan.
"Validations
if komdlgn-brgew_hdr is initial and komdlgn-volum_hdr is initial.
append initial line to e_result assigning <result>.
<result>-type = 'E'.
<result>-id = 'ZM'.
<result>-number = '000'.
<result>-message_v1 = 'Terminating, gross weight or volume must exist'(003).
return.
endif.
try.
gdt_conv->xml_datetime_to_local(
exporting
i_xml_datetime = delivery-actual_execution-yard_arrival_date_time-content
importing
e_date = komdlgn-lfdat
e_time = komdlgn-lfuhr
).
hvr_tstmp = gdt_conv->xml_datetime_to_utc_tstmp( i_xml_datetime = delivery-actual_execution-yard_arrival_date_time-content ).
if hvr_tstmp is initial.
raise exception type cx_conversion_failed. "must have arrival date
endif.
catch cx_conversion_failed.
append initial line to e_result assigning <result>.
<result>-type = 'E'.
<result>-id = 'ZM'.
<result>-number = '000'.
<result>-message_v1 = 'Terminating, unable to parse arrival date:'(003).
<result>-message_v2 = delivery-actual_execution-yard_arrival_date_time-content.
return.
endtry.
"
" We must provide an item number to use here so that the
" GN_DELIVERY_CREATE function enhancement can find the
" correct lines in LIPS. See below for further detail...
"
loop at delivery-item assigning <itm>.
komdlgn-rfpos = sy-tabix * 10.
komdlgn-vgbel = <itm>-purchase_order_reference-id-content.
komdlgn-vgpos = <itm>-purchase_order_reference-item_id.
po = komdlgn-vgbel.
select single inco1 into (komdlgn-inco1)
from ekko
where ebeln = po.
e_result = validate_incoterms( i_incoterms = komdlgn-inco1 i_po = po ).
if lines( e_result ) > 0.
exit.
endif.
"Material not sent from PI..lookup from the PO
select single matnr ematn into (komdlgn-matnr, komdlgn-ematn)
from ekpo
where ebeln = po
and ebelp = komdlgn-vgpos.
if sy-subrc <> 0.
append initial line to e_result assigning <result>.
<result>-type = 'E'.
<result>-id = 'ZM'.
<result>-number = '000'.
<result>-message_v1 = 'Unable to find item:'(001).
<result>-message_v2 = komdlgn-vgpos.
<result>-message_v3 = 'on PO:'(002).
<result>-message_v4 = po.
return.
endif.
try.
komdlgn-lfimg = <itm>-quantity-content.
komdlgn-vrkme = uom_iso_to_local( <itm>-quantity-unit_code ).
komdlgn-brgew = <itm>-gross_weight_measure-content.
komdlgn-gewei = uom_iso_to_local( <itm>-gross_weight_measure-unit_code ).
komdlgn-volum = <itm>-gross_volume_measure-content.
komdlgn-voleh = uom_iso_to_local( <itm>-gross_volume_measure-unit_code ).
komdlgn-anzpk = <itm>-package_quantity-content.
catch lcx_uom_conversion_exception into uom_exception.
append initial line to e_result assigning <result>.
<result>-type = 'E'.
<result>-id = 'ZM'.
<result>-number = '026'.
<result>-message_v1 = uom_exception->get_iso_uom( ).
return.
endtry.
append komdlgn to komdlgn_t.
endloop.
check lines( e_result ) = 0.
"This function creates a confirmation in the PO item but also fills
"additional details like conversion factors etc which are req'd to create delivery
call function 'ME_CONFIRMATION_VIA_EDI'
tables
t_kom = komdlgn_t
errors = conf_errors
exceptions
error_message = 1.
if sy-subrc <> 0 and lines( conf_errors ) = 0.
"FM returns a mix of error returns and message statements..capture the
"messages thrown here.
ret_line-type = sy-msgty.
ret_line-id = sy-msgid.
ret_line-number = sy-msgno.
ret_line-message_v1 = sy-msgv1.
ret_line-message_v2 = sy-msgv2.
ret_line-message_v3 = sy-msgv3.
ret_line-message_v4 = sy-msgv4.
append ret_line to e_result.
endif.
loop at conf_errors assigning <conf_err> where msgty = 'E'.
ret_line-type = <conf_err>-msgty.
ret_line-id = <conf_err>-msgid.
ret_line-number = <conf_err>-msgno.
ret_line-message_v1 = <conf_err>-msgv1.
ret_line-message_v2 = <conf_err>-msgv2.
ret_line-message_v3 = <conf_err>-msgv3.
ret_line-message_v4 = <conf_err>-msgv4.
append ret_line to e_result.
endloop.
check lines( e_result ) = 0.
" The delivery bapi's do not support the fields required and so cannot be used here.
" In SAP's enterprise service for creating deliveries they use the below function. We
" also could not use the standard service as we need upsert functionality.
"
" Additionally set a memory parameter to activate the enhancement Z_GN_DELIVERY_CREATE_VOLUM
" to override the VOLUM field on delivery items with what is provided by this function call.
set parameter id 'Z_INB_DELIVERY_VOLUM' field 'X'.
call function 'GN_DELIVERY_CREATE'
exporting
vbsk_i = vbsk "Has to be provided but we aren't filling as not doing collective processing
no_commit = 'X'
if_synchron = ' '
if_no_deque = 'X'
if_check_uom = 'B'
tables
xkomdlgn = komdlgn_t
xvbfs = vbfs_t "Error log
xvbls = vbls_t "Deliveries created
exceptions
error_message = 1
others = 2 ##fm_subrc_ok.
set parameter id 'Z_INB_DELIVERY_VOLUM' field ' '.
"Read back the first delivery number - if blank then create failed
loop at vbls_t into vbls.
e_vbeln = vbls-vbeln_lif.
exit.
endloop.
loop at vbfs_t into vbfs.
ret_line-type = vbfs-msgty.
ret_line-id = vbfs-msgid.
ret_line-number = vbfs-msgno.
ret_line-message_v1 = vbfs-msgv1.
ret_line-message_v2 = vbfs-msgv2.
ret_line-message_v3 = vbfs-msgv3.
ret_line-message_v4 = vbfs-msgv4.
append ret_line to e_result.
endloop.
delete adjacent duplicates from e_result.
if e_vbeln is not initial.
"Write in all the version zero (plan) events
init_events( i_vbeln = e_vbeln ).
"Add the handover event
add_event( i_eventname = 'ZZCODE_HVR' i_tmstp = hvr_tstmp ).
try.
soc_tmstmp = gdt_conv->xml_datetime_to_utc_tstmp( i_xml_datetime = i_payload-advance_shipping_notice_mainte-message_header-creation_date_time ).
catch cx_conversion_failed.
get time stamp field soc_tmstmp.
endtry.
add_event( i_eventname = 'ZZCODE_SOC' i_tmstp = soc_tmstmp ).
add_relationship( i_vbeln = e_vbeln ).
write_events( i_vbeln = e_vbeln ).
"At this point the delivery is created, but we need to request the goods receipt to be done.
"The delivery MUST not fail if the GR fails, commit what we have done and then let the GR happen
commit work and wait.
"NOTE: Error handling for the GR is via SLG1, interface isn't responsible for recording errors with this component
" lfsnr is 16 chars long so only the first 16 chars of the external delivery note id will be used
data lfsnr type lfsnr.
lfsnr = delivery-delivery_note_external_id.
auto_gr = zcl_mle_auto_po_gr=>get_instance( iv_vbeln = e_vbeln iv_flag = abap_true iv_lfsnr = lfsnr ).
auto_gr->process( ).
else. "no delivery number
read table e_result with key type = 'E' transporting no fields.
if sy-subrc <> 0.
"We shouldn't ever really get here but if we do a error hasn't been caught and no delivery create so raise error
ret_line-type = 'E'.
ret_line-id = 'ZM'.
ret_line-number = '000'.
ret_line-message_v1 = 'Unexpected error, delivery not created'(004).
append ret_line to e_result.
endif.
endif.
endmethod. "create_delivery
method event_codes_read_and_cache.
mt_events = mo_system_api->event_codes_config_read( ).
endmethod. "event_codes_read_and_cache
method get_delivery_from_vendorid.
data: deliveries type standard table of vbeln_vl.
select vbeln into table deliveries
from likp
where lifex = i_lifex
and lfart = mc_inbd_del_type.
if lines( deliveries ) > 1.
raise exception type lcx_lifex_not_unique.
elseif lines( deliveries ) = 1.
read table deliveries into r_vbeln index 1.
endif.
endmethod. "get_docid_from_refid
method get_ech_error_cat.
data: err_code type string.
concatenate i_bapiret-id i_bapiret-number into err_code.
case err_code.
when 'VL046'.
r_ech_cat = 'PRE.TEL'. "Doc is locked, so put it against a retry category
when 'ZM021' or 'ZM022' or 'ZM023' or 'ZM024'.
r_ech_cat = 'PRE.VAE'. "Business process issue
when others.
r_ech_cat = 'PRE'.
endcase.
endmethod. "get_ech_error_cat
method get_event_sort.
data: event like line of me->mt_events.
read table me->mt_events into event with key evt_name = event_code.
if sy-subrc = 0.
r_sort = event-sort_order.
else.
r_sort = -1.
endif.
endmethod. "get_event_sort
method get_evt_hdr_for_mod.
data: header like line of r_header.
constants: c_head_obj type string value 'WSHDRLIKP',
c_head_tpl type string value 'ZZIDHDR'.
select single * into corresponding fields of header
from tsegh where head_hdl = i_handle
and head_obj = c_head_obj
and head_tpl = c_head_tpl.
if sy-subrc = 0.
"Record exists - add change info
header-diag_modif = 'U'.
header-head_unach = sy-uname.
get time stamp field header-head_tstch.
else.
"New record
header-diag_modif = 'I'.
header-head_hdl = i_handle.
header-head_obj = c_head_obj.
header-head_tpl = c_head_tpl.
header-head_unacr = sy-uname.
get time stamp field header-head_tstcr.
endif.
append header to r_header.
endmethod. "get_evt_hdr_for_mod
method get_likp_handle.
data likp_tab type standard table of likpvb.
field-symbols: <rec> like line of likp_tab.
select single handle into rv_handle
from likp where vbeln = i_vbeln.
if sy-subrc <> 0.
"is a create, so have to get it from the memory tabs
call function 'RV_DELIVERY_GET_TABLES_FOM_MEM'
tables
c_xlikp = likp_tab.
loop at likp_tab assigning <rec> where vbeln = i_vbeln.
rv_handle = <rec>-handle.
exit.
endloop.
endif.
endmethod. "get_likp_handle
method if_ech_action~fail.
"Fail is used to mark the message as permanent failure and prevent further processing
call method cl_feh_registration=>s_fail
exporting
i_data = i_data
importing
e_execution_failed = e_execution_failed
e_return_message = e_return_message.
endmethod. "if_ech_action~fail
method if_ech_action~finalize_after_retry_error ##needed.
endmethod. "if_ech_action~finalize_after_retry_error
method if_ech_action~finish.
"Closes out the PPO, but doesn't actually process the message
call method cl_feh_registration=>s_finish
exporting
i_data = i_data
importing
e_execution_failed = e_execution_failed
e_return_message = e_return_message.
endmethod. "if_ech_action~finish
method if_ech_action~no_rollback_on_retry_error ##needed.
endmethod. "if_ech_action~no_rollback_on_retry_error
method if_ech_action~retry.
data: feh_registration type ref to cl_feh_registration,
api_data type zadvance_shipping_notice_main8,
messages type bapiret2_tab.
clear: e_execution_failed, e_return_message.
feh_registration = cl_feh_registration=>s_retry( i_error_object_id = i_error_object_id ).
call method feh_registration->retrieve_data
exporting
i_data = i_data
importing
e_post_mapping_data = api_data.
if api_data is not initial.
me->process_in( exporting input = api_data feh_registration = feh_registration importing r_result = messages ).
endif.
read table messages into e_return_message index 1.
"Dont set e_execution_failed to true as it will display the return as a message in the PPO screen rather than recording it in
feh_registration->resolve_retry( ).
endmethod. "if_ech_action~retry
method if_ech_action~s_create.
if not go_ech_action is bound.
create object go_ech_action.
endif.
r_action_class = go_ech_action.
endmethod. "if_ech_action~s_create
method init_events.
"Method writes a plan (version 0) event with no time for all event types, if not done
"the actuals (version 1) wont show up properly
data: header_tab type tseg_headwritetab,
event_tab type tseg_evenwritetab,
event like line of event_tab,
hdl type tsegguid_likp.
field-symbols <evt> like line of mt_events.
hdl = get_likp_handle( i_vbeln = i_vbeln ).
header_tab = get_evt_hdr_for_mod( i_handle = hdl ).
loop at mt_events assigning <evt>.
event-even = <evt>-evt_name.
event-even_sor = <evt>-sort_order.
event-head_hdl = hdl.
event-even_verty = 0.
event-even_vernu = 0.
event-even_zonfr = sy-zonlo.
event-even_zonto = sy-zonlo.
event-diag_modif = 'I'.
insert event into table event_tab.
endloop.
call function 'TSEG_WRITE_UPD'
exporting
it_tsegh = header_tab
it_tsege = event_tab.
endmethod. "init_events
method map_proxy_to_likp.
r_likp-bolnr = i_msg-way_bill_id-content.
r_likp-traty = i_msg-transport_means-description_code-content.
r_likp-traid = i_msg-transport_means-id. "Container
r_likp-vsart = i_msg-shipping_type-content.
r_likp-zzstt = i_msg-shipment-id-content.
r_likp-zzori_country = i_msg-origin-country_name.
r_likp-zzori_city = i_msg-origin-city_name.
r_likp-zzdest_country = i_msg-destination-country_name.
r_likp-zzdest_city = i_msg-destination-city_name.
r_likp-zzdisp_country = i_msg-port_of_discharge-country_name.
r_likp-zzdisp_city = i_msg-port_of_discharge-city_name.
r_likp-zzload_country = i_msg-port_of_loading-country_name.
r_likp-zzload_city = i_msg-port_of_loading-city_name.
endmethod. "map_proxy_to_likp
method process_change_events.
" If the given EVENT does not yet exist then create it with version 0.
" If the given MESSAGE_EVENT equals the CHANGE_EVENT then add a new
" event with version 1.
data: tmp_eta_time type tsegevtfrs.
select even_tstfr into (tmp_eta_time)
from tsege up to 1 rows
where head_hdl = tseg_hdl
and even = event
and even_verty = '0'
order by even_vernu ascending.
endselect.
if tmp_eta_time is initial.
add_event( i_eventname = event i_tmstp = value i_version = '0' ).
endif.
if message_event = change_event.
add_event( i_eventname = event i_tmstp = value ).
endif.
endmethod. "process_change_events
method process_in.
constants: c_delete_code type zaction_code value '03'.
data: return_messages type bapiret2_tab,
feh_details type ech_str_object,
error_cat type ech_dte_error_category,
main_message like line of return_messages,
vbeln type vbeln_vl,
lifex type lifex.
field-symbols: <delivery> like input-advance_shipping_notice_mainte-advance_shipping_notification.
" Ensure updates are synchronous
set update task local.
assign input-advance_shipping_notice_mainte-advance_shipping_notification to <delivery>.
" External delivery Id
lifex = <delivery>-id-content.
try.
vbeln = get_delivery_from_vendorid( i_lifex = lifex ).
catch lcx_lifex_not_unique.
main_message-type = 'E'.
main_message-id = 'ZM'.
main_message-number = '024'.
main_message-message_v1 = lifex.
append main_message to return_messages.
endtry.
if <delivery>-action_code = c_delete_code.
"Service doesn't support delete operation, business users to manually deal with this
main_message-type = 'E'.
main_message-id = 'ZM'.
main_message-number = '025'.
main_message-message_v1 = vbeln.
append main_message to return_messages.
endif.
if lines( return_messages ) = 0.
if vbeln is initial. "Its a create
if mo_gdt_conv->indicator_in( <delivery>-allowed_behaviours-creation_allowed_indicator ) = abap_false.
main_message-type = 'E'.
main_message-id = 'ZM'.
main_message-number = '023'.
main_message-message_v1 = lifex.
append main_message to return_messages.
else.
create_delivery(
exporting
i_payload = input
importing
e_vbeln = vbeln
e_result = return_messages
).
endif.
else. "Update
if mo_gdt_conv->indicator_in( <delivery>-allowed_behaviours-update_allowed_indicator ) = abap_false.
main_message-type = 'E'.
main_message-id = 'ZM'.
main_message-number = '021'.
main_message-message_v1 = vbeln.
append main_message to return_messages.
else.
update_delivery( exporting i_payload = input i_vbeln = vbeln importing e_result = return_messages ).
endif.
endif.
endif.
r_result = return_messages.
"If errors have occurred - send to FEH
read table return_messages into main_message with key type = 'E'.
if sy-subrc = 0.
error_cat = get_ech_error_cat( i_bapiret = main_message ).
feh_details-objcat = '1'. "table: /SAPPO/S_OBJECT
feh_details-objtype = 'ZINPEX'.
feh_details-objkey = lifex.
try.
"Send the message to post processing office
feh_registration->collect(
exporting
i_single_bo = input
i_error_category = error_cat
i_main_message = main_message
i_messages = return_messages
i_main_object = feh_details
).
catch cx_ai_system_fault ##no_handler. "There's a error tab being returned anyway, even if FEH fails
endtry.
endif.
endmethod. "process_in
method uom_iso_to_local.
local_uom = mo_system_api->unit_of_measure_iso_to_sap( iso_uom ).
endmethod. "uom_iso_to_local
method update_delivery.
constants: c_delivered_status type string value 'ZZCODE_POD'.
data: bdc_ret type standard table of bdcmsgcoll,
soc_tmstmp type timestamp,
event_tmstmp type timestamp,
eta_tmstmp type timestamp,
eta_site_locl type dats,
evt_name type string,
delivery like i_payload-advance_shipping_notice_mainte-advance_shipping_notification,
tseg_hdl type tsegguid_likp,
tmp_eta_time type tsegevtfrs,
bdc_subrc like sy-subrc,
del_date type dats,
inbd_record type likp.
field-symbols: <ret_line> like line of e_result,
<bdc_ret> type bdcmsgcoll.
clear e_result.
delivery = i_payload-advance_shipping_notice_mainte-advance_shipping_notification.
" Get valid event name
concatenate 'ZZCODE_' delivery-inbound_delivery_event-event_type_code-content into evt_name.
if valid_event_name( evt_name ) = abap_false.
append initial line to e_result assigning <ret_line>.
<ret_line>-type = 'E'.
<ret_line>-id = 'ZM'.
<ret_line>-number = '000'.
<ret_line>-message_v1 = 'Event name: '(005).
<ret_line>-message_v2 = evt_name.
<ret_line>-message_v3 = ' not allowed'(006).
return.
endif.
" Get ETA timestamp
try.
eta_tmstmp = mo_gdt_conv->xml_datetime_to_utc_tstmp( i_xml_datetime = delivery-estimated_time_of_arrival-content ).
if eta_tmstmp is initial.
raise exception type cx_conversion_failed. "ETA must be supplied
endif.
catch cx_conversion_failed.
append initial line to e_result assigning <ret_line>.
<ret_line>-type = 'E'.
<ret_line>-id = 'ZM'.
<ret_line>-number = '000'.
<ret_line>-message_v1 = 'Unable to parse ETA Date:'(000).
<ret_line>-message_v2 = delivery-estimated_time_of_arrival-content.
endtry.
check lines( e_result ) = 0.
" Get event timestamp
try.
data event_date_external type string.
event_date_external = i_payload-advance_shipping_notice_mainte-advance_shipping_notification-inbound_delivery_event-event_date-content.
event_tmstmp = mo_gdt_conv->xml_datetime_to_utc_tstmp( i_xml_datetime = event_date_external ).
if event_tmstmp is initial.
raise exception type cx_conversion_failed. "ETA must be supplied
endif.
catch cx_conversion_failed.
append initial line to e_result assigning <ret_line>.
<ret_line>-type = 'E'.
<ret_line>-id = 'ZM'.
<ret_line>-number = '000'.
<ret_line>-message_v1 = 'Unable to parse Event Date:'(007).
<ret_line>-message_v2 = event_date_external.
endtry.
add_event( i_eventname = evt_name i_tmstp = event_tmstmp ).
" Get Delivery Date
if evt_name = mc_eta_final_dest or evt_name = mc_eta_final_dest_changed.
"cl_abap_tstmp=>systemtstmp_utc2syst( exporting utc_tstmp = event_tmstmp importing syst_date = eta_site_locl ).
cl_abap_tstmp=>systemtstmp_utc2syst( exporting utc_tstmp = eta_tmstmp importing syst_date = eta_site_locl ).
del_date = eta_site_locl.
concatenate del_date+6(2) del_date+4(2) del_date+0(4) into del_date.
else.
del_date = '/'. "no_data character for BDC
endif.
if evt_name <> c_delivered_status.
inbd_record = map_proxy_to_likp( delivery ).
"FM is a BDC, had to do this as standard FM's didn't support updating VSART, this combined with the zz fields
"added made BDC the 'best' option
call function 'Z_MLE_UPDATE_INBD_DEL'
exporting
vbeln = i_vbeln
lfdat = del_date
bolnr = inbd_record-bolnr
traty = inbd_record-traty
traid = inbd_record-traid
vsart = inbd_record-vsart
zzstt = inbd_record-zzstt
zzori_country = inbd_record-zzori_country
zzori_city = inbd_record-zzori_city
zzdest_country = inbd_record-zzdest_country
zzdest_city = inbd_record-zzdest_city
zzdisp_country = inbd_record-zzdisp_country
zzdisp_city = inbd_record-zzdisp_city
zzload_country = inbd_record-zzload_country
zzload_city = inbd_record-zzload_city
importing
subrc = bdc_subrc
tables
messtab = bdc_ret.
"The return msgs from bdc can return success even though its error, if the retcode <> 0 then treat it as error
if bdc_subrc <> 0.
loop at bdc_ret assigning <bdc_ret>.
<bdc_ret>-msgtyp = 'E'.
endloop.
endif.
loop at bdc_ret assigning <bdc_ret> where msgtyp = 'E'.
append initial line to e_result assigning <ret_line>.
<ret_line>-type = <bdc_ret>-msgtyp.
<ret_line>-id = <bdc_ret>-msgid.
<ret_line>-number = <bdc_ret>-msgnr.
<ret_line>-message_v1 = <bdc_ret>-msgv1.
<ret_line>-message_v2 = <bdc_ret>-msgv2.
<ret_line>-message_v3 = <bdc_ret>-msgv3.
<ret_line>-message_v4 = <bdc_ret>-msgv4.
endloop.
endif.
check lines( e_result ) = 0.
tseg_hdl = get_likp_handle( i_vbeln = i_vbeln ).
process_change_events(
tseg_hdl = tseg_hdl
value = eta_tmstmp
message_event = evt_name
event = mc_eta_port
change_event = mc_eta_port_changed ).
process_change_events(
tseg_hdl = tseg_hdl
value = eta_tmstmp
message_event = evt_name
event = mc_eta_final_dest
change_event = mc_eta_final_dest_changed ).
" Add the SOC event
try.
soc_tmstmp = mo_gdt_conv->xml_datetime_to_utc_tstmp( i_xml_datetime = i_payload-advance_shipping_notice_mainte-message_header-creation_date_time ).
catch cx_conversion_failed.
get time stamp field soc_tmstmp.
endtry.
add_event( i_eventname = 'ZZCODE_SOC' i_tmstp = soc_tmstmp ).
add_relationship( i_vbeln = i_vbeln ).
write_events( i_vbeln = i_vbeln ).
" Check for CTA time segment changes and send emails
zcl_inb_delivery_timesegments=>send_cta_message( iv_handle = get_likp_handle( i_vbeln ) ).
endmethod. "update_delivery
method validate_incoterms.
data: err like line of r_messages.
if i_incoterms is initial or not contains( val = 'EXW|FCA|FAS|FOB|' sub = i_incoterms case = abap_false ).
err-type = 'E'.
err-id = 'ZM'.
err-number = '022'.
err-message_v1 = i_po.
append err to r_messages.
endif.
endmethod. "validate_incoterms
method valid_event_name.
read table mt_events with key evt_name = event_name transporting no fields.
if sy-subrc = 0.
valid = abap_true.
else.
valid = abap_false.
endif.
endmethod. "valid_event_name
method write_events.
data: header_tab type tseg_headwritetab,
hdl type tsegguid_likp,
events type tseg_evenwritetab,
event like line of events.
field-symbols: <evt> like line of me->mt_events_to_write.
check lines( me->mt_events_to_write ) > 0.
hdl = get_likp_handle( i_vbeln = i_vbeln ).
header_tab = get_evt_hdr_for_mod( i_handle = hdl ).
loop at me->mt_events_to_write assigning <evt>.
event = <evt>.
event-head_hdl = hdl.
insert event into table events.
endloop.
call function 'TSEG_WRITE_UPD'
exporting
it_tsegh = header_tab
it_tsege = events.
clear me->mt_events_to_write.
endmethod. "write_events
method zii_advance_shipg_notice_maint~advance_shipping_notice_replic.
data feh_registration type ref to cl_feh_registration.
data messages type bapiret2_tab.
set update task local.
feh_registration = cl_feh_registration=>s_initialize( ).
me->process_in( exporting input = input feh_registration = feh_registration importing r_result = messages ).
read table messages transporting no fields with key type = 'E'.
if sy-subrc = 0.
"This must be thrown for FEH/ECH to work properly, the 'collect' is don in the process_in method
"but returning this exceptions forces the link to the PPO from SXMB_MONI to be created
cl_proxy_fault=>raise( exception_class_name = 'ZCX_ADVANCE_SHIPPING_NOTICE_MA' bapireturn_tab = messages ).
else.
commit work.
endif.
endmethod. "ZII_ADVANCE_SHIPG_NOTICE_MAINT~ADVANCE_SHIPPING_NOTICE_REPLIC
endclass. "ZCL_ADVANCE_SHIPG_NOTICE_MAINT IMPLEMENTATION
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment