4115 lines
119 KiB
Markdown
4115 lines
119 KiB
Markdown
*&---------------------------------------------------------------------*
|
||
*& Report ZMM_LIFECYCLE_CODE_INHERITANCE
|
||
*&---------------------------------------------------------------------*
|
||
*& Lebenszykluscode-Vererbung für Materialstammdaten
|
||
*& Optimiert für große Stücklisten und Massendatenverarbeitung
|
||
*&---------------------------------------------------------------------*
|
||
REPORT zmm_lifecycle_code_inheritance.
|
||
TYPES: ty_percentage TYPE p LENGTH 8 DECIMALS 2. " <-- NEUE ZEILE HINZUFÜGEN
|
||
*----------------------------------------------------------------------*
|
||
* Typdefinitionen
|
||
*----------------------------------------------------------------------*
|
||
TYPES: BEGIN OF ty_material,
|
||
matnr TYPE matnr,
|
||
mtart TYPE mtart,
|
||
zzlzcod TYPE char4,
|
||
zzlzcodsort TYPE char4, " NEU: Sortiments-Code
|
||
zztyp_f4 TYPE char1,
|
||
pstat TYPE pstat_d,
|
||
mstae TYPE mstae,
|
||
level TYPE i,
|
||
disst TYPE dismm,
|
||
END OF ty_material.
|
||
TYPES: BEGIN OF ty_mattab,
|
||
matnr TYPE matnr,
|
||
mtart TYPE mtart,
|
||
disst TYPE dismm,
|
||
lzcod TYPE char4,
|
||
lzcodsort TYPE char4,
|
||
matnr_top TYPE matnr,
|
||
menge TYPE stpo-menge,
|
||
END OF ty_mattab.
|
||
|
||
DATA: mattab TYPE STANDARD TABLE OF ty_mattab WITH HEADER LINE,
|
||
mattab2 TYPE STANDARD TABLE OF ty_mattab WITH HEADER LINE,
|
||
mattab_dummy TYPE ty_mattab,
|
||
imara TYPE mara,
|
||
stb TYPE STANDARD TABLE OF stpox,
|
||
bg TYPE STANDARD TABLE OF cscmat,
|
||
c_werks TYPE werks_d VALUE '1100'.
|
||
TYPES: BEGIN OF ty_bom_relation,
|
||
parent TYPE matnr,
|
||
child TYPE matnr,
|
||
menge TYPE stpo-menge,
|
||
level TYPE i,
|
||
END OF ty_bom_relation.
|
||
|
||
TYPES: BEGIN OF ty_usage,
|
||
matnr TYPE matnr,
|
||
parent TYPE matnr,
|
||
parent_code TYPE char4,
|
||
usage_qty TYPE kmpmg,
|
||
END OF ty_usage.
|
||
|
||
TYPES: BEGIN OF ty_result,
|
||
matnr TYPE matnr,
|
||
old_code TYPE char4,
|
||
new_code TYPE char4,
|
||
old_code_sort TYPE char4, " NEU
|
||
new_code_sort TYPE char4, " NEU
|
||
|
||
changed_sort TYPE abap_bool, " NEU
|
||
changed TYPE abap_bool,
|
||
message TYPE string,
|
||
END OF ty_result.
|
||
|
||
TYPES: BEGIN OF ty_consumption,
|
||
matnr TYPE matnr,
|
||
menge TYPE menge_d,
|
||
|
||
gsv01 TYPE mver-gsv01, " NEU: Originalverbrauch aus VERBRAUCH_SUMMIEREN
|
||
gsv_korr TYPE mver-gsv01, " NEU: Korrigierter Verbrauch nach MSEG-Logik
|
||
|
||
END OF ty_consumption.
|
||
|
||
* Hash-Tabellen für Performance
|
||
TYPES: tt_material_hash TYPE HASHED TABLE OF ty_material
|
||
WITH UNIQUE KEY matnr.
|
||
TYPES: tt_bom_hash TYPE HASHED TABLE OF ty_bom_relation
|
||
WITH UNIQUE KEY parent child.
|
||
TYPES: tt_usage_std TYPE STANDARD TABLE OF ty_usage.
|
||
TYPES: tt_result TYPE STANDARD TABLE OF ty_result.
|
||
|
||
*----------------------------------------------------------------------*
|
||
* Konstanten
|
||
*----------------------------------------------------------------------*
|
||
CONSTANTS: gc_max_hierarchy_level TYPE i VALUE 4,
|
||
gc_batch_size TYPE i VALUE 5000,
|
||
gc_max_iterations TYPE i VALUE 10,
|
||
gc_code_initial TYPE char4 VALUE 'ZZZZ',
|
||
gc_code_normal TYPE char1 VALUE 'N',
|
||
gc_code_auslauf TYPE char1 VALUE 'A',
|
||
gc_code_ersatz TYPE char1 VALUE 'E',
|
||
gc_code_sonder TYPE char1 VALUE 'S'.
|
||
|
||
*----------------------------------------------------------------------*
|
||
* Globale Datendeklarationen
|
||
*----------------------------------------------------------------------*
|
||
DATA: gt_materials TYPE tt_material_hash,
|
||
gt_bom_relations TYPE tt_bom_hash,
|
||
gt_usages TYPE tt_usage_std,
|
||
gt_results TYPE tt_result,
|
||
gt_consumption TYPE HASHED TABLE OF ty_consumption
|
||
WITH UNIQUE KEY matnr.
|
||
|
||
|
||
DATA: p_mm TYPE abap_bool,
|
||
p_di TYPE abap_bool,
|
||
p_gozin TYPE abap_bool.
|
||
|
||
TABLES mara.
|
||
*----------------------------------------------------------------------*
|
||
* Selektionsbildschirm
|
||
*----------------------------------------------------------------------*
|
||
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
|
||
SELECT-OPTIONS: s_matnr FOR mara-matnr,
|
||
s_typcd FOR mara-mtart.
|
||
PARAMETERS: "p_alles TYPE abap_bool AS CHECKBOX DEFAULT '',
|
||
" p_gozin TYPE abap_bool AS CHECKBOX DEFAULT '',
|
||
p_upda TYPE abap_bool AS CHECKBOX DEFAULT 'X',
|
||
p_test TYPE abap_bool AS CHECKBOX DEFAULT ' '.
|
||
SELECTION-SCREEN END OF BLOCK b1.
|
||
*
|
||
*SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-002.
|
||
* PARAMETERS: p_batch TYPE i DEFAULT 5000,
|
||
* p_maxlv TYPE i DEFAULT 4.
|
||
*SELECTION-SCREEN END OF BLOCK b2.
|
||
|
||
|
||
|
||
* HIER NEUEN BLOCK EINFÜGEN
|
||
SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-003.
|
||
PARAMETERS: p_lzc TYPE abap_bool RADIOBUTTON GROUP mode , "Lebenszykluscode
|
||
p_sort TYPE abap_bool RADIOBUTTON GROUP mode DEFAULT 'X'. "Sortimentscode
|
||
SELECTION-SCREEN END OF BLOCK b3.
|
||
* SELECTION-SCREEN BEGIN OF BLOCK b4 WITH FRAME TITLE TEXT-004.
|
||
* PARAMETERS: p_mm TYPE abap_bool RADIOBUTTON GROUP moe2 , "Lebenszykluscode
|
||
* p_di TYPE abap_bool RADIOBUTTON GROUP moe2 DEFAULT 'X'. "Sortimentscode
|
||
* SELECTION-SCREEN END OF BLOCK b4.
|
||
*----------------------------------------------------------------------*
|
||
* Klassendeklaration für Hauptlogik
|
||
*----------------------------------------------------------------------*
|
||
CLASS lcl_lifecycle_processor DEFINITION.
|
||
PUBLIC SECTION.
|
||
METHODS: constructor,
|
||
execute,
|
||
|
||
get_code_percentage
|
||
IMPORTING iv_code TYPE char4
|
||
RETURNING VALUE(rv_percentage) TYPE ty_percentage ,
|
||
|
||
display_gozinto_graph IMPORTING iv_top_material TYPE matnr,
|
||
|
||
display_results.
|
||
|
||
PRIVATE SECTION.
|
||
" ==================== TYPDEFINITIONEN ====================
|
||
|
||
" Node-Struktur für Gozinto-Graph
|
||
TYPES: BEGIN OF ty_node,
|
||
node_key TYPE matnr,
|
||
parent_key TYPE matnr,
|
||
matnr TYPE matnr,
|
||
menge TYPE kmpmg,
|
||
zzlzcod TYPE char4,
|
||
level TYPE i,
|
||
zzlzcodsort TYPE char4,
|
||
verbrauch_text TYPE string,
|
||
END OF ty_node.
|
||
|
||
|
||
TYPES: tt_nodes TYPE STANDARD TABLE OF ty_node WITH EMPTY KEY.
|
||
|
||
" Material-Level für hierarchische Sortierung
|
||
TYPES: BEGIN OF ty_material_level,
|
||
matnr TYPE matnr,
|
||
level TYPE i,
|
||
END OF ty_material_level.
|
||
|
||
|
||
TYPES: tt_material_level TYPE STANDARD TABLE OF ty_material_level
|
||
WITH NON-UNIQUE DEFAULT KEY.
|
||
|
||
|
||
|
||
" ==================== DATENDEKLARATIONEN ====================
|
||
|
||
|
||
|
||
|
||
" BDC-Daten für MM02-Transaktion
|
||
DATA: gt_bdcdata TYPE TABLE OF bdcdata,
|
||
gt_bdcmsg TYPE TABLE OF bdcmsgcoll.
|
||
|
||
" Progress-Indicator (optional)
|
||
DATA: mo_progress TYPE REF TO cl_progress_indicator.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
" ==================== METHODENDEKLARATIONEN ====================
|
||
|
||
" === HAUPTMETHODEN ===
|
||
METHODS: load_materials,
|
||
load_consumption_data,
|
||
calculate_inheritance,
|
||
calculate_sortiment_inhe,
|
||
update_database,
|
||
update_database_sortiment.
|
||
|
||
" === HIERARCHIE-METHODEN ===
|
||
METHODS:
|
||
|
||
build_hierarchy_from_stb
|
||
IMPORTING iv_top_material TYPE matnr OPTIONAL,
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
transfer_to_new_structures
|
||
IMPORTING iv_top_material TYPE matnr OPTIONAL,
|
||
|
||
build_graph_nodes
|
||
IMPORTING iv_matnr TYPE matnr
|
||
iv_parent_key TYPE matnr
|
||
iv_menge TYPE kmpmg
|
||
iv_level TYPE i
|
||
CHANGING ct_nodes TYPE tt_nodes.
|
||
|
||
" === VERWENDUNGS-METHODEN ===
|
||
METHODS: load_usages_for_components .
|
||
|
||
|
||
" === BERECHNUNGS-METHODEN ===
|
||
METHODS: apply_dominance_rules
|
||
IMPORTING iv_matnr TYPE matnr
|
||
RETURNING VALUE(rv_code) TYPE char4,
|
||
|
||
apply_sortiment_rules
|
||
IMPORTING iv_matnr TYPE matnr
|
||
RETURNING VALUE(rv_code) TYPE char4,
|
||
|
||
calculate_sonder_percentage
|
||
IMPORTING iv_matnr TYPE matnr
|
||
iv_type TYPE char2 OPTIONAL
|
||
RETURNING VALUE(rv_percentage) TYPE ty_percentage.
|
||
|
||
|
||
|
||
" === STÜCKLISTEN-METHODEN ===
|
||
METHODS: read_bill_of_material
|
||
IMPORTING iv_matnr TYPE matnr,
|
||
|
||
|
||
" === DATENBANK-UPDATE-METHODEN ===
|
||
|
||
|
||
|
||
|
||
bdc_dynpro
|
||
IMPORTING
|
||
iv_program TYPE bdc_prog
|
||
iv_dynpro TYPE bdc_dynr,
|
||
|
||
bdc_field
|
||
IMPORTING
|
||
iv_fnam TYPE fnam_____4
|
||
iv_fval TYPE bdc_fval,
|
||
|
||
bdc_transaction
|
||
IMPORTING
|
||
iv_tcode TYPE tcode,
|
||
|
||
Write_to_journal
|
||
IMPORTING
|
||
iv_matnr TYPE matnr
|
||
iv_old_lzcod TYPE char4
|
||
iv_new_lzcod TYPE char4
|
||
iv_old_lzcodsort TYPE char4 OPTIONAL
|
||
iv_new_lzcodsort TYPE char4 OPTIONAL.
|
||
|
||
" === DEBUG-METHODEN ===
|
||
METHODS: debug_show_relations,
|
||
|
||
is_debug_material
|
||
IMPORTING iv_matnr TYPE matnr
|
||
RETURNING VALUE(rv_debug) TYPE abap_bool,
|
||
|
||
debug_problem_material
|
||
IMPORTING
|
||
iv_matnr TYPE matnr
|
||
iv_phase TYPE string,
|
||
|
||
debug_collect_info
|
||
IMPORTING iv_matnr TYPE matnr
|
||
RETURNING VALUE(rt_info) TYPE string_table.
|
||
|
||
|
||
|
||
" === HILFS-METHODEN ===
|
||
|
||
ENDCLASS.
|
||
|
||
*----------------------------------------------------------------------*
|
||
* Klassenimplementierung
|
||
*----------------------------------------------------------------------*
|
||
CLASS lcl_lifecycle_processor IMPLEMENTATION.
|
||
|
||
METHOD constructor.
|
||
"CREATE OBJECT mo_progress.
|
||
ENDMETHOD.
|
||
|
||
METHOD execute.
|
||
DATA: lv_start_time TYPE timestampl,
|
||
lv_end_time TYPE timestampl.
|
||
|
||
GET TIME STAMP FIELD lv_start_time.
|
||
|
||
" *** NEU: Job deaktivieren (nur produktiv) ***"
|
||
PERFORM deactivate_job.
|
||
|
||
" Phase 1: Datenladen"
|
||
""Write: / 'Phase 1: Lade Materialstammdaten...'.
|
||
load_materials( ).
|
||
|
||
" ===== HIER EINFÜGEN: Debug nach dem Laden ====="
|
||
me->debug_show_relations( ).
|
||
""Write: / 'Phase 2: Lade Stücklistenbeziehungen...'.
|
||
|
||
|
||
""Write: / 'Phase 3: Erweitere Hierarchie...'.
|
||
"expand_hierarchy( ).
|
||
|
||
""Write: / 'Phase 4: Lade Verbrauchsdaten...'.
|
||
load_consumption_data( ).
|
||
|
||
" Phase 5: Berechnungen basierend auf Auswahl durchführen"
|
||
IF p_lzc = abap_true.
|
||
" Modus: Nur Lebenszykluscode (ZZLZCOD)"
|
||
""Write: / 'Phase 5: Berechne Lebenszykluscode-Vererbung (ZZLZCOD)...'.
|
||
PERFORM save_vknr_codes.
|
||
calculate_inheritance( ).
|
||
PERFORM restore_vknr_codes.
|
||
|
||
" PERFORM protect_vknr_from_update USING '1'."
|
||
IF p_upda = abap_true AND p_test = abap_false.
|
||
""Write: / 'Phase 6: Aktualisiere Datenbank für ZZLZCOD...'.
|
||
update_database( ).
|
||
ENDIF.
|
||
|
||
ELSEIF p_sort = abap_true.
|
||
" Modus: Nur Sortimentscode (ZZLZCODSORT)"
|
||
""Write: / 'Phase 5: Berechne Sortiments-Lebenszykluscode-Vererbung (ZZLZCODSORT)...'.
|
||
calculate_sortiment_inhe( ).
|
||
|
||
IF p_upda = abap_true AND p_test = abap_false.
|
||
""Write: / 'Phase 6: Aktualisiere Datenbank für ZZLZCODSORT...'.
|
||
update_database_sortiment( ).
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
|
||
GET TIME STAMP FIELD lv_end_time.
|
||
DATA(lv_runtime) = lv_end_time - lv_start_time.
|
||
""Write: / |Gesamtlaufzeit: { lv_runtime } Sekunden|.
|
||
|
||
" *** NEU: Job reaktivieren (nur produktiv) ***"
|
||
PERFORM reactivate_job.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD transfer_to_new_structures.
|
||
" Parameter-Deklaration in der Klassendeklaration hinzufügen:
|
||
" IMPORTING iv_top_material TYPE matnr OPTIONAL
|
||
|
||
DATA: lv_material_count TYPE i,
|
||
lv_relation_count TYPE i.
|
||
|
||
""Write: / '=== START transfer_to_new_structures ==='.
|
||
|
||
" Zeige welches TOP-Material übergeben wurde
|
||
IF iv_top_material IS NOT INITIAL.
|
||
""Write: / 'TOP-Material aus Parameter:', iv_top_material.
|
||
ELSE.
|
||
""Write: / 'WARNUNG: Kein TOP-Material übergeben!'.
|
||
ENDIF.
|
||
|
||
" Schritt 1: Baue Hierarchie mit dem KORREKTEN TOP-Material
|
||
me->build_hierarchy_from_stb( iv_top_material = iv_top_material ).
|
||
|
||
|
||
|
||
" Validierung
|
||
lv_material_count = lines( gt_materials ).
|
||
lv_relation_count = lines( gt_bom_relations ).
|
||
|
||
""Write: / 'Materialien in gt_materials:', lv_material_count.
|
||
""Write: / 'Beziehungen in gt_bom_relations:', lv_relation_count.
|
||
""Write: / '=== ENDE transfer_to_new_structures ==='.
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
|
||
METHOD debug_show_relations.
|
||
DATA: lt_sorted TYPE STANDARD TABLE OF ty_bom_relation,
|
||
lv_current_child TYPE matnr,
|
||
lv_parent_count TYPE i,
|
||
lt_parent_codes TYPE STANDARD TABLE OF char4.
|
||
|
||
""Write: / ''.
|
||
""Write: / '╔══════════════════════════════════════════════════╗'.
|
||
""Write: / '║ VOLLSTÄNDIGE PARENT-CHILD ANALYSE ║'.
|
||
""Write: / '╚══════════════════════════════════════════════════╝'.
|
||
|
||
lt_sorted = gt_bom_relations.
|
||
SORT lt_sorted BY child parent.
|
||
|
||
" Fokus auf die kritischen Materialien
|
||
DATA: lt_critical_mats TYPE STANDARD TABLE OF matnr.
|
||
APPEND 'AR13025' TO lt_critical_mats.
|
||
APPEND 'AR13026' TO lt_critical_mats.
|
||
APPEND 'AR15117' TO lt_critical_mats.
|
||
|
||
LOOP AT lt_critical_mats INTO DATA(lv_critical_mat).
|
||
""Write: / ''.
|
||
""Write: / '▶▶▶ MATERIAL:', lv_critical_mat.
|
||
|
||
CLEAR: lv_parent_count, lt_parent_codes.
|
||
|
||
LOOP AT lt_sorted INTO DATA(ls_rel) WHERE child = lv_critical_mat.
|
||
ADD 1 TO lv_parent_count.
|
||
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel-parent
|
||
INTO DATA(ls_parent).
|
||
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_rel-parent
|
||
INTO DATA(ls_cons).
|
||
|
||
IF sy-subrc = 0.
|
||
""Write: / ' ← Parent:', ls_rel-parent,
|
||
"'Code:', ls_parent-zzlzcod,
|
||
"'Menge:', ls_rel-menge,
|
||
"'Verbrauch:', ls_cons-gsv_korr.
|
||
ELSE.
|
||
""Write: / ' ← Parent:', ls_rel-parent,
|
||
"'Code:', ls_parent-zzlzcod,
|
||
"'Menge:', ls_rel-menge,
|
||
"'Verbrauch: 0'.
|
||
ENDIF.
|
||
|
||
APPEND ls_parent-zzlzcod TO lt_parent_codes.
|
||
ENDLOOP.
|
||
|
||
""Write: / ' └─ Anzahl Parents:', lv_parent_count.
|
||
|
||
" Erwartete Berechnung
|
||
IF lv_critical_mat = 'AR13025'.
|
||
""Write: / ' ERWARTUNG: Sollte A8T0 werden (mit 46781, 47187 als Parents)'.
|
||
ELSEIF lv_critical_mat = 'AR15117'.
|
||
""Write: / ' ERWARTUNG: Sollte A3T0 werden (mit 46781, 47187, 48006)'.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
""Write: / '══════════════════════════════════════════════════'.
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
METHOD read_bill_of_material.
|
||
|
||
ENDMETHOD.
|
||
|
||
METHOD load_materials.
|
||
DATA: lt_selected_mats TYPE STANDARD TABLE OF ty_material,
|
||
lt_all_relations TYPE STANDARD TABLE OF ty_bom_relation.
|
||
|
||
" 1. Lade selektierte Materialien inkl. Status
|
||
SELECT matnr, mtart, zzlzcod, zzlzcodsort, zztyp_f4, pstat, disst, mstae
|
||
FROM mara
|
||
INTO CORRESPONDING FIELDS OF TABLE @lt_selected_mats
|
||
WHERE matnr IN @s_matnr
|
||
AND mtart IN @s_typcd.
|
||
|
||
" ========================================
|
||
" *** NEU: STATUS 99 BEHANDLUNG ***
|
||
" ========================================
|
||
DATA: lv_status99_count TYPE i.
|
||
|
||
LOOP AT lt_selected_mats ASSIGNING FIELD-SYMBOL(<fs_mat_status>).
|
||
IF <fs_mat_status>-mstae = '99'.
|
||
" Status 99 = Ausgelaufen → ZZLZCODSORT leeren
|
||
<fs_mat_status>-zzlzcodsort = ''.
|
||
ADD 1 TO lv_status99_count.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
IF lv_status99_count > 0.
|
||
WRITE: / 'Status 99 (Ausgelaufen):', lv_status99_count, 'Materialien → ZZLZCODSORT geleert'.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" *** FILTER NUR TOP-MATERIALIEN ***
|
||
" ========================================
|
||
DATA: lt_top_mats TYPE STANDARD TABLE OF ty_material,
|
||
lv_filtered_mats TYPE i.
|
||
|
||
LOOP AT lt_selected_mats INTO DATA(ls_mat_check).
|
||
|
||
" Prüfe: Existiert Material als KOMPNR (Kind) in ZPOWERBI_VC?
|
||
SELECT COUNT(*) FROM zpowerbi_vc
|
||
INTO @DATA(lv_child_count)
|
||
WHERE kompnr = @ls_mat_check-matnr.
|
||
|
||
IF lv_child_count = 0.
|
||
" Material hat KEINE Parents → IST TOP-Material
|
||
APPEND ls_mat_check TO lt_top_mats.
|
||
ELSE.
|
||
" Material hat Parents → AUSSCHLIESSEN
|
||
ADD 1 TO lv_filtered_mats.
|
||
ENDIF.
|
||
|
||
ENDLOOP.
|
||
|
||
" *** Ersetze lt_selected_mats durch lt_top_mats ***
|
||
CLEAR lt_selected_mats.
|
||
lt_selected_mats = lt_top_mats.
|
||
|
||
" 2. Füge zu gt_materials hinzu
|
||
LOOP AT lt_selected_mats INTO DATA(ls_mat).
|
||
INSERT ls_mat INTO TABLE gt_materials.
|
||
ENDLOOP.
|
||
|
||
" 3. Für jedes Material: Lade Stückliste UND Verwendungen
|
||
LOOP AT lt_selected_mats INTO ls_mat.
|
||
|
||
" 3a. Top-Down: Stückliste lesen
|
||
me->read_bill_of_material( iv_matnr = ls_mat-matnr ).
|
||
me->build_hierarchy_from_stb( iv_top_material = ls_mat-matnr ).
|
||
|
||
CLEAR: stb, bg.
|
||
|
||
me->debug_problem_material(
|
||
iv_matnr = ls_mat-matnr
|
||
iv_phase = 'LOAD' ).
|
||
ENDLOOP.
|
||
|
||
me->load_usages_for_components( ).
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
METHOD load_usages_for_components.
|
||
DATA: lt_all_components TYPE SORTED TABLE OF matnr WITH UNIQUE KEY table_line,
|
||
lt_vc_raw TYPE STANDARD TABLE OF zpowerbi_vc,
|
||
ls_bom_rel TYPE ty_bom_relation,
|
||
ls_material TYPE ty_material,
|
||
lv_added_count TYPE i,
|
||
lv_vc_count TYPE i.
|
||
|
||
" ========================================
|
||
" SCHRITT 1: SAMMLE ALLE KOMPONENTEN
|
||
" ========================================
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel).
|
||
INSERT ls_rel-child INTO TABLE lt_all_components.
|
||
ENDLOOP.
|
||
|
||
" ========================================
|
||
" SCHRITT 2: FÜR JEDE KOMPONENTE - BOTTOM-UP VIA ZPOWERBI_VC
|
||
" ========================================
|
||
LOOP AT lt_all_components INTO DATA(lv_component).
|
||
|
||
CLEAR lt_vc_raw.
|
||
|
||
SELECT matnr, kompnr, menge, stufe
|
||
FROM zpowerbi_vc
|
||
INTO CORRESPONDING FIELDS OF TABLE @lt_vc_raw
|
||
WHERE kompnr = @lv_component.
|
||
|
||
IF sy-subrc = 0.
|
||
lv_vc_count = lv_vc_count + lines( lt_vc_raw ).
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 3: VERARBEITE JEDE VERWENDUNG
|
||
" ========================================
|
||
LOOP AT lt_vc_raw INTO DATA(ls_vc_raw).
|
||
|
||
READ TABLE gt_bom_relations
|
||
WITH KEY parent = ls_vc_raw-matnr
|
||
child = lv_component
|
||
TRANSPORTING NO FIELDS.
|
||
|
||
IF sy-subrc <> 0.
|
||
CLEAR ls_bom_rel.
|
||
ls_bom_rel-parent = ls_vc_raw-matnr.
|
||
ls_bom_rel-child = lv_component.
|
||
|
||
DATA(lv_menge_char) = ls_vc_raw-menge.
|
||
DATA(lv_menge_numeric) = CONV menge_d( lv_menge_char ).
|
||
ls_bom_rel-menge = lv_menge_numeric.
|
||
|
||
DATA(lv_stufe_char) = ls_vc_raw-stufe.
|
||
DATA(lv_stufe_int) = CONV i( lv_stufe_char ).
|
||
ls_bom_rel-level = lv_stufe_int.
|
||
|
||
INSERT ls_bom_rel INTO TABLE gt_bom_relations.
|
||
ADD 1 TO lv_added_count.
|
||
|
||
" Parent-Material zu gt_materials hinzufügen falls unbekannt
|
||
READ TABLE gt_materials WITH KEY matnr = ls_bom_rel-parent
|
||
TRANSPORTING NO FIELDS.
|
||
IF sy-subrc <> 0.
|
||
CLEAR ls_material.
|
||
SELECT SINGLE matnr, mtart, zzlzcod, zzlzcodsort,
|
||
zztyp_f4, pstat, disst, mstae
|
||
FROM mara
|
||
INTO CORRESPONDING FIELDS OF @ls_material
|
||
WHERE matnr = @ls_bom_rel-parent.
|
||
|
||
IF sy-subrc = 0.
|
||
" *** NEU: Status 99 prüfen ***
|
||
IF ls_material-mstae = '99'.
|
||
ls_material-zzlzcodsort = ''.
|
||
ENDIF.
|
||
INSERT ls_material INTO TABLE gt_materials.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
ENDLOOP.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
METHOD load_consumption_data.
|
||
DATA: w_datum_von LIKE sy-datum,
|
||
w_datum_bis LIKE sy-datum,
|
||
w_mblnr_von TYPE mkpf-mblnr,
|
||
w_mblnr_bis TYPE mkpf-mblnr,
|
||
lt_all_materials TYPE SORTED TABLE OF matnr WITH UNIQUE KEY table_line,
|
||
ls_consumption TYPE ty_consumption,
|
||
lt_mseg TYPE STANDARD TABLE OF mseg.
|
||
|
||
""Write: / 'Lade Verbrauchsdaten...'.
|
||
|
||
" Sammle ALLE Materialien (Parents UND Children)
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel).
|
||
INSERT ls_rel-parent INTO TABLE lt_all_materials.
|
||
INSERT ls_rel-child INTO TABLE lt_all_materials.
|
||
ENDLOOP.
|
||
|
||
" Zusätzlich: Alle Materialien aus gt_materials
|
||
LOOP AT gt_materials INTO DATA(ls_mat).
|
||
INSERT ls_mat-matnr INTO TABLE lt_all_materials.
|
||
ENDLOOP.
|
||
|
||
IF lt_all_materials IS INITIAL.
|
||
""Write: / 'Keine Materialien für Verbrauchsermittlung gefunden.'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Datumsbereiche
|
||
w_datum_bis = sy-datum.
|
||
WHILE w_datum_bis+4(2) = sy-datum+4(2).
|
||
w_datum_bis = w_datum_bis - 1.
|
||
ENDWHILE.
|
||
w_datum_von = w_datum_bis - 360.
|
||
w_datum_von+6(2) = '01'.
|
||
|
||
" MKPF-Belegnummern-Bereich
|
||
SELECT MIN( mblnr ) INTO @w_mblnr_von
|
||
FROM mkpf
|
||
WHERE budat >= @w_datum_von AND blart = 'WL'.
|
||
|
||
SELECT MAX( mblnr ) INTO @w_mblnr_bis
|
||
FROM mkpf
|
||
WHERE budat <= @w_datum_bis AND blart = 'WL'.
|
||
|
||
" Verbrauch für ALLE Materialien ermitteln
|
||
LOOP AT lt_all_materials INTO DATA(lv_matnr).
|
||
CLEAR ls_consumption.
|
||
ls_consumption-matnr = lv_matnr.
|
||
|
||
" Basis-Verbrauch
|
||
CALL FUNCTION 'VERBRAUCH_SUMMIEREN'
|
||
EXPORTING
|
||
abdatum = w_datum_von
|
||
bisdatum = w_datum_bis
|
||
matnr = lv_matnr
|
||
periv = ' '
|
||
perkz = 'M'
|
||
werks = '1100'
|
||
IMPORTING
|
||
gesamtverbrauch = ls_consumption-gsv01
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
" MSEG-Korrekturen
|
||
CLEAR lt_mseg.
|
||
SELECT matnr, menge, bwart, shkzg
|
||
FROM mseg
|
||
INTO CORRESPONDING FIELDS OF TABLE @lt_mseg
|
||
WHERE matnr = @lv_matnr
|
||
AND werks = '1100'
|
||
AND lgort = '0001'
|
||
AND sobkz = 'E'
|
||
AND mblnr BETWEEN @w_mblnr_von AND @w_mblnr_bis
|
||
AND bwart IN ('601', '602', '651', '654').
|
||
|
||
LOOP AT lt_mseg INTO DATA(ls_mseg).
|
||
DATA(lv_menge) = ls_mseg-menge.
|
||
IF ls_mseg-shkzg = 'S'.
|
||
lv_menge = lv_menge * -1.
|
||
ENDIF.
|
||
ls_consumption-gsv01 = ls_consumption-gsv01 + lv_menge.
|
||
ENDLOOP.
|
||
|
||
ls_consumption-gsv_korr = ls_consumption-gsv01.
|
||
INSERT ls_consumption INTO TABLE gt_consumption.
|
||
ENDLOOP.
|
||
|
||
|
||
|
||
" !!! DEBUG: Verbrauch für kritische Materialien
|
||
IF lv_matnr = 'B58383' OR lv_matnr = 'B53618' OR
|
||
lv_matnr = 'B56383' OR lv_matnr = 'B69327'.
|
||
"Write: / '!!! VERBRAUCH für', lv_matnr, ':'.
|
||
"Write: / '!!! gsv01 (Original):', ls_consumption-gsv01.
|
||
"Write: / '!!! gsv_korr (Korrigiert):', ls_consumption-gsv_korr.
|
||
"Write: / '!!! Anzahl MSEG-Korrekturen:', lines( lt_mseg ).
|
||
ENDIF.
|
||
""Write: / |{ lines( gt_consumption ) } Verbrauchsdatensätze geladen|.
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
METHOD calculate_inheritance.
|
||
DATA: lt_materials_sorted TYPE STANDARD TABLE OF ty_material,
|
||
|
||
|
||
ls_material TYPE ty_material,
|
||
ls_child TYPE ty_material,
|
||
lv_changed TYPE abap_bool,
|
||
lv_iteration TYPE i.
|
||
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ STARTE VERERBUNGSBERECHNUNG ║'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
|
||
" ========================================
|
||
" SCHRITT 1: INITIALISIERE ALLE MATERIALIEN MIT LEVEL 999
|
||
" ========================================
|
||
LOOP AT gt_materials INTO ls_material.
|
||
ls_material-level = 999.
|
||
MODIFY TABLE gt_materials FROM ls_material.
|
||
ENDLOOP.
|
||
|
||
" ========================================
|
||
" SCHRITT 2: SETZE LEVEL FÜR TOP-MATERIALIEN
|
||
" ========================================
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ HIERARCHIE-LEVEL BESTIMMUNG ║'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
|
||
"Write: / 'Materialien initialisiert:', lines( gt_materials ).
|
||
|
||
LOOP AT gt_materials INTO ls_material.
|
||
READ TABLE gt_bom_relations WITH KEY child = ls_material-matnr
|
||
TRANSPORTING NO FIELDS.
|
||
|
||
IF sy-subrc <> 0.
|
||
ls_material-level = 0.
|
||
MODIFY TABLE gt_materials FROM ls_material.
|
||
"Write: / ' TOP-Material (Level 0):', ls_material-matnr.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
" ========================================
|
||
" SCHRITT 3: ÜBERNEHME LEVEL AUS gt_bom_relations
|
||
" ========================================
|
||
DATA lv_level_changes TYPE i VALUE 0.
|
||
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel).
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel-child
|
||
INTO ls_child.
|
||
|
||
IF sy-subrc = 0.
|
||
IF ls_rel-level < ls_child-level.
|
||
ls_child-level = ls_rel-level.
|
||
MODIFY TABLE gt_materials FROM ls_child.
|
||
ADD 1 TO lv_level_changes.
|
||
|
||
IF lv_level_changes <= 10.
|
||
"Write: / ' Level gesetzt:', ls_child-matnr, 'Level:', ls_child-level.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
"Write: / 'Level-Änderungen:', lv_level_changes.
|
||
|
||
" ========================================
|
||
" SCHRITT 4: SORTIERE NACH LEVEL
|
||
" ========================================
|
||
"Write: / ''.
|
||
"Write: / '═══ FINALE VERARBEITUNGSREIHENFOLGE ═══'.
|
||
|
||
LOOP AT gt_materials INTO ls_material.
|
||
APPEND ls_material TO lt_materials_sorted.
|
||
ENDLOOP.
|
||
|
||
SORT lt_materials_sorted BY level ASCENDING matnr ASCENDING.
|
||
|
||
"Write: / 'Verarbeite', lines( lt_materials_sorted ), 'Materialien in hierarchischer Reihenfolge'.
|
||
|
||
LOOP AT lt_materials_sorted INTO ls_material.
|
||
IF sy-tabix <= 10.
|
||
"Write: / ' Material:', ls_material-matnr, 'Level:', ls_material-level.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
" ========================================
|
||
" SCHRITT 5: VERARBEITE HIERARCHISCH + BEFÜLLE gt_results
|
||
" ========================================
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
DO 10 TIMES.
|
||
lv_iteration = sy-index.
|
||
lv_changed = abap_false.
|
||
|
||
"Write: / ''.
|
||
"Write: / '──── Iteration', lv_iteration, '────'.
|
||
|
||
DATA lv_change_count TYPE i VALUE 0.
|
||
|
||
LOOP AT lt_materials_sorted INTO ls_material.
|
||
" Hole aktuellen Code
|
||
READ TABLE gt_materials WITH KEY matnr = ls_material-matnr
|
||
INTO DATA(ls_current).
|
||
|
||
" Speichere alten Code
|
||
|
||
|
||
|
||
|
||
|
||
DATA(lv_old_code) = ls_current-zzlzcod.
|
||
|
||
|
||
" Berechne neuen Code
|
||
DATA(lv_new_code) = me->apply_dominance_rules( iv_matnr = ls_material-matnr ).
|
||
|
||
" Prüfe ob Änderung
|
||
|
||
|
||
|
||
|
||
|
||
|
||
IF lv_new_code <> ls_current-zzlzcod.
|
||
ls_current-zzlzcod = lv_new_code.
|
||
MODIFY TABLE gt_materials FROM ls_current.
|
||
lv_changed = abap_true.
|
||
ADD 1 TO lv_change_count.
|
||
ENDIF.
|
||
|
||
" *** NEU: BEFÜLLE gt_results ***
|
||
DATA(ls_result) = VALUE ty_result(
|
||
matnr = ls_material-matnr
|
||
old_code = lv_old_code
|
||
new_code = lv_new_code
|
||
changed = COND #( WHEN lv_new_code <> lv_old_code THEN 'X' ELSE '-' )
|
||
message = ''
|
||
).
|
||
|
||
" Prüfe ob bereits in gt_results
|
||
READ TABLE gt_results WITH KEY matnr = ls_material-matnr
|
||
TRANSPORTING NO FIELDS.
|
||
|
||
IF sy-subrc = 0.
|
||
|
||
|
||
MODIFY TABLE gt_results FROM ls_result.
|
||
|
||
ELSE.
|
||
|
||
INSERT ls_result INTO TABLE gt_results.
|
||
ENDIF.
|
||
|
||
ENDLOOP.
|
||
|
||
"Write: / 'Iteration', lv_iteration, ':', lv_change_count, 'Änderungen'.
|
||
|
||
IF lv_changed = abap_false.
|
||
"Write: / '*** Keine Änderungen mehr - Vererbung abgeschlossen ***'.
|
||
EXIT.
|
||
ENDIF.
|
||
ENDDO.
|
||
|
||
"Write: / '════════════════════════════════════════════════'.
|
||
|
||
" ========================================
|
||
" SCHRITT 6: DEBUG - ZEIGE gt_results
|
||
" ========================================
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ gt_results BEFÜLLT ║'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
"Write: / 'Anzahl Einträge in gt_results:', lines( gt_results ).
|
||
|
||
DATA lv_changed_count TYPE i VALUE 0.
|
||
LOOP AT gt_results INTO ls_result WHERE changed = 'X'.
|
||
ADD 1 TO lv_changed_count.
|
||
ENDLOOP.
|
||
|
||
"Write: / 'Davon geändert:', lv_changed_count.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD apply_dominance_rules.
|
||
DATA: lt_parents TYPE tt_usage_std,
|
||
lv_has_a TYPE abap_bool,
|
||
lv_has_e TYPE abap_bool,
|
||
lv_has_n TYPE abap_bool,
|
||
lv_has_s TYPE abap_bool,
|
||
lv_count_a TYPE i,
|
||
lv_count_e TYPE i,
|
||
lv_count_n TYPE i,
|
||
lv_position_4 TYPE char1 VALUE '0',
|
||
lv_total_verbrauch TYPE p DECIMALS 2,
|
||
lv_a_verbrauch TYPE p DECIMALS 2,
|
||
lv_anteil TYPE p DECIMALS 2,
|
||
lv_parent_verbrauch TYPE p DECIMALS 2,
|
||
lv_effektive_menge TYPE p DECIMALS 3,
|
||
lv_mara_code TYPE char4,
|
||
lv_parent_count TYPE i,
|
||
lv_all_n0x0 TYPE abap_bool VALUE abap_true,
|
||
lv_parent_count_debug TYPE i.
|
||
|
||
" ========================================
|
||
" DEBUG: START
|
||
" ========================================
|
||
"Write: / ''.
|
||
"Write: / '╔══════════════════════════════════════════════════╗'.
|
||
"Write: / '║ apply_dominance_rules für Material:', iv_matnr.
|
||
"Write: / '╚══════════════════════════════════════════════════╝'.
|
||
|
||
" ========================================
|
||
" SCHRITT 1: HOLE AKTUELLES MATERIAL
|
||
" ========================================
|
||
READ TABLE gt_materials WITH KEY matnr = iv_matnr
|
||
INTO DATA(ls_current_mat).
|
||
IF sy-subrc <> 0.
|
||
"Write: / '*** FEHLER: Material nicht in gt_materials gefunden!'.
|
||
rv_code = 'N0X0'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
"Write: / ' Aktueller Code:', ls_current_mat-zzlzcod.
|
||
"Write: / ' Level:', ls_current_mat-level.
|
||
|
||
" Position 4 Behandlung
|
||
IF strlen( ls_current_mat-zzlzcod ) >= 4.
|
||
lv_position_4 = ls_current_mat-zzlzcod+3(1).
|
||
ENDIF.
|
||
"Write: / ' Position 4:', lv_position_4.
|
||
|
||
" ========================================
|
||
" SCHRITT 2: ZZZZ-SPEZIALREGEL
|
||
" ========================================
|
||
IF ls_current_mat-zzlzcod = 'ZZZZ'.
|
||
SELECT SINGLE zzlzcod FROM mara
|
||
INTO @lv_mara_code
|
||
WHERE matnr = @iv_matnr.
|
||
IF lv_mara_code = 'ZZZZ'.
|
||
"Write: / ' -> ZZZZ-Material (unveränderbar)'.
|
||
rv_code = 'ZZZZ'.
|
||
RETURN.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 3: N0X1 SPEZIALFALL
|
||
" ========================================
|
||
IF ls_current_mat-zzlzcod = 'N0X1'.
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel_check)
|
||
WHERE child = iv_matnr.
|
||
ADD 1 TO lv_parent_count.
|
||
ENDLOOP.
|
||
|
||
IF lv_parent_count = 1.
|
||
READ TABLE gt_bom_relations INTO ls_rel_check
|
||
WITH KEY child = iv_matnr.
|
||
IF sy-subrc = 0.
|
||
READ TABLE gt_materials INTO DATA(ls_single_parent)
|
||
WITH KEY matnr = ls_rel_check-parent.
|
||
IF sy-subrc = 0 AND ls_single_parent-zzlzcod(1) = 'A'.
|
||
"Write: / ' -> N0X1 Spezialfall: Bleibt N0X1'.
|
||
rv_code = 'N0X1'.
|
||
RETURN.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 4: ZÄHLE PARENTS (DEBUG)
|
||
" ========================================
|
||
CLEAR lv_parent_count_debug.
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel_debug)
|
||
WHERE child = iv_matnr.
|
||
ADD 1 TO lv_parent_count_debug.
|
||
ENDLOOP.
|
||
|
||
"Write: / ' Anzahl Parents in gt_bom_relations:', lv_parent_count_debug.
|
||
|
||
|
||
"INS 18.1.26
|
||
IF lv_parent_count_debug = 0.
|
||
" *** TOP-MATERIAL (keine Parents) → NICHT ANFASSEN ***
|
||
" Defensive Variante: Prüft alle Fälle explizit
|
||
|
||
IF ls_current_mat-zzlzcod = 'ZZZZ'.
|
||
" ZZZZ bleibt ZZZZ
|
||
rv_code = 'ZZZZ'.
|
||
ELSEIF ls_current_mat-zzlzcod IS INITIAL.
|
||
" Leerer Code bleibt leer
|
||
rv_code = ''.
|
||
ELSE.
|
||
" Vorhandener Code bleibt unverändert
|
||
rv_code = ls_current_mat-zzlzcod.
|
||
ENDIF.
|
||
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
|
||
" DEL 18.1.26
|
||
* IF lv_parent_count_debug = 0.
|
||
* "Write: / ' *** KEINE PARENTS GEFUNDEN! ***'.
|
||
* "Write: / ' Material behält Code:', ls_current_mat-zzlzcod.
|
||
*
|
||
* IF ls_current_mat-zzlzcod IS NOT INITIAL AND
|
||
* ls_current_mat-zzlzcod <> 'ZZZZ'.
|
||
* rv_code = ls_current_mat-zzlzcod.
|
||
* ELSE.
|
||
* rv_code = 'N0X0'.
|
||
* ENDIF.
|
||
*
|
||
* "Write: / ' Rückgabe:', rv_code.
|
||
* "Write: / '╚══════════════════════════════════════════════════╝'.
|
||
* RETURN.
|
||
* ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 5: SAMMLE ALLE PARENTS
|
||
" ========================================
|
||
"Write: / ' Sammle Parents:'.
|
||
|
||
LOOP AT gt_bom_relations INTO DATA(ls_relation)
|
||
WHERE child = iv_matnr.
|
||
|
||
READ TABLE gt_materials WITH KEY matnr = ls_relation-parent
|
||
INTO DATA(ls_parent).
|
||
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Parent:', ls_relation-parent,
|
||
"'Code:', ls_parent-zzlzcod,
|
||
"'Menge:', ls_relation-menge.
|
||
|
||
IF ls_parent-zzlzcod = 'ZZZZ'.
|
||
"Write: ' -> ZZZZ übersprungen'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" *** KORREKTUR: Verwende Default-Code statt zu überspringen ***
|
||
IF ls_parent-zzlzcod IS INITIAL.
|
||
"Write: ' -> Code ist leer, übersprungen'.
|
||
CONTINUE. " ❌ ÜBERSPRINGT PARENT!
|
||
ENDIF.
|
||
|
||
|
||
|
||
APPEND VALUE #( matnr = iv_matnr
|
||
parent = ls_relation-parent
|
||
parent_code = ls_parent-zzlzcod
|
||
usage_qty = ls_relation-menge ) TO lt_parents.
|
||
|
||
" Verbrauchslogik mit Fallbacks
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_relation-parent
|
||
INTO DATA(ls_consumption).
|
||
|
||
IF sy-subrc = 0 AND ls_consumption-gsv_korr > 0.
|
||
lv_parent_verbrauch = ls_consumption-gsv_korr.
|
||
lv_effektive_menge = ls_relation-menge.
|
||
"Write: ' -> Verbrauch:', lv_parent_verbrauch.
|
||
|
||
ELSEIF sy-subrc = 0 AND ls_consumption-gsv_korr = 0.
|
||
lv_parent_verbrauch = 1.
|
||
lv_effektive_menge = ls_relation-menge.
|
||
"Write: ' -> Verbrauch=0, verwende Menge'.
|
||
|
||
ELSE.
|
||
lv_parent_verbrauch = 1.
|
||
IF ls_relation-menge > 0.
|
||
lv_effektive_menge = ls_relation-menge.
|
||
ELSE.
|
||
lv_effektive_menge = 1.
|
||
ENDIF.
|
||
"Write: ' -> Kein Verbrauch, Fallback'.
|
||
ENDIF.
|
||
|
||
" Gesamtverbrauch akkumulieren
|
||
lv_total_verbrauch = lv_total_verbrauch +
|
||
( lv_effektive_menge * lv_parent_verbrauch ).
|
||
|
||
" A-Anteil berechnen
|
||
CASE ls_parent-zzlzcod+0(1).
|
||
WHEN 'A'.
|
||
lv_has_a = abap_true.
|
||
ADD 1 TO lv_count_a.
|
||
|
||
DATA: lv_percent_digit TYPE c,
|
||
lv_mittlerer_prozent TYPE p DECIMALS 2.
|
||
lv_percent_digit = ls_parent-zzlzcod+1(1).
|
||
|
||
IF lv_percent_digit = '0' AND ls_parent-zzlzcod+2(1) = 'X'.
|
||
lv_a_verbrauch = lv_a_verbrauch +
|
||
( lv_effektive_menge * lv_parent_verbrauch ).
|
||
"Write: ' -> A0X: 100% A-Anteil'.
|
||
|
||
ELSEIF lv_percent_digit BETWEEN '1' AND '9'.
|
||
CASE lv_percent_digit.
|
||
WHEN '0'. lv_mittlerer_prozent = '2'. " 0-4% → 2%
|
||
WHEN '1'. lv_mittlerer_prozent = '9.5'. " 5-14% → 9.5% ✅
|
||
WHEN '2'. lv_mittlerer_prozent = '19.5'. " 15-24% → 19.5% ✅
|
||
WHEN '3'. lv_mittlerer_prozent = '29.5'. " 25-34% → 29.5% ✅
|
||
WHEN '4'. lv_mittlerer_prozent = '39.5'. " 35-44% → 39.5% ✅
|
||
WHEN '5'. lv_mittlerer_prozent = '49.5'. " 45-54% → 49.5% ✅
|
||
WHEN '6'. lv_mittlerer_prozent = '59.5'. " 55-64% → 59.5% ✅
|
||
WHEN '7'. lv_mittlerer_prozent = '69.5'. " 65-74% → 69.5% ✅
|
||
WHEN '8'. lv_mittlerer_prozent = '79.5'. " 75-84% → 79.5% ✅
|
||
WHEN '9'. lv_mittlerer_prozent = '92'. " 85-99% → 92% ✅
|
||
ENDCASE.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
lv_a_verbrauch = lv_a_verbrauch +
|
||
( lv_effektive_menge * lv_parent_verbrauch *
|
||
lv_mittlerer_prozent / 100 ).
|
||
"Write: ' -> A-Anteil:', lv_mittlerer_prozent, '%'.
|
||
|
||
ELSEIF lv_percent_digit = '0' AND ls_parent-zzlzcod+2(1) = 'T'.
|
||
lv_a_verbrauch = lv_a_verbrauch +
|
||
( lv_effektive_menge * lv_parent_verbrauch * 2 / 100 ).
|
||
"Write: ' -> A0T: 2% A-Anteil'.
|
||
ENDIF.
|
||
|
||
WHEN 'E'.
|
||
lv_has_e = abap_true.
|
||
ADD 1 TO lv_count_e.
|
||
"Write: ' -> E-Code'.
|
||
|
||
WHEN 'N'.
|
||
lv_has_n = abap_true.
|
||
ADD 1 TO lv_count_n.
|
||
"Write: ' -> N-Code'.
|
||
|
||
WHEN 'S'.
|
||
lv_has_s = abap_true.
|
||
"Write: ' -> S-Code'.
|
||
ENDCASE.
|
||
|
||
ELSE.
|
||
"Write: / ' Parent:', ls_relation-parent, '-> NICHT IN gt_materials!'.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
"Write: / ' Gültige Parents gesammelt:', lines( lt_parents ).
|
||
"Write: / ' Counts: A=', lv_count_a, 'E=', lv_count_e, 'N=', lv_count_n.
|
||
|
||
" ========================================
|
||
" SCHRITT 6: FEHLERFALL - KEINE GÜLTIGEN PARENTS
|
||
" ========================================
|
||
IF lines( lt_parents ) = 0.
|
||
"Write: / ' *** KEINE GÜLTIGEN PARENTS! ***'.
|
||
|
||
IF ls_current_mat-zzlzcod IS NOT INITIAL AND
|
||
ls_current_mat-zzlzcod <> 'ZZZZ'.
|
||
rv_code = ls_current_mat-zzlzcod.
|
||
ELSE.
|
||
rv_code = 'N0X0'.
|
||
ENDIF.
|
||
|
||
"Write: / ' Rückgabe:', rv_code.
|
||
"Write: / '╚══════════════════════════════════════════════════╝'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 7: BERECHNE A-ANTEIL
|
||
" ========================================
|
||
IF lv_total_verbrauch > 0.
|
||
lv_anteil = ( lv_a_verbrauch / lv_total_verbrauch ) * 100.
|
||
ELSE.
|
||
lv_anteil = 0.
|
||
ENDIF.
|
||
|
||
"Write: / ' Total-Verbrauch:', lv_total_verbrauch.
|
||
"Write: / ' A-Verbrauch:', lv_a_verbrauch.
|
||
"Write: / ' A-Anteil:', lv_anteil, '%'.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
" ========================================
|
||
" SCHRITT 8: VERERBUNGSLOGIK (KORRIGIERT)
|
||
" ========================================
|
||
|
||
" REGEL 1: E dominiert (nur wenn KEIN N dabei ist)
|
||
IF lv_has_e = abap_true AND lv_has_n = abap_false.
|
||
"Write: / ' REGEL 1: E dominiert (ohne N)'.
|
||
rv_code = 'E0X' && lv_position_4.
|
||
|
||
" REGEL 2: N + A (ohne E) → Auslauf-Teilmenge
|
||
ELSEIF lv_has_n = abap_true AND lv_has_a = abap_true AND lv_has_e = abap_false.
|
||
"Write: / ' REGEL 2: N + A (ohne E) = Auslauf-Teilmenge'.
|
||
|
||
IF lv_anteil = 100.
|
||
rv_code = 'A0X' && lv_position_4.
|
||
ELSEIF lv_anteil >= 85.
|
||
rv_code = 'A9T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 75.
|
||
rv_code = 'A8T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 65.
|
||
rv_code = 'A7T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 55.
|
||
rv_code = 'A6T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 45.
|
||
rv_code = 'A5T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 35.
|
||
rv_code = 'A4T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 25.
|
||
rv_code = 'A3T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 15.
|
||
rv_code = 'A2T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 5.
|
||
rv_code = 'A1T' && lv_position_4.
|
||
ELSEIF lv_anteil > 0.
|
||
rv_code = 'A0T' && lv_position_4.
|
||
ELSE.
|
||
rv_code = 'N0X' && lv_position_4.
|
||
ENDIF.
|
||
|
||
" REGEL 3: N dominiert IMMER (auch mit E/A)
|
||
ELSEIF lv_has_n = abap_true.
|
||
"Write: / ' REGEL 3: N dominiert (mit E/A)'.
|
||
rv_code = 'N0X' && lv_position_4.
|
||
|
||
" REGEL 4: Nur A (ohne N/E)
|
||
ELSEIF lv_has_a = abap_true AND lv_has_n = abap_false AND lv_has_e = abap_false.
|
||
"Write: / ' REGEL 4: Nur A'.
|
||
|
||
IF lv_anteil = 100.
|
||
rv_code = 'A0X' && lv_position_4.
|
||
ELSEIF lv_anteil >= 85.
|
||
rv_code = 'A9T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 75.
|
||
rv_code = 'A8T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 65.
|
||
rv_code = 'A7T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 55.
|
||
rv_code = 'A6T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 45.
|
||
rv_code = 'A5T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 35.
|
||
rv_code = 'A4T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 25.
|
||
rv_code = 'A3T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 15.
|
||
rv_code = 'A2T' && lv_position_4.
|
||
ELSEIF lv_anteil >= 5.
|
||
rv_code = 'A1T' && lv_position_4.
|
||
ELSEIF lv_anteil > 0.
|
||
rv_code = 'A0T' && lv_position_4.
|
||
ELSE.
|
||
rv_code = 'N0X' && lv_position_4.
|
||
ENDIF.
|
||
|
||
" REGEL 5: Fallback
|
||
ELSE.
|
||
"Write: / ' REGEL 5: Fallback (keine gültigen Parents)'.
|
||
rv_code = 'N0X' && lv_position_4.
|
||
ENDIF.
|
||
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD Write_to_journal.
|
||
DATA: ls_journal TYPE zlzcod_journl.
|
||
|
||
ls_journal-mandt = sy-mandt.
|
||
ls_journal-matnr = iv_matnr.
|
||
ls_journal-aenderungsdatum = sy-datum.
|
||
ls_journal-aenderungszeit = sy-uzeit.
|
||
ls_journal-aenderungsuser = sy-uname.
|
||
ls_journal-old_lzcod = iv_old_lzcod.
|
||
ls_journal-new_lzcod = iv_new_lzcod.
|
||
ls_journal-old_lzcodsort = iv_old_lzcodsort.
|
||
ls_journal-new_lzcodsort = iv_new_lzcodsort.
|
||
ls_journal-programm = sy-repid.
|
||
|
||
" TOP-Material aus aktueller Selektion (da Wrapper nur 1 übergibt)
|
||
READ TABLE s_matnr INDEX 1 INTO DATA(ls_sel).
|
||
IF sy-subrc = 0 AND ls_sel-sign = 'I' AND ls_sel-option = 'EQ'.
|
||
ls_journal-top_matnr = ls_sel-low.
|
||
ELSE.
|
||
ls_journal-top_matnr = iv_matnr. " Fallback
|
||
ENDIF.
|
||
|
||
" In Tabelle schreiben
|
||
INSERT zlzcod_journl FROM ls_journal.
|
||
|
||
IF sy-subrc <> 0.
|
||
" Bei Fehler: Update statt Insert (falls Eintrag schon existiert)
|
||
UPDATE zlzcod_journl FROM ls_journal.
|
||
ENDIF.
|
||
|
||
" Für Debug-Zwecke:
|
||
IF p_test = abap_true.
|
||
""Write: / 'Journal:', ls_journal-matnr,
|
||
"'TOP:', ls_journal-top_matnr,
|
||
"'LZCOD:', ls_journal-old_lzcod, '->', ls_journal-new_lzcod,
|
||
" 'LZCODSORT:', ls_journal-old_lzcodsort, '->', ls_journal-new_lzcodsort.
|
||
ENDIF.
|
||
ENDMETHOD.
|
||
|
||
|
||
*
|
||
* METHOD update_database.
|
||
* DATA: lt_update_batch TYPE STANDARD TABLE OF ty_result,
|
||
* lv_batch_count TYPE i,
|
||
* lv_updated TYPE i,
|
||
* lv_datum_str TYPE dats.
|
||
*
|
||
* lv_datum_str = sy-datum.
|
||
*
|
||
* " Batch-Update für Performance
|
||
* LOOP AT gt_results INTO DATA(ls_result) WHERE changed = abap_true.
|
||
* APPEND ls_result TO lt_update_batch.
|
||
*
|
||
*
|
||
* " Führe Batch-Update durch
|
||
* LOOP AT lt_update_batch INTO DATA(ls_update).
|
||
*
|
||
* UPDATE mara SET zzlzcod = @ls_update-new_code,
|
||
* zzlzdat = @lv_datum_str
|
||
* WHERE matnr = @ls_update-matnr.
|
||
* COMMIT WORK.
|
||
*
|
||
* " Verwende MM02 für Update
|
||
*
|
||
* " Journal-Eintrag mit TOP-Material
|
||
** me->""Write_to_journal(
|
||
** iv_matnr = ls_update-matnr
|
||
** iv_old_lzcod = ls_update-old_code
|
||
** iv_new_lzcod = ls_update-new_code ).
|
||
*
|
||
* ADD 1 TO lv_updated.
|
||
* ENDLOOP.
|
||
*
|
||
* COMMIT WORK.
|
||
* CLEAR lt_update_batch.
|
||
* ADD 1 TO lv_batch_count.
|
||
*
|
||
* " Progress-Anzeige
|
||
* cl_progress_indicator=>progress_indicate(
|
||
* i_text = |Batch { lv_batch_count } aktualisiert|
|
||
* i_processed = lv_updated
|
||
* i_total = lines( gt_results ) ).
|
||
*
|
||
* ENDLOOP.
|
||
*
|
||
* " Letzter Batch
|
||
* IF lines( lt_update_batch ) > 0.
|
||
* LOOP AT lt_update_batch INTO ls_update.
|
||
* IF p_mm = abap_true.
|
||
* UPDATE mara SET zzlzcod = @ls_update-new_code,
|
||
* zzlzdat = @lv_datum_str
|
||
* WHERE matnr = @ls_update-matnr. COMMIT WORK.
|
||
* ELSE.
|
||
*
|
||
*
|
||
*
|
||
*
|
||
* ENDIF.
|
||
*
|
||
* " Journal-Eintrag mit TOP-Material
|
||
* me->Write_to_journal(
|
||
* iv_matnr = ls_update-matnr
|
||
* iv_old_lzcod = ls_update-old_code
|
||
* iv_new_lzcod = ls_update-new_code ).
|
||
*
|
||
* ADD 1 TO lv_updated.
|
||
* ENDLOOP.
|
||
* COMMIT WORK.
|
||
* ENDIF.
|
||
*
|
||
* ""Write: / |{ lv_updated } Materialien über MM02 aktualisiert (ZZLZCOD)|.
|
||
* ENDMETHOD.
|
||
|
||
METHOD update_database.
|
||
DATA: lv_updated TYPE i,
|
||
lv_datum_str TYPE dats.
|
||
|
||
lv_datum_str = sy-datum.
|
||
|
||
LOOP AT gt_results INTO DATA(ls_result) WHERE changed = abap_true.
|
||
|
||
" Update durchführen
|
||
UPDATE mara SET zzlzcod = @ls_result-new_code,
|
||
zzlzdat = @lv_datum_str
|
||
WHERE matnr = @ls_result-matnr.
|
||
COMMIT WORK.
|
||
|
||
" Journal-Eintrag
|
||
me->write_to_journal(
|
||
iv_matnr = ls_result-matnr
|
||
iv_old_lzcod = ls_result-old_code
|
||
iv_new_lzcod = ls_result-new_code ).
|
||
|
||
ADD 1 TO lv_updated.
|
||
|
||
ENDLOOP.
|
||
|
||
WRITE: / lv_updated, 'Materialien aktualisiert (ZZLZCOD)'.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
METHOD update_database_sortiment.
|
||
DATA: lv_updated TYPE i,
|
||
lv_errors TYPE i,
|
||
lv_status99 TYPE i,
|
||
lv_datum_str TYPE dats.
|
||
|
||
lv_datum_str = sy-datum.
|
||
|
||
WRITE: / '=================================================='.
|
||
WRITE: / 'START: Update Database Sortiment'.
|
||
WRITE: / 'Anzahl zu aktualisieren:', lines( gt_results ).
|
||
|
||
LOOP AT gt_results INTO DATA(ls_result)
|
||
WHERE changed_sort = abap_true.
|
||
|
||
" *** DIREKTES UPDATE MARA (auch leerer Code für Status 99) ***
|
||
UPDATE mara
|
||
SET zzlzcodsort = @ls_result-new_code_sort,
|
||
zzlzdat = @lv_datum_str
|
||
WHERE matnr = @ls_result-matnr.
|
||
|
||
IF sy-subrc = 0.
|
||
ADD 1 TO lv_updated.
|
||
|
||
" Zähle Status 99 separat
|
||
IF ls_result-new_code_sort IS INITIAL.
|
||
ADD 1 TO lv_status99.
|
||
ENDIF.
|
||
|
||
" Journal-Eintrag
|
||
me->write_to_journal(
|
||
iv_matnr = ls_result-matnr
|
||
iv_old_lzcod = ls_result-old_code
|
||
iv_new_lzcod = ls_result-old_code
|
||
iv_old_lzcodsort = ls_result-old_code_sort
|
||
iv_new_lzcodsort = ls_result-new_code_sort ).
|
||
|
||
ELSE.
|
||
ADD 1 TO lv_errors.
|
||
WRITE: / 'ERROR: Update fehlgeschlagen für', ls_result-matnr.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
" Commit nach allen Updates
|
||
IF lv_updated > 0.
|
||
COMMIT WORK AND WAIT.
|
||
WRITE: / 'ERFOLG:', lv_updated, 'Sortiments-Codes aktualisiert'.
|
||
WRITE: / ' Davon Status 99 (geleert):', lv_status99.
|
||
ENDIF.
|
||
|
||
IF lv_errors > 0.
|
||
WRITE: / 'FEHLER:', lv_errors, 'Updates fehlgeschlagen'.
|
||
ENDIF.
|
||
|
||
WRITE: / 'ENDE: Update Database Sortiment'.
|
||
WRITE: / '=================================================='.
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
*
|
||
* METHOD transaktion_mm02.
|
||
* DATA: lv_matnr_str TYPE bdc_fval, " Geändert zu bdc_fval
|
||
* lv_datum_str TYPE bdc_fval, " Geändert zu bdc_fval
|
||
* lv_lzcod_str TYPE bdc_fval, " NEU für iv_lzcod
|
||
* lv_lzcodsort_str TYPE bdc_fval. " NEU für iv_lzcodsort
|
||
*
|
||
* CLEAR: gt_bdcdata, gt_bdcmsg.
|
||
*
|
||
* " Material und Datum formatieren - als bdc_fval
|
||
* lv_matnr_str = |{ iv_matnr ALPHA = OUT }|.
|
||
* lv_datum_str = |{ sy-datum DATE = USER }|.
|
||
* lv_lzcod_str = iv_lzcod. " Direkte Zuweisung, da char4 -> bdc_fval kompatibel
|
||
*
|
||
* " Optional: ZZLZCODSORT konvertieren
|
||
* IF iv_lzcodsort IS NOT INITIAL.
|
||
* lv_lzcodsort_str = iv_lzcodsort.
|
||
* ENDIF.
|
||
*
|
||
* " Einstiegsbild MM02
|
||
* me->bdc_dynpro( iv_program = 'SAPLMGMM' iv_dynpro = '0060' ).
|
||
* me->bdc_field( iv_fnam = 'BDC_OKCODE' iv_fval = 'AUSW' ).
|
||
* me->bdc_field( iv_fnam = 'RMMG1-MATNR' iv_fval = lv_matnr_str ).
|
||
*
|
||
* " Sichtenauswahl
|
||
* me->bdc_dynpro( iv_program = 'SAPLMGMM' iv_dynpro = '0070' ).
|
||
* me->bdc_field( iv_fnam = 'BDC_OKCODE' iv_fval = '=ENTR' ).
|
||
* me->bdc_field( iv_fnam = 'MSICHTAUSW-KZSEL(01)' iv_fval = 'X' ).
|
||
*
|
||
* " Grunddaten - beide Felder setzen
|
||
* me->bdc_dynpro( iv_program = 'SAPLMGMM' iv_dynpro = '4004' ).
|
||
* me->bdc_field( iv_fnam = 'BDC_OKCODE' iv_fval = '/11' ).
|
||
*
|
||
* " ZZLZCOD setzen
|
||
* me->bdc_field( iv_fnam = 'MARA-ZZLZCOD' iv_fval = lv_lzcod_str ).
|
||
* me->bdc_field( iv_fnam = 'MARA-ZZLZDAT' iv_fval = lv_datum_str ).
|
||
*
|
||
* " ZZLZCODSORT setzen falls übergeben
|
||
* IF iv_lzcodsort IS NOT INITIAL.
|
||
* me->bdc_field( iv_fnam = 'MARA-ZZLZCODSORT' iv_fval = lv_lzcodsort_str ).
|
||
* ENDIF.
|
||
*
|
||
* me->bdc_transaction( iv_tcode = 'MM02' ).
|
||
* ENDMETHOD.
|
||
*
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD is_debug_material.
|
||
" NUR diese 4 Materialien debuggen!
|
||
rv_debug = COND #(
|
||
WHEN iv_matnr = 'B53618' OR
|
||
iv_matnr = 'B56383' OR
|
||
iv_matnr = 'B58383' OR
|
||
iv_matnr = 'B69327'
|
||
THEN abap_true
|
||
ELSE abap_false
|
||
).
|
||
ENDMETHOD.
|
||
|
||
METHOD debug_problem_material.
|
||
DATA: lt_info TYPE string_table,
|
||
lv_line TYPE string.
|
||
|
||
" Nur debuggen wenn es ein Problem-Material ist
|
||
IF is_debug_material( iv_matnr ) = abap_false.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ DEBUG PROBLEM-MATERIAL:', iv_matnr, 'Phase:', iv_phase.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
|
||
" Je nach Phase verschiedene Informationen sammeln
|
||
CASE iv_phase.
|
||
WHEN 'LOAD'.
|
||
" Nach dem Laden: Zeige Stammdaten
|
||
READ TABLE gt_materials WITH KEY matnr = iv_matnr
|
||
INTO DATA(ls_mat).
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Stammdaten gefunden:'.
|
||
"Write: / ' MTART:', ls_mat-mtart.
|
||
"Write: / ' ZZLZCOD (aktuell):', ls_mat-zzlzcod.
|
||
"Write: / ' ZZLZCODSORT:', ls_mat-zzlzcodsort.
|
||
"Write: / ' DISST:', ls_mat-disst.
|
||
ELSE.
|
||
"Write: / ' *** FEHLER: Material NICHT in gt_materials!'.
|
||
ENDIF.
|
||
|
||
WHEN 'RELATIONS'.
|
||
" Parent-Child Beziehungen
|
||
DATA: lv_parent_count TYPE i,
|
||
lv_child_count TYPE i.
|
||
|
||
"Write: / ' === PARENT-BEZIEHUNGEN ==='.
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel)
|
||
WHERE child = iv_matnr.
|
||
ADD 1 TO lv_parent_count.
|
||
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel-parent
|
||
INTO DATA(ls_parent).
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Parent', lv_parent_count, ':', ls_rel-parent.
|
||
"Write: / ' Code:', ls_parent-zzlzcod.
|
||
"Write: / ' Menge:', ls_rel-menge.
|
||
ELSE.
|
||
"Write: / ' Parent', lv_parent_count, ':', ls_rel-parent,
|
||
"'*** NICHT in gt_materials!'.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
IF lv_parent_count = 0.
|
||
"Write: / ' *** KEINE PARENTS GEFUNDEN! ***'.
|
||
ELSE.
|
||
"Write: / ' Anzahl Parents:', lv_parent_count.
|
||
ENDIF.
|
||
|
||
"Write: / ' === CHILD-BEZIEHUNGEN ==='.
|
||
LOOP AT gt_bom_relations INTO ls_rel
|
||
WHERE parent = iv_matnr.
|
||
ADD 1 TO lv_child_count.
|
||
"Write: / ' Child:', ls_rel-child, 'Menge:', ls_rel-menge.
|
||
ENDLOOP.
|
||
|
||
IF lv_child_count = 0.
|
||
"Write: / ' Keine Children (Endprodukt oder Komponente ohne Stückliste)'.
|
||
ELSE.
|
||
"Write: / ' Anzahl Children:', lv_child_count.
|
||
ENDIF.
|
||
|
||
WHEN 'CONSUMPTION'.
|
||
" Verbrauchsdaten
|
||
"Write: / ' === VERBRAUCHSDATEN ==='.
|
||
|
||
" Eigener Verbrauch
|
||
READ TABLE gt_consumption WITH KEY matnr = iv_matnr
|
||
INTO DATA(ls_cons).
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Eigener Verbrauch:'.
|
||
"Write: / ' gsv01:', ls_cons-gsv01.
|
||
"Write: / ' gsv_korr:', ls_cons-gsv_korr.
|
||
ELSE.
|
||
"Write: / ' *** KEIN Verbrauch gefunden!'.
|
||
ENDIF.
|
||
|
||
" Parent-Verbräuche
|
||
"Write: / ' Parent-Verbräuche:'.
|
||
LOOP AT gt_bom_relations INTO ls_rel WHERE child = iv_matnr.
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_rel-parent
|
||
INTO ls_cons.
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Parent', ls_rel-parent,
|
||
"'gsv01:', ls_cons-gsv01,
|
||
"'gsv_korr:', ls_cons-gsv_korr.
|
||
ELSE.
|
||
"Write: / ' Parent', ls_rel-parent, 'KEIN Verbrauch'.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
WHEN 'BEFORE_CALC'.
|
||
" Vor der Berechnung
|
||
"Write: / ' === VOR BERECHNUNG ==='.
|
||
READ TABLE gt_materials WITH KEY matnr = iv_matnr INTO ls_mat.
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Aktueller Code:', ls_mat-zzlzcod.
|
||
|
||
" Zeige Parent-Codes
|
||
"Write: / ' Parent-Codes:'.
|
||
LOOP AT gt_bom_relations INTO ls_rel WHERE child = iv_matnr.
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel-parent
|
||
INTO ls_parent.
|
||
IF sy-subrc = 0.
|
||
"Write: / ' ', ls_rel-parent, '=', ls_parent-zzlzcod.
|
||
ELSE.
|
||
"Write: / ' ', ls_rel-parent, '= NICHT GEFUNDEN'.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
ENDIF.
|
||
|
||
WHEN 'AFTER_CALC'.
|
||
" Nach der Berechnung
|
||
"Write: / ' === NACH BERECHNUNG ==='.
|
||
READ TABLE gt_results WITH KEY matnr = iv_matnr
|
||
INTO DATA(ls_result).
|
||
IF sy-subrc = 0.
|
||
"Write: / ' Alter Code:', ls_result-old_code.
|
||
"Write: / ' Neuer Code:', ls_result-new_code.
|
||
"Write: / ' Geändert:', ls_result-changed.
|
||
ELSE.
|
||
"Write: / ' *** KEIN ERGEBNIS in gt_results!'.
|
||
ENDIF.
|
||
|
||
ENDCASE.
|
||
|
||
"Write: / '══════════════════════════════════════════════════════════'.
|
||
ENDMETHOD.
|
||
|
||
METHOD build_hierarchy_from_stb.
|
||
DATA: lt_vc_data TYPE STANDARD TABLE OF zpowerbi_vc,
|
||
ls_bom_rel TYPE ty_bom_relation,
|
||
ls_material TYPE ty_material,
|
||
lv_top_material TYPE matnr,
|
||
lv_rel_count TYPE i,
|
||
lv_mat_count TYPE i.
|
||
|
||
" ========================================
|
||
" SCHRITT 1: TOP-Material bestimmen
|
||
" ========================================
|
||
IF iv_top_material IS NOT INITIAL.
|
||
lv_top_material = iv_top_material.
|
||
ELSE.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 2: Prüfe ob Material in ZPOWERBI_VC existiert
|
||
" ========================================
|
||
SELECT COUNT(*) FROM zpowerbi_vc
|
||
INTO @DATA(lv_row_count)
|
||
WHERE matnr = @lv_top_material.
|
||
|
||
IF lv_row_count = 0.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 3: Lade Hierarchie aus ZPOWERBI_VC
|
||
" ========================================
|
||
SELECT * FROM zpowerbi_vc
|
||
INTO TABLE @lt_vc_data
|
||
WHERE matnr = @lv_top_material
|
||
ORDER BY mat_mstav.
|
||
|
||
IF sy-subrc <> 0.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 4: TOP-Material zu gt_materials hinzufügen
|
||
" ========================================
|
||
READ TABLE gt_materials WITH KEY matnr = lv_top_material
|
||
TRANSPORTING NO FIELDS.
|
||
IF sy-subrc <> 0.
|
||
SELECT SINGLE matnr, mtart, zzlzcod, zzlzcodsort, zztyp_f4, pstat, disst, mstae
|
||
FROM mara
|
||
INTO CORRESPONDING FIELDS OF @ls_material
|
||
WHERE matnr = @lv_top_material.
|
||
IF sy-subrc = 0.
|
||
" *** NEU: Status 99 prüfen ***
|
||
IF ls_material-mstae = '99'.
|
||
ls_material-zzlzcodsort = ''.
|
||
ENDIF.
|
||
INSERT ls_material INTO TABLE gt_materials.
|
||
ADD 1 TO lv_mat_count.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" SCHRITT 5: Verarbeite ZPOWERBI_VC-Einträge
|
||
" ========================================
|
||
LOOP AT lt_vc_data INTO DATA(ls_vc).
|
||
|
||
DATA(lv_parent_matnr) = VALUE matnr( ).
|
||
|
||
IF ls_vc-mat_mstae = 0.
|
||
lv_parent_matnr = lv_top_material.
|
||
ELSE.
|
||
lv_parent_matnr = ls_vc-kom_mstae.
|
||
ENDIF.
|
||
|
||
IF lv_parent_matnr IS NOT INITIAL.
|
||
ls_bom_rel-parent = lv_parent_matnr.
|
||
ls_bom_rel-child = ls_vc-kompnr.
|
||
ls_bom_rel-menge = ls_vc-menge.
|
||
ls_bom_rel-level = ls_vc-stufe.
|
||
|
||
INSERT ls_bom_rel INTO TABLE gt_bom_relations.
|
||
ADD 1 TO lv_rel_count.
|
||
ENDIF.
|
||
|
||
" Füge Child-Material zu gt_materials hinzu
|
||
READ TABLE gt_materials WITH KEY matnr = ls_vc-kompnr
|
||
TRANSPORTING NO FIELDS.
|
||
IF sy-subrc <> 0.
|
||
CLEAR ls_material.
|
||
ls_material-matnr = ls_vc-kompnr.
|
||
ls_material-mtart = ls_vc-materialart.
|
||
ls_material-disst = ''.
|
||
|
||
" Hole ZZLZCOD/ZZLZCODSORT/MSTAE aus MARA
|
||
SELECT SINGLE zzlzcod, zzlzcodsort, zztyp_f4, pstat, mstae
|
||
FROM mara
|
||
INTO CORRESPONDING FIELDS OF @ls_material
|
||
WHERE matnr = @ls_vc-kompnr.
|
||
|
||
IF sy-subrc = 0.
|
||
" *** NEU: Status 99 prüfen ***
|
||
IF ls_material-mstae = '99'.
|
||
ls_material-zzlzcodsort = ''.
|
||
ENDIF.
|
||
INSERT ls_material INTO TABLE gt_materials.
|
||
ADD 1 TO lv_mat_count.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
ENDMETHOD.
|
||
|
||
METHOD debug_collect_info.
|
||
DATA: lv_info TYPE string.
|
||
|
||
CLEAR rt_info.
|
||
|
||
" Sammle alle Informationen für ein Material
|
||
READ TABLE gt_materials WITH KEY matnr = iv_matnr INTO DATA(ls_mat).
|
||
IF sy-subrc = 0.
|
||
lv_info = |Material: { iv_matnr } Code: { ls_mat-zzlzcod }|.
|
||
APPEND lv_info TO rt_info.
|
||
ENDIF.
|
||
|
||
" Parents
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel) WHERE child = iv_matnr.
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel-parent
|
||
INTO DATA(ls_parent).
|
||
IF sy-subrc = 0.
|
||
lv_info = | Parent: { ls_rel-parent } Code: { ls_parent-zzlzcod }|.
|
||
ELSE.
|
||
lv_info = | Parent: { ls_rel-parent } NICHT GEFUNDEN|.
|
||
ENDIF.
|
||
APPEND lv_info TO rt_info.
|
||
ENDLOOP.
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD bdc_dynpro.
|
||
DATA: ls_bdcdata TYPE bdcdata.
|
||
|
||
ls_bdcdata-program = iv_program.
|
||
ls_bdcdata-dynpro = iv_dynpro.
|
||
ls_bdcdata-dynbegin = 'X'.
|
||
APPEND ls_bdcdata TO gt_bdcdata.
|
||
ENDMETHOD.
|
||
|
||
METHOD bdc_field.
|
||
DATA: ls_bdcdata TYPE bdcdata.
|
||
|
||
ls_bdcdata-fnam = iv_fnam.
|
||
ls_bdcdata-fval = iv_fval.
|
||
APPEND ls_bdcdata TO gt_bdcdata.
|
||
ENDMETHOD.
|
||
|
||
METHOD bdc_transaction.
|
||
DATA: ls_options TYPE ctu_params.
|
||
|
||
ls_options-dismode = 'N'. " Kein Bildschirm
|
||
ls_options-updmode = 'S'. " Synchron
|
||
ls_options-defsize = 'X'. " Standardgröße
|
||
|
||
CALL TRANSACTION iv_tcode
|
||
USING gt_bdcdata
|
||
OPTIONS FROM ls_options
|
||
MESSAGES INTO gt_bdcmsg.
|
||
|
||
" Fehlerbehandlung
|
||
LOOP AT gt_bdcmsg INTO DATA(ls_msg)
|
||
WHERE msgtyp = 'E' OR msgtyp = 'A'.
|
||
MESSAGE ID ls_msg-msgid TYPE 'I' NUMBER ls_msg-msgnr
|
||
WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4.
|
||
ENDLOOP.
|
||
ENDMETHOD.
|
||
|
||
|
||
METHOD calculate_sortiment_inhe.
|
||
DATA: lv_iteration TYPE i,
|
||
lv_changes TYPE i,
|
||
lt_material_list TYPE STANDARD TABLE OF matnr.
|
||
|
||
" Sammle alle zu verarbeitenden Materialien
|
||
LOOP AT gt_materials INTO DATA(ls_material).
|
||
APPEND ls_material-matnr TO lt_material_list.
|
||
ENDLOOP.
|
||
|
||
" Iterative Vererbungsberechnung (max. 10 Iterationen)
|
||
DO 10 TIMES.
|
||
lv_iteration = sy-index.
|
||
CLEAR lv_changes.
|
||
|
||
" Verarbeite alle Materialien
|
||
LOOP AT lt_material_list INTO DATA(lv_matnr).
|
||
|
||
" Prüfe auf Änderung
|
||
READ TABLE gt_materials WITH KEY matnr = lv_matnr
|
||
ASSIGNING FIELD-SYMBOL(<fs_mat>).
|
||
|
||
IF sy-subrc <> 0.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" *** NEU: Status 99 = Ausgelaufen → Überspringen ***
|
||
IF <fs_mat>-mstae = '99'.
|
||
" Nur beim ersten Durchlauf in Ergebnis aufnehmen
|
||
IF lv_iteration = 1.
|
||
READ TABLE gt_results WITH KEY matnr = lv_matnr
|
||
TRANSPORTING NO FIELDS.
|
||
IF sy-subrc <> 0.
|
||
APPEND VALUE #(
|
||
matnr = lv_matnr
|
||
old_code_sort = <fs_mat>-zzlzcodsort
|
||
new_code_sort = ''
|
||
changed_sort = COND #( WHEN <fs_mat>-zzlzcodsort IS NOT INITIAL
|
||
THEN abap_true ELSE abap_false )
|
||
message = 'Status 99 - Ausgelaufen'
|
||
) TO gt_results.
|
||
ENDIF.
|
||
ENDIF.
|
||
CONTINUE. " Nächstes Material
|
||
ENDIF.
|
||
|
||
" Prüfe manuelle Sperre (Position 4 = '1')
|
||
IF strlen( <fs_mat>-zzlzcodsort ) >= 4 AND
|
||
<fs_mat>-zzlzcodsort+3(1) = '1'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" Berechne neuen Sortimentscode
|
||
DATA(lv_new_code) = apply_sortiment_rules( lv_matnr ).
|
||
|
||
" Prüfe ob sich der Code geändert hat
|
||
IF <fs_mat>-zzlzcodsort <> lv_new_code.
|
||
" Speichere Änderung in Ergebnistabelle
|
||
READ TABLE gt_results ASSIGNING FIELD-SYMBOL(<fs_result>)
|
||
WITH KEY matnr = lv_matnr.
|
||
|
||
IF sy-subrc = 0.
|
||
<fs_result>-old_code_sort = <fs_mat>-zzlzcodsort.
|
||
<fs_result>-new_code_sort = lv_new_code.
|
||
<fs_result>-changed_sort = abap_true.
|
||
ELSE.
|
||
APPEND VALUE #(
|
||
matnr = lv_matnr
|
||
old_code_sort = <fs_mat>-zzlzcodsort
|
||
new_code_sort = lv_new_code
|
||
changed_sort = abap_true
|
||
) TO gt_results.
|
||
ENDIF.
|
||
|
||
" Update Material mit neuem Code
|
||
<fs_mat>-zzlzcodsort = lv_new_code.
|
||
ADD 1 TO lv_changes.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
" Beende wenn keine Änderungen mehr
|
||
IF lv_changes = 0.
|
||
EXIT.
|
||
ENDIF.
|
||
ENDDO.
|
||
ENDMETHOD.
|
||
|
||
METHOD apply_sortiment_rules.
|
||
DATA: lv_position_4 TYPE char1,
|
||
lv_has_s TYPE abap_bool,
|
||
lv_has_o TYPE abap_bool,
|
||
lv_has_e TYPE abap_bool,
|
||
lv_has_n TYPE abap_bool,
|
||
lv_has_a TYPE abap_bool,
|
||
lv_has_c TYPE abap_bool,
|
||
lv_parent_count TYPE i,
|
||
lv_sonder_weighted TYPE p DECIMALS 3,
|
||
lv_core_weighted TYPE p DECIMALS 3,
|
||
lv_total_weighted TYPE p DECIMALS 3,
|
||
lv_percentage TYPE p DECIMALS 2,
|
||
lv_pct_int TYPE i,
|
||
ls_current_mat TYPE ty_material,
|
||
ls_parent TYPE ty_material,
|
||
ls_relation TYPE ty_bom_relation,
|
||
ls_consumption TYPE ty_consumption.
|
||
|
||
WRITE: / ''.
|
||
WRITE: / '╔══════════════════════════════════════════════════════════╗'.
|
||
WRITE: / '║ apply_sortiment_rules (NEU) für:', iv_matnr.
|
||
WRITE: / '╚══════════════════════════════════════════════════════════╝'.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 1: HOLE AKTUELLES MATERIAL
|
||
" ══════════════════════════════════════════════════════════════════
|
||
READ TABLE gt_materials WITH TABLE KEY matnr = iv_matnr INTO ls_current_mat.
|
||
IF sy-subrc <> 0.
|
||
WRITE: / ' ERROR: Material nicht in gt_materials!'.
|
||
rv_code = 'N0X0'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" *** NEU: Status 99 = Ausgelaufen → Leerer Code ***
|
||
IF ls_current_mat-mstae = '99'.
|
||
WRITE: / ' Status 99 (Ausgelaufen) → Code wird geleert'.
|
||
rv_code = ''.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Initialisiere Code falls leer
|
||
IF ls_current_mat-zzlzcodsort IS INITIAL.
|
||
ls_current_mat-zzlzcodsort = 'N0X0'.
|
||
MODIFY TABLE gt_materials FROM ls_current_mat.
|
||
ENDIF.
|
||
|
||
WRITE: / ' Aktueller Code:', ls_current_mat-zzlzcodsort.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 2: POSITION 4 (MANUELLE SPERRE)
|
||
" ══════════════════════════════════════════════════════════════════
|
||
IF strlen( ls_current_mat-zzlzcodsort ) >= 4.
|
||
lv_position_4 = ls_current_mat-zzlzcodsort+3(1).
|
||
ELSE.
|
||
lv_position_4 = '0'.
|
||
ENDIF.
|
||
|
||
IF lv_position_4 = '1'.
|
||
WRITE: / ' -> GESPERRT (Position 4 = 1), Code bleibt'.
|
||
rv_code = ls_current_mat-zzlzcodsort.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 3: SAMMLE PARENTS UND ANALYSIERE TYPEN
|
||
" ══════════════════════════════════════════════════════════════════
|
||
WRITE: / ' Analysiere Parents:'.
|
||
CLEAR: lv_parent_count, lv_has_s, lv_has_o, lv_has_e,
|
||
lv_has_n, lv_has_a, lv_has_c.
|
||
|
||
LOOP AT gt_bom_relations INTO ls_relation WHERE child = iv_matnr.
|
||
ADD 1 TO lv_parent_count.
|
||
|
||
READ TABLE gt_materials WITH TABLE KEY matnr = ls_relation-parent INTO ls_parent.
|
||
IF sy-subrc <> 0.
|
||
WRITE: / ' Parent', ls_relation-parent, 'NICHT GEFUNDEN!'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" *** NEU: Parent mit Status 99 überspringen ***
|
||
IF ls_parent-mstae = '99'.
|
||
WRITE: / ' Parent', ls_relation-parent, 'Status 99 → übersprungen'.
|
||
SUBTRACT 1 FROM lv_parent_count.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" Überspringe ZZZZ und leere Codes
|
||
IF ls_parent-zzlzcodsort = 'ZZZZ' OR ls_parent-zzlzcodsort IS INITIAL.
|
||
WRITE: / ' Parent', ls_relation-parent, 'übersprungen (ZZZZ/leer)'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
WRITE: / ' Parent:', ls_relation-parent,
|
||
'Code:', ls_parent-zzlzcodsort,
|
||
'Typ:', ls_parent-zzlzcodsort(1).
|
||
|
||
" Setze Flags basierend auf 1. Stelle
|
||
CASE ls_parent-zzlzcodsort(1).
|
||
WHEN 'S'. lv_has_s = abap_true.
|
||
WHEN 'O'. lv_has_o = abap_true.
|
||
WHEN 'E'. lv_has_e = abap_true.
|
||
" WHEN 'N'. lv_has_n = abap_true.
|
||
WHEN 'A'. lv_has_a = abap_true.
|
||
WHEN 'C'. lv_has_c = abap_true.
|
||
ENDCASE.
|
||
ENDLOOP.
|
||
|
||
WRITE: / ' Anzahl gültige Parents:', lv_parent_count.
|
||
WRITE: / ' Flags: S=', lv_has_s, 'O=', lv_has_o, 'E=', lv_has_e,
|
||
'C=', lv_has_c, 'N=', lv_has_n, 'A=', lv_has_a.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 4: KEINE PARENTS → CODE BLEIBT
|
||
" ══════════════════════════════════════════════════════════════════
|
||
IF lv_parent_count = 0.
|
||
rv_code = ls_current_mat-zzlzcodsort.
|
||
WRITE: / ' -> Keine Parents, Code bleibt:', rv_code.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 5: REINE FÄLLE (NUR EIN TYP VORHANDEN)
|
||
" ══════════════════════════════════════════════════════════════════
|
||
|
||
" REGEL 1: Nur C → C0X*
|
||
IF lv_has_c = abap_true AND
|
||
lv_has_s = abap_false AND lv_has_o = abap_false AND
|
||
lv_has_e = abap_false AND lv_has_a = abap_false.
|
||
rv_code = 'C0X' && lv_position_4.
|
||
WRITE: / ' REGEL 1: Nur C → C0X*'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" REGEL 2: Nur E → E0X*
|
||
IF lv_has_e = abap_true AND
|
||
lv_has_s = abap_false AND lv_has_o = abap_false AND
|
||
lv_has_c = abap_false AND lv_has_a = abap_false.
|
||
rv_code = 'E0X' && lv_position_4.
|
||
WRITE: / ' REGEL 2: Nur E → E0X*'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" REGEL 3: Nur O → O0X*
|
||
IF lv_has_o = abap_true AND
|
||
lv_has_s = abap_false AND lv_has_e = abap_false AND
|
||
lv_has_c = abap_false AND lv_has_a = abap_false.
|
||
rv_code = 'O0X' && lv_position_4.
|
||
WRITE: / ' REGEL 3: Nur O → O0X*'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" REGEL 4: Nur N → N0X*
|
||
* IF lv_has_n = abap_true AND
|
||
* lv_has_s = abap_false AND lv_has_o = abap_false AND
|
||
* lv_has_e = abap_false AND lv_has_c = abap_false AND lv_has_a = abap_false.
|
||
* rv_code = 'N0X' && lv_position_4.
|
||
* WRITE: / ' REGEL 4: Nur N → N0X*'.
|
||
* RETURN.
|
||
* ENDIF.
|
||
|
||
" REGEL 5: Nur A → A0X*
|
||
IF lv_has_a = abap_true AND
|
||
lv_has_s = abap_false AND lv_has_o = abap_false AND
|
||
lv_has_e = abap_false AND lv_has_c = abap_false .
|
||
rv_code = 'A0X' && lv_position_4.
|
||
WRITE: / ' REGEL 5: Nur A → A0X*'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 6: GEMISCHTE FÄLLE → PROZENTBERECHNUNG
|
||
" ══════════════════════════════════════════════════════════════════
|
||
WRITE: / ' GEMISCHTER FALL → Prozentberechnung'.
|
||
WRITE: / ' Berechne Sonder-Anteil (O + E = 100%, C/N/A = 0%):'.
|
||
|
||
CLEAR: lv_sonder_weighted, lv_core_weighted, lv_total_weighted.
|
||
|
||
" Durchlaufe alle Parents und berechne gewichtete Anteile
|
||
LOOP AT gt_bom_relations INTO ls_relation WHERE child = iv_matnr.
|
||
READ TABLE gt_materials WITH TABLE KEY matnr = ls_relation-parent INTO ls_parent.
|
||
IF sy-subrc <> 0 OR ls_parent-zzlzcodsort IS INITIAL OR
|
||
ls_parent-zzlzcodsort = 'ZZZZ'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" *** NEU: Parent mit Status 99 überspringen ***
|
||
IF ls_parent-mstae = '99'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" Hole Verbrauch (mit Fallback)
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_parent-matnr INTO ls_consumption.
|
||
DATA: lv_parent_verbrauch TYPE p DECIMALS 2.
|
||
|
||
IF sy-subrc = 0 AND ls_consumption-gsv_korr > 0.
|
||
lv_parent_verbrauch = ls_consumption-gsv_korr.
|
||
ELSE.
|
||
lv_parent_verbrauch = 1.
|
||
ENDIF.
|
||
|
||
" Gewichtete Menge = Menge × Verbrauch
|
||
DATA(lv_gewichtete_menge) = ls_relation-menge * lv_parent_verbrauch.
|
||
|
||
WRITE: / ' Parent:', ls_parent-matnr,
|
||
' Code:', ls_parent-zzlzcodsort,
|
||
' Menge:', ls_relation-menge,
|
||
' Verbrauch:', lv_parent_verbrauch,
|
||
' Gewicht:', lv_gewichtete_menge.
|
||
|
||
" Klassifiziere Parent-Code
|
||
CASE ls_parent-zzlzcodsort(1).
|
||
WHEN 'O' OR 'E'.
|
||
" O und E = 100% Sonder
|
||
lv_sonder_weighted = lv_sonder_weighted + lv_gewichtete_menge.
|
||
WRITE: ' → 100% Sonder'.
|
||
|
||
WHEN 'S'.
|
||
" S-Code: Hole Prozentsatz aus Code
|
||
DATA(lv_s_prozent) = get_code_percentage( ls_parent-zzlzcodsort ).
|
||
lv_sonder_weighted = lv_sonder_weighted +
|
||
( lv_gewichtete_menge * lv_s_prozent / 100 ).
|
||
lv_core_weighted = lv_core_weighted +
|
||
( lv_gewichtete_menge * ( 100 - lv_s_prozent ) / 100 ).
|
||
WRITE: ' → ', lv_s_prozent, '% Sonder'.
|
||
|
||
WHEN 'C' OR 'A'.
|
||
" C, N, A = 0% Sonder (100% Core)
|
||
lv_core_weighted = lv_core_weighted + lv_gewichtete_menge.
|
||
WRITE: ' → 0% Sonder (Core)'.
|
||
ENDCASE.
|
||
ENDLOOP.
|
||
|
||
" Berechne Gesamtprozentsatz
|
||
lv_total_weighted = lv_sonder_weighted + lv_core_weighted.
|
||
|
||
IF lv_total_weighted > 0.
|
||
lv_percentage = ( lv_sonder_weighted / lv_total_weighted ) * 100.
|
||
ELSE.
|
||
lv_percentage = 0.
|
||
ENDIF.
|
||
|
||
WRITE: / ' Sonder-Gewicht:', lv_sonder_weighted.
|
||
WRITE: / ' Core-Gewicht:', lv_core_weighted.
|
||
WRITE: / ' Total-Gewicht:', lv_total_weighted.
|
||
WRITE: / ' Sonder-Prozentsatz:', lv_percentage, '%'.
|
||
|
||
" ══════════════════════════════════════════════════════════════════
|
||
" SCHRITT 7: SCHWELLENWERTE (FLOOR-LOGIK)
|
||
" ══════════════════════════════════════════════════════════════════
|
||
|
||
" Mini-Schwelle: 0 < % < 3 → Core
|
||
IF lv_percentage > 0 AND lv_percentage < 3.
|
||
rv_code = 'C0X' && lv_position_4.
|
||
WRITE: / ' Schwelle: <3% → C0X*'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Floor-Konvertierung (14.73% → 14)
|
||
lv_pct_int = lv_percentage.
|
||
|
||
rv_code = COND #(
|
||
WHEN lv_pct_int >= 100 THEN 'S0X'
|
||
WHEN lv_pct_int >= 85 THEN 'S9T'
|
||
WHEN lv_pct_int >= 75 THEN 'S8T'
|
||
WHEN lv_pct_int >= 65 THEN 'S7T'
|
||
WHEN lv_pct_int >= 55 THEN 'S6T'
|
||
WHEN lv_pct_int >= 45 THEN 'S5T'
|
||
WHEN lv_pct_int >= 35 THEN 'S4T'
|
||
WHEN lv_pct_int >= 25 THEN 'S3T'
|
||
WHEN lv_pct_int >= 15 THEN 'S2T'
|
||
WHEN lv_pct_int >= 5 THEN 'S1T'
|
||
WHEN lv_pct_int >= 3 THEN 'S0T'
|
||
ELSE 'S0X'
|
||
) && lv_position_4.
|
||
|
||
WRITE: / ' Neuer Code (nach Schwellen):', rv_code.
|
||
WRITE: / '╚══════════════════════════════════════════════════════════╝'.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
METHOD calculate_sonder_percentage.
|
||
DATA: lv_sonder_weighted TYPE p DECIMALS 3,
|
||
lv_core_weighted TYPE p DECIMALS 3,
|
||
lv_total_weighted TYPE p DECIMALS 3,
|
||
lv_parent_verbrauch TYPE p DECIMALS 3,
|
||
lv_gewichtete_menge TYPE p DECIMALS 3,
|
||
lv_sonder_prozent TYPE p DECIMALS 2,
|
||
ls_parent TYPE ty_material,
|
||
ls_relation TYPE ty_bom_relation,
|
||
ls_consumption TYPE ty_consumption.
|
||
|
||
CLEAR: rv_percentage, lv_sonder_weighted, lv_core_weighted, lv_total_weighted.
|
||
|
||
LOOP AT gt_bom_relations INTO ls_relation WHERE child = iv_matnr.
|
||
|
||
READ TABLE gt_materials WITH TABLE KEY matnr = ls_relation-parent INTO ls_parent.
|
||
IF sy-subrc <> 0.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_parent-matnr INTO ls_consumption.
|
||
IF sy-subrc = 0 AND ls_consumption-gsv_korr > 0.
|
||
lv_parent_verbrauch = ls_consumption-gsv_korr.
|
||
ELSE.
|
||
lv_parent_verbrauch = 1.
|
||
ENDIF.
|
||
|
||
|
||
|
||
lv_gewichtete_menge = ls_relation-menge * lv_parent_verbrauch.
|
||
|
||
" S, O und E zählen als Sonder
|
||
CASE ls_parent-zzlzcodsort(1).
|
||
WHEN 'S' OR 'O' OR 'E'.
|
||
" *** NEU: Prüfe Position 3 für 'X' (100% Sonder) ***
|
||
IF strlen( ls_parent-zzlzcodsort ) >= 3 AND ls_parent-zzlzcodsort+2(1) = 'X'.
|
||
" 100% Sonder (O0X, E0X, S0X)
|
||
lv_sonder_weighted = lv_sonder_weighted + lv_gewichtete_menge.
|
||
ELSE.
|
||
" Prozent-Anteil berechnen
|
||
DATA(lv_parent_code) = ls_parent-zzlzcodsort.
|
||
DATA(lv_sonder_prozent_temp) = get_code_percentage( lv_parent_code ).
|
||
lv_sonder_prozent = lv_sonder_prozent_temp.
|
||
|
||
lv_sonder_weighted = lv_sonder_weighted +
|
||
( lv_gewichtete_menge * lv_sonder_prozent / 100 ).
|
||
lv_core_weighted = lv_core_weighted +
|
||
( lv_gewichtete_menge * ( 100 - lv_sonder_prozent ) / 100 ).
|
||
ENDIF.
|
||
|
||
WHEN 'C' OR 'N' OR 'A'.
|
||
" 100% Core
|
||
lv_core_weighted = lv_core_weighted + lv_gewichtete_menge.
|
||
ENDCASE.
|
||
ENDLOOP.
|
||
|
||
lv_total_weighted = lv_sonder_weighted + lv_core_weighted.
|
||
|
||
IF lv_total_weighted > 0.
|
||
rv_percentage = ( lv_sonder_weighted / lv_total_weighted ) * 100.
|
||
ELSE.
|
||
rv_percentage = 0.
|
||
ENDIF.
|
||
ENDMETHOD.
|
||
|
||
|
||
METHOD get_code_percentage.
|
||
" %-Wert aus S/O/E-Code ableiten (…nT / …0X) – Mittelwerte je Range
|
||
DATA: lv_digit TYPE c.
|
||
lv_digit = iv_code+1(1).
|
||
|
||
rv_percentage = COND ty_percentage(
|
||
WHEN iv_code+2(1) = 'X' AND lv_digit = '0' THEN 100 " *0X = 100%
|
||
WHEN lv_digit = '9' THEN '92' " 85–99 → 92.0
|
||
WHEN lv_digit = '8' THEN '79.5' " 75–84 → 79.5
|
||
WHEN lv_digit = '7' THEN '69.5' " 65–74 → 69.5
|
||
WHEN lv_digit = '6' THEN '59.5'
|
||
WHEN lv_digit = '5' THEN '49.5'
|
||
WHEN lv_digit = '4' THEN '39.5'
|
||
WHEN lv_digit = '3' THEN '29.5'
|
||
WHEN lv_digit = '2' THEN '19.5'
|
||
WHEN lv_digit = '1' THEN '9.5'
|
||
WHEN lv_digit = '0' AND iv_code+2(1) = 'T' THEN '2' " *0T = ~2%
|
||
ELSE '0'
|
||
).
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
METHOD build_graph_nodes.
|
||
DATA: lv_verbrauch_text TYPE string,
|
||
lv_child_count TYPE i,
|
||
lv_verbrauch_monat TYPE p DECIMALS 2,
|
||
lv_is_debug TYPE abap_bool.
|
||
|
||
" Prüfe ob Debug-Material
|
||
lv_is_debug = me->is_debug_material( iv_matnr ).
|
||
|
||
" Debug-Ausgabe für TOP-Material
|
||
IF iv_level = 0 AND lv_is_debug = abap_true.
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ BUILD_GRAPH_NODES für TOP-Material:', iv_matnr.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
"Write: / 'Anzahl Beziehungen in gt_bom_relations:', lines( gt_bom_relations ).
|
||
ENDIF.
|
||
|
||
" Sicherheitsabbruch bei zu tiefer Rekursion
|
||
IF iv_level > 20.
|
||
IF lv_is_debug = abap_true.
|
||
"Write: / '*** ABBRUCH: Level > 20 erreicht ***'.
|
||
ENDIF.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Hole Stammdaten des aktuellen Knotens
|
||
READ TABLE gt_materials WITH KEY matnr = iv_matnr INTO DATA(ls_material).
|
||
IF sy-subrc <> 0.
|
||
IF lv_is_debug = abap_true.
|
||
"Write: / '*** Material', iv_matnr, 'nicht in gt_materials gefunden ***'.
|
||
ENDIF.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" ===== VERBRAUCHSDATEN MIT FORMATIERUNG =====
|
||
READ TABLE gt_consumption WITH KEY matnr = iv_matnr INTO DATA(ls_consumption).
|
||
|
||
IF sy-subrc = 0 AND ls_consumption-gsv_korr > 0.
|
||
" Berechne Durchschnitt pro Monat
|
||
lv_verbrauch_monat = ls_consumption-gsv_korr / 12.
|
||
|
||
" Formatiere mit Tausender-Trennzeichen und ohne Dezimalstellen
|
||
lv_verbrauch_text = |{ lv_verbrauch_monat NUMBER = USER DECIMALS = 0 } / Mon|.
|
||
|
||
IF lv_is_debug = abap_true.
|
||
"Write: / 'Material', iv_matnr, ':'.
|
||
"Write: / ' Jahresverbrauch:', ls_consumption-gsv_korr.
|
||
"Write: / ' Monatsverbrauch:', lv_verbrauch_monat.
|
||
"Write: / ' Formatiert:', lv_verbrauch_text.
|
||
ENDIF.
|
||
ELSE.
|
||
" Kein Verbrauch vorhanden
|
||
lv_verbrauch_text = '0'.
|
||
|
||
IF lv_is_debug = abap_true.
|
||
"Write: / 'Material', iv_matnr, ': KEIN Verbrauch in gt_consumption'.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ===== KNOTEN ZUR AUSGABE HINZUFÜGEN =====
|
||
APPEND VALUE #(
|
||
node_key = iv_matnr
|
||
parent_key = iv_parent_key
|
||
matnr = ls_material-matnr
|
||
menge = iv_menge
|
||
zzlzcod = ls_material-zzlzcod
|
||
zzlzcodsort = ls_material-zzlzcodsort
|
||
level = iv_level
|
||
verbrauch_text = lv_verbrauch_text
|
||
) TO ct_nodes.
|
||
|
||
" ===== DEBUG: ZÄHLE KINDER =====
|
||
IF lv_is_debug = abap_true.
|
||
CLEAR lv_child_count.
|
||
LOOP AT gt_bom_relations INTO DATA(ls_check) WHERE parent = iv_matnr.
|
||
ADD 1 TO lv_child_count.
|
||
ENDLOOP.
|
||
|
||
IF lv_child_count > 0.
|
||
"Write: / ' Material', iv_matnr, 'hat', lv_child_count, 'Komponenten'.
|
||
ELSE.
|
||
"Write: / ' Material', iv_matnr, 'hat KEINE Komponenten (Endprodukt)'.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ===== REKURSION: VERARBEITE ALLE KINDER =====
|
||
LOOP AT gt_bom_relations INTO DATA(ls_bom_rel) WHERE parent = iv_matnr.
|
||
|
||
IF lv_is_debug = abap_true.
|
||
|
||
DATA: lv_next_level TYPE i.
|
||
lv_next_level = iv_level + 1.
|
||
"Write: / ' → Rekursion für Kind:', ls_bom_rel-child,
|
||
"'Menge:', ls_bom_rel-menge,
|
||
"'Level:', lv_next_level.
|
||
|
||
|
||
|
||
|
||
ENDIF.
|
||
|
||
" Rekursiver Aufruf für jedes Kind
|
||
me->build_graph_nodes(
|
||
EXPORTING
|
||
iv_matnr = ls_bom_rel-child
|
||
iv_parent_key = iv_matnr
|
||
iv_menge = ls_bom_rel-menge
|
||
iv_level = iv_level + 1
|
||
CHANGING
|
||
ct_nodes = ct_nodes
|
||
).
|
||
ENDLOOP.
|
||
|
||
" ===== ABSCHLUSS FÜR TOP-MATERIAL =====
|
||
IF iv_level = 0 AND lv_is_debug = abap_true.
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ BUILD_GRAPH_NODES ABGESCHLOSSEN'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
"Write: / 'Anzahl Knoten im Baum:', lines( ct_nodes ).
|
||
ENDIF.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
|
||
" SORTIMENTSCODE ENDE
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
METHOD display_gozinto_graph.
|
||
" Zeigt ALLE selektierten TOP-Materialien in EINEM ALV
|
||
" mit Code-Änderungen und Berechnungsformel
|
||
|
||
TYPES: BEGIN OF ty_tree_extended,
|
||
top_material TYPE matnr,
|
||
hierarchy_text TYPE string,
|
||
matnr TYPE matnr,
|
||
menge TYPE kmpmg,
|
||
code_alt TYPE char4,
|
||
code_neu TYPE char4,
|
||
zzlzcodsort TYPE char4,
|
||
verbrauch TYPE string,
|
||
formel TYPE string,
|
||
verwendungen TYPE string,
|
||
level TYPE i,
|
||
END OF ty_tree_extended.
|
||
|
||
DATA: lt_all_display TYPE STANDARD TABLE OF ty_tree_extended,
|
||
lt_nodes TYPE tt_nodes,
|
||
lv_mat_count TYPE i.
|
||
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ KOMBINIERTE GRAPH-ANZEIGE (ALLE TOP-MATERIALIEN) ║'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
|
||
" ========================================
|
||
" FÜR JEDES SELEKTIERTE MATERIAL
|
||
" ========================================
|
||
LOOP AT s_matnr INTO DATA(ls_matnr_range).
|
||
IF ls_matnr_range-sign = 'I' AND ls_matnr_range-option = 'EQ'.
|
||
|
||
ADD 1 TO lv_mat_count.
|
||
DATA(lv_top_mat) = ls_matnr_range-low.
|
||
|
||
"Write: / 'Verarbeite TOP-Material', lv_mat_count, ':', lv_top_mat.
|
||
|
||
" Baue Hierarchie
|
||
CLEAR lt_nodes.
|
||
me->build_graph_nodes(
|
||
EXPORTING
|
||
iv_matnr = lv_top_mat
|
||
iv_parent_key = ''
|
||
iv_menge = 1
|
||
iv_level = 0
|
||
CHANGING
|
||
ct_nodes = lt_nodes ).
|
||
|
||
IF lt_nodes IS INITIAL.
|
||
"Write: / ' -> Keine Hierarchie gefunden'.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" TRENNLINIE EINFÜGEN (außer beim ersten Material)
|
||
" ========================================
|
||
IF lv_mat_count > 1.
|
||
APPEND VALUE #(
|
||
top_material = lv_top_mat
|
||
hierarchy_text = '────────────────────────────────────────────────'
|
||
level = -1
|
||
) TO lt_all_display.
|
||
ENDIF.
|
||
|
||
" ========================================
|
||
" VERARBEITE JEDEN KNOTEN
|
||
" ========================================
|
||
LOOP AT lt_nodes INTO DATA(ls_node).
|
||
|
||
" *** CODE VORHER/NACHHER ***
|
||
DATA: lv_code_alt TYPE char4,
|
||
lv_code_neu TYPE char4.
|
||
|
||
READ TABLE gt_results INTO DATA(ls_result)
|
||
WITH KEY matnr = ls_node-matnr.
|
||
IF sy-subrc = 0.
|
||
lv_code_alt = ls_result-old_code.
|
||
lv_code_neu = ls_result-new_code.
|
||
ELSE.
|
||
lv_code_alt = ls_node-zzlzcod.
|
||
lv_code_neu = ls_node-zzlzcod.
|
||
ENDIF.
|
||
|
||
" *** FORMEL BERECHNEN ***
|
||
DATA: lv_formel TYPE string.
|
||
CLEAR lv_formel.
|
||
|
||
IF ls_node-matnr CA 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
|
||
" Nur für Komponenten (B-Materialien)
|
||
|
||
DATA: lt_parent_info TYPE STANDARD TABLE OF string,
|
||
lv_total_verb TYPE p DECIMALS 2,
|
||
lv_a_verb TYPE p DECIMALS 2,
|
||
lv_parent_count TYPE i,
|
||
lv_has_a TYPE abap_bool,
|
||
lv_has_n TYPE abap_bool,
|
||
lv_has_e TYPE abap_bool.
|
||
|
||
CLEAR: lt_parent_info, lv_total_verb, lv_a_verb, lv_parent_count,
|
||
lv_has_a, lv_has_n, lv_has_e.
|
||
|
||
" Sammle Parent-Informationen
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel_form)
|
||
WHERE child = ls_node-matnr.
|
||
|
||
ADD 1 TO lv_parent_count.
|
||
|
||
READ TABLE gt_materials WITH KEY matnr = ls_rel_form-parent
|
||
INTO DATA(ls_parent_mat).
|
||
IF sy-subrc <> 0.
|
||
CONTINUE.
|
||
ENDIF.
|
||
|
||
" Hole Verbrauch
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_rel_form-parent
|
||
INTO DATA(ls_cons_form).
|
||
|
||
DATA: lv_parent_verb TYPE p DECIMALS 2.
|
||
IF sy-subrc = 0 AND ls_cons_form-gsv_korr > 0.
|
||
lv_parent_verb = ls_cons_form-gsv_korr.
|
||
ELSE.
|
||
lv_parent_verb = 1.
|
||
ENDIF.
|
||
|
||
DATA: lv_gewicht TYPE p DECIMALS 2.
|
||
lv_gewicht = ls_rel_form-menge * lv_parent_verb.
|
||
lv_total_verb = lv_total_verb + lv_gewicht.
|
||
|
||
" Prüfe Code-Typ
|
||
CASE ls_parent_mat-zzlzcod+0(1).
|
||
WHEN 'A'.
|
||
lv_has_a = abap_true.
|
||
|
||
DATA: lv_prozent TYPE p DECIMALS 2.
|
||
DATA(lv_digit) = ls_parent_mat-zzlzcod+1(1).
|
||
|
||
IF lv_digit = '0' AND ls_parent_mat-zzlzcod+2(1) = 'X'.
|
||
lv_prozent = 100.
|
||
ELSEIF lv_digit BETWEEN '1' AND '9'.
|
||
CASE lv_digit.
|
||
WHEN '0'. lv_prozent = 2.
|
||
WHEN '1'. lv_prozent = 9.
|
||
WHEN '2'. lv_prozent = 19.
|
||
WHEN '3'. lv_prozent = 29.
|
||
WHEN '4'. lv_prozent = 39.
|
||
WHEN '5'. lv_prozent = 49.
|
||
WHEN '6'. lv_prozent = 59.
|
||
WHEN '7'. lv_prozent = 69.
|
||
WHEN '8'. lv_prozent = 79.
|
||
WHEN '9'. lv_prozent = 92.
|
||
ENDCASE.
|
||
ELSEIF lv_digit = '0' AND ls_parent_mat-zzlzcod+2(1) = 'T'.
|
||
lv_prozent = 2.
|
||
ENDIF.
|
||
|
||
lv_a_verb = lv_a_verb + ( lv_gewicht * lv_prozent / 100 ).
|
||
|
||
DATA: lv_parent_text TYPE string.
|
||
lv_parent_text = |{ ls_rel_form-parent }({ ls_parent_mat-zzlzcod }):{ lv_parent_verb NUMBER = USER DECIMALS = 0 }×{ lv_prozent }%|.
|
||
APPEND lv_parent_text TO lt_parent_info.
|
||
|
||
WHEN 'E'.
|
||
lv_has_e = abap_true.
|
||
lv_parent_text = |{ ls_rel_form-parent }(E0X):{ lv_parent_verb NUMBER = USER DECIMALS = 0 }|.
|
||
APPEND lv_parent_text TO lt_parent_info.
|
||
|
||
WHEN 'N'.
|
||
lv_has_n = abap_true.
|
||
lv_parent_text = |{ ls_rel_form-parent }(N0X):{ lv_parent_verb NUMBER = USER DECIMALS = 0 }×0%|.
|
||
APPEND lv_parent_text TO lt_parent_info.
|
||
|
||
ENDCASE.
|
||
|
||
ENDLOOP.
|
||
|
||
" Erstelle Formel-Text
|
||
IF lv_parent_count > 0.
|
||
DATA: lv_regel TYPE string.
|
||
|
||
IF lv_has_e = abap_true AND lv_has_n = abap_true.
|
||
lv_regel = 'E+N→N0X'.
|
||
lv_formel = |Regel:{ lv_regel } (N dominiert)|.
|
||
|
||
ELSEIF lv_has_e = abap_true AND lv_has_n = abap_false.
|
||
lv_regel = 'E→E0X'.
|
||
lv_formel = |Regel:{ lv_regel } (E dominiert)|.
|
||
|
||
ELSEIF lv_has_n = abap_true AND lv_has_a = abap_true AND lv_has_e = abap_false.
|
||
lv_regel = 'N+A→A0T'.
|
||
lv_formel = |Regel:{ lv_regel } (fix A0T)|.
|
||
|
||
ELSEIF lv_has_a = abap_true AND lv_has_n = abap_false AND lv_has_e = abap_false.
|
||
DATA: lv_anteil TYPE p DECIMALS 2.
|
||
IF lv_total_verb > 0.
|
||
lv_anteil = ( lv_a_verb / lv_total_verb ) * 100.
|
||
ELSE.
|
||
lv_anteil = 0.
|
||
ENDIF.
|
||
|
||
lv_formel = |A-Anteil={ lv_anteil NUMBER = USER DECIMALS = 1 }% (|.
|
||
|
||
DATA: lv_count TYPE i.
|
||
LOOP AT lt_parent_info INTO DATA(lv_pinfo).
|
||
lv_count = lv_count + 1.
|
||
IF lv_count = 1.
|
||
lv_formel = |{ lv_formel }{ lv_pinfo }|.
|
||
ELSE.
|
||
lv_formel = |{ lv_formel }+{ lv_pinfo }|.
|
||
ENDIF.
|
||
IF lv_count >= 3.
|
||
IF lines( lt_parent_info ) > 3.
|
||
DATA(lv_rest) = lines( lt_parent_info ) - 3.
|
||
lv_formel = |{ lv_formel }+{ lv_rest }mehr|.
|
||
ENDIF.
|
||
EXIT.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
lv_formel = |{ lv_formel })→{ lv_code_neu }|.
|
||
|
||
ELSEIF lv_has_n = abap_true AND lv_has_a = abap_false AND lv_has_e = abap_false.
|
||
lv_regel = 'Nur N'.
|
||
lv_formel = |Regel:{ lv_regel } → N0X|.
|
||
|
||
ELSE.
|
||
lv_formel = 'Keine Vererbung'.
|
||
ENDIF.
|
||
|
||
ELSE.
|
||
lv_formel = 'TOP-Material'.
|
||
ENDIF.
|
||
|
||
ELSE.
|
||
lv_formel = 'TOP-Material (keine Vererbung)'.
|
||
ENDIF.
|
||
|
||
" *** VERWENDUNGEN ***
|
||
DATA: lv_usage_text TYPE string.
|
||
CLEAR lv_usage_text.
|
||
|
||
IF ls_node-matnr CA 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
|
||
DATA: lt_usage_info TYPE SORTED TABLE OF string
|
||
WITH UNIQUE KEY table_line,
|
||
lv_usage_count TYPE i,
|
||
lv_total_usages TYPE i.
|
||
|
||
CLEAR lt_usage_info.
|
||
|
||
LOOP AT gt_bom_relations INTO DATA(ls_rel_use)
|
||
WHERE child = ls_node-matnr.
|
||
|
||
READ TABLE gt_consumption WITH KEY matnr = ls_rel_use-parent
|
||
INTO DATA(ls_cons_use).
|
||
|
||
IF sy-subrc = 0 AND ls_cons_use-gsv_korr > 0 AND
|
||
ls_rel_use-parent NA 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
|
||
|
||
DATA: lv_verbrauch_monat TYPE p DECIMALS 0.
|
||
lv_verbrauch_monat = ls_cons_use-gsv_korr / 12.
|
||
|
||
DATA: lv_usage_entry TYPE string.
|
||
lv_usage_entry = |{ ls_rel_use-parent }({ lv_verbrauch_monat NUMBER = USER DECIMALS = 0 })|.
|
||
|
||
INSERT lv_usage_entry INTO TABLE lt_usage_info.
|
||
lv_usage_count = lines( lt_usage_info ).
|
||
|
||
IF lv_usage_count >= 10.
|
||
EXIT.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
IF lt_usage_info IS NOT INITIAL.
|
||
lv_usage_text = 'Verwendet in: '.
|
||
DATA: lv_first TYPE abap_bool VALUE abap_true.
|
||
LOOP AT lt_usage_info INTO DATA(lv_usage_entry_text).
|
||
IF lv_first = abap_true.
|
||
lv_usage_text = |{ lv_usage_text }{ lv_usage_entry_text }|.
|
||
lv_first = abap_false.
|
||
ELSE.
|
||
lv_usage_text = |{ lv_usage_text }, { lv_usage_entry_text }|.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
CLEAR lv_total_usages.
|
||
LOOP AT gt_bom_relations TRANSPORTING NO FIELDS
|
||
WHERE child = ls_node-matnr.
|
||
ADD 1 TO lv_total_usages.
|
||
ENDLOOP.
|
||
|
||
IF lv_total_usages > 10.
|
||
DATA: lv_remaining TYPE i.
|
||
lv_remaining = lv_total_usages - 10.
|
||
lv_usage_text = |{ lv_usage_text } (+{ lv_remaining } weitere)|.
|
||
ENDIF.
|
||
ELSE.
|
||
lv_usage_text = ''.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" *** ERSTELLE ANZEIGE-EINTRAG ***
|
||
DATA(lv_indent) = repeat( val = ' ' occ = ls_node-level ).
|
||
DATA(lv_prefix) = COND string(
|
||
WHEN ls_node-level = 0 THEN '►'
|
||
WHEN ls_node-level = 1 THEN '├─'
|
||
WHEN ls_node-level = 2 THEN '│ └─'
|
||
ELSE '│ └─' ).
|
||
|
||
APPEND VALUE #(
|
||
top_material = lv_top_mat
|
||
hierarchy_text = |{ lv_indent }{ lv_prefix } { ls_node-matnr }|
|
||
matnr = ls_node-matnr
|
||
menge = ls_node-menge
|
||
code_alt = lv_code_alt
|
||
code_neu = lv_code_neu
|
||
zzlzcodsort = ls_node-zzlzcodsort
|
||
verbrauch = ls_node-verbrauch_text
|
||
formel = lv_formel
|
||
verwendungen = lv_usage_text
|
||
level = ls_node-level
|
||
) TO lt_all_display.
|
||
|
||
ENDLOOP.
|
||
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
" ========================================
|
||
" ANZEIGE ALS EIN ALV
|
||
" ========================================
|
||
IF lt_all_display IS INITIAL.
|
||
MESSAGE 'Keine Daten für Graph-Anzeige' TYPE 'I'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
TRY.
|
||
cl_salv_table=>factory(
|
||
IMPORTING r_salv_table = DATA(lo_alv)
|
||
CHANGING t_table = lt_all_display ).
|
||
|
||
DATA(lo_columns) = lo_alv->get_columns( ).
|
||
|
||
lo_columns->get_column( 'TOP_MATERIAL' )->set_long_text( 'TOP-Material' ).
|
||
lo_columns->get_column( 'TOP_MATERIAL' )->set_output_length( 12 ).
|
||
|
||
lo_columns->get_column( 'HIERARCHY_TEXT' )->set_long_text( 'Hierarchie' ).
|
||
lo_columns->get_column( 'HIERARCHY_TEXT' )->set_output_length( 40 ).
|
||
lo_columns->get_column( 'MATNR' )->set_visible( abap_false ).
|
||
lo_columns->get_column( 'LEVEL' )->set_visible( abap_false ).
|
||
lo_columns->get_column( 'MENGE' )->set_long_text( 'Menge' ).
|
||
|
||
lo_columns->get_column( 'CODE_ALT' )->set_long_text( 'Code VORHER' ).
|
||
lo_columns->get_column( 'CODE_NEU' )->set_long_text( 'Code NACHHER' ).
|
||
lo_columns->get_column( 'CODE_ALT' )->set_output_length( 10 ).
|
||
lo_columns->get_column( 'CODE_NEU' )->set_output_length( 10 ).
|
||
|
||
lo_columns->get_column( 'ZZLZCODSORT' )->set_long_text( 'Sort-Code' ).
|
||
lo_columns->get_column( 'VERBRAUCH' )->set_long_text( 'Verbrauch/Mon' ).
|
||
|
||
lo_columns->get_column( 'FORMEL' )->set_long_text( 'Berechnungsformel' ).
|
||
lo_columns->get_column( 'FORMEL' )->set_output_length( 60 ).
|
||
|
||
lo_columns->get_column( 'VERWENDUNGEN' )->set_long_text( 'Verwendungen' ).
|
||
lo_columns->get_column( 'VERWENDUNGEN' )->set_output_length( 80 ).
|
||
lo_columns->set_optimize( ).
|
||
|
||
lo_alv->get_display_settings( )->set_list_header(
|
||
|Kombinierte Hierarchie: { lv_mat_count } TOP-Materialien| ).
|
||
|
||
lo_alv->get_functions( )->set_all( ).
|
||
|
||
lo_alv->display( ).
|
||
|
||
CATCH cx_salv_msg.
|
||
MESSAGE 'Fehler bei kombinierter Graph-Anzeige' TYPE 'E'.
|
||
ENDTRY.
|
||
|
||
"Write: / ''.
|
||
"Write: / '╔════════════════════════════════════════════════════════════╗'.
|
||
"Write: / '║ KOMBINIERTE ANZEIGE ABGESCHLOSSEN ║'.
|
||
"Write: / '╚════════════════════════════════════════════════════════════╝'.
|
||
"Write: / 'Anzahl TOP-Materialien:', lv_mat_count.
|
||
"Write: / 'Anzahl Zeilen im ALV:', lines( lt_all_display ).
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
|
||
METHOD display_results.
|
||
DATA: lo_alv TYPE REF TO cl_salv_table.
|
||
|
||
TRY.
|
||
cl_salv_table=>factory(
|
||
IMPORTING
|
||
r_salv_table = lo_alv
|
||
CHANGING
|
||
t_table = gt_results ).
|
||
|
||
" Spaltenoptimierung
|
||
lo_alv->get_columns( )->set_optimize( abap_true ).
|
||
|
||
" Funktionen aktivieren
|
||
lo_alv->get_functions( )->set_all( abap_true ).
|
||
|
||
" Anzeige
|
||
lo_alv->display( ).
|
||
|
||
CATCH cx_salv_msg.
|
||
MESSAGE 'Fehler bei ALV-Anzeige' TYPE 'E'.
|
||
ENDTRY.
|
||
ENDMETHOD.
|
||
|
||
ENDCLASS.
|
||
|
||
*----------------------------------------------------------------------*
|
||
* Hauptprogramm
|
||
*----------------------------------------------------------------------*
|
||
START-OF-SELECTION.
|
||
|
||
"Ersetzte Parameter 251217
|
||
DATA(p_batch) = CONV i( 5000 ).
|
||
DATA(p_maxlv) = CONV i( 4 ).
|
||
|
||
|
||
|
||
p_mm = abap_false.
|
||
p_di = abap_true. "Default wie vorher
|
||
p_gozin = abap_false.
|
||
"END
|
||
|
||
|
||
|
||
|
||
" Validierungen
|
||
" IF p_alles = abap_true AND s_matnr[] IS NOT INITIAL.
|
||
" MESSAGE 'Entweder "Alle Materialien" oder Selektion angeben' TYPE 'E'.
|
||
" ENDIF.
|
||
|
||
IF p_batch > 50000.
|
||
MESSAGE 'Batch-Größe zu groß (max. 50000)' TYPE 'W'.
|
||
p_batch = 50000.
|
||
ENDIF.
|
||
|
||
" Hauptverarbeitung
|
||
DATA(lo_processor) = NEW lcl_lifecycle_processor( ).
|
||
lo_processor->execute( ).
|
||
|
||
END-OF-SELECTION.
|
||
" Ergebnisanzeige
|
||
|
||
IF p_gozin = 'X'.
|
||
lo_processor->display_results( ).
|
||
" Graph-Anzeige für ein ausgewähltes Material
|
||
" NEU: Graph-Anzeige für ALLE selektierten Materialien (max. 4 Stufen)
|
||
DATA: lv_material_count TYPE i.
|
||
|
||
LOOP AT s_matnr INTO DATA(ls_matnr_range).
|
||
" Verarbeite jeden Eintrag im Range
|
||
IF ls_matnr_range-sign = 'I' AND ls_matnr_range-option = 'EQ'.
|
||
" Einzelnes Material
|
||
ADD 1 TO lv_material_count.
|
||
""Write: / ''.
|
||
""Write: / '================================================'.
|
||
""Write: / 'Gozinto-Graph für Material:', ls_matnr_range-low.
|
||
""Write: / '================================================'.
|
||
" lo_processor->display_gozinto_graph( iv_top_material = ls_matnr_range-low ).
|
||
|
||
ELSEIF ls_matnr_range-sign = 'I' AND ls_matnr_range-option = 'BT'.
|
||
" Range von Materialien
|
||
SELECT matnr FROM mara
|
||
INTO @DATA(lv_matnr)
|
||
WHERE matnr BETWEEN @ls_matnr_range-low AND @ls_matnr_range-high.
|
||
|
||
ADD 1 TO lv_material_count.
|
||
""Write: / ''.
|
||
""Write: / '================================================'.
|
||
""Write: / 'Gozinto-Graph für Material:', lv_matnr.
|
||
""Write: / '================================================'.
|
||
lo_processor->display_gozinto_graph( iv_top_material = lv_matnr ).
|
||
|
||
" Optional: Begrenzung auf z.B. max 10 Materialien
|
||
IF lv_material_count >= 10.
|
||
""Write: / 'WARNUNG: Maximal 10 Graphen angezeigt. Weitere Materialien übersprungen.'.
|
||
EXIT.
|
||
ENDIF.
|
||
ENDSELECT.
|
||
ENDIF.
|
||
ENDLOOP.
|
||
|
||
|
||
lo_processor->display_gozinto_graph( iv_top_material = ls_matnr_range-low ).
|
||
|
||
|
||
IF lv_material_count = 0.
|
||
""Write: / 'Keine Materialien für Gozinto-Graph gefunden.'.
|
||
ELSE.
|
||
""Write: / ''.
|
||
""Write: / '================================================'.
|
||
""Write: / 'Gesamt', lv_material_count, 'Gozinto-Graphen angezeigt.'.
|
||
ENDIF.
|
||
|
||
|
||
|
||
ENDIF.
|
||
*&---------------------------------------------------------------------*
|
||
*& Form DEACTIVATE_JOB
|
||
*& Entfernt Startbedingung des Jobs VC_AUFLOESUNG_ZLO
|
||
*& Nur im produktiven Modus (p_test = abap_false)
|
||
*&---------------------------------------------------------------------*
|
||
FORM deactivate_job.
|
||
DATA: lt_joblist TYPE STANDARD TABLE OF tbtcjob,
|
||
ls_job TYPE tbtcjob.
|
||
|
||
" Nur im produktiven Modus
|
||
IF p_test = abap_true.
|
||
WRITE: / 'Testmodus: Job-Deaktivierung übersprungen'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
CLEAR: gv_job_was_active, gv_job_count.
|
||
|
||
" Suche freigegebenen Job (Status 'S' = Scheduled/Released)
|
||
CALL FUNCTION 'BP_JOB_SELECT'
|
||
EXPORTING
|
||
jobselect_dialog = abap_false
|
||
jobname = gc_job_name
|
||
username = gc_job_user
|
||
TABLES
|
||
jobselect_joblist = lt_joblist
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc <> 0.
|
||
WRITE: / 'WARNUNG: Job', gc_job_name, 'nicht gefunden'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Finde freigegebenen Job (Status S = Scheduled)
|
||
LOOP AT lt_joblist INTO ls_job WHERE status = 'S'.
|
||
EXIT.
|
||
ENDLOOP.
|
||
|
||
IF sy-subrc <> 0.
|
||
WRITE: / 'INFO: Kein freigegebener Job', gc_job_name, 'gefunden'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Sichere Job-Count für spätere Reaktivierung
|
||
gv_job_count = ls_job-jobcount.
|
||
gv_job_was_active = abap_true.
|
||
|
||
" Entferne Startbedingung (Job auf 'P' = Planned setzen)
|
||
CALL FUNCTION 'BP_JOB_MODIFY'
|
||
EXPORTING
|
||
jobname = gc_job_name
|
||
jobcount = gv_job_count
|
||
new_status = 'P'
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc = 0.
|
||
WRITE: / 'Job', gc_job_name, 'deaktiviert (Startbedingung entfernt)'.
|
||
ELSE.
|
||
WRITE: / 'FEHLER: Job', gc_job_name, 'konnte nicht deaktiviert werden'.
|
||
CLEAR gv_job_was_active.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form REACTIVATE_JOB
|
||
*& Setzt Startbedingung des Jobs wieder (01:00 nächster Tag, täglich)
|
||
*& Nur wenn Job vorher aktiv war und produktiver Modus
|
||
*&---------------------------------------------------------------------*
|
||
FORM reactivate_job.
|
||
DATA: lv_next_date TYPE sy-datum,
|
||
lv_start_time TYPE sy-uzeit VALUE '010000'.
|
||
|
||
" Nur im produktiven Modus und wenn Job vorher aktiv war
|
||
IF p_test = abap_true.
|
||
WRITE: / 'Testmodus: Job-Reaktivierung übersprungen'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
IF gv_job_was_active = abap_false.
|
||
WRITE: / 'INFO: Job war nicht aktiv, keine Reaktivierung nötig'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Berechne nächsten Tag
|
||
lv_next_date = sy-datum + 1.
|
||
|
||
" Setze Startbedingung wieder (täglich um 01:00)
|
||
CALL FUNCTION 'BP_JOB_MODIFY'
|
||
EXPORTING
|
||
jobname = gc_job_name
|
||
jobcount = gv_job_count
|
||
new_status = 'S'
|
||
new_sdlstrtdt = lv_next_date
|
||
new_sdlstrttm = lv_start_time
|
||
new_prddays = 1
|
||
new_periodic = abap_true
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc = 0.
|
||
WRITE: / 'Job', gc_job_name, 'reaktiviert für', lv_next_date, lv_start_time.
|
||
ELSE.
|
||
WRITE: / 'FEHLER: Job', gc_job_name, 'konnte nicht reaktiviert werden!'.
|
||
WRITE: / 'ACHTUNG: Job muss manuell in SM37 freigegeben werden!'.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
*----------------------------------------------------------------------*
|
||
* Unit-Tests (wenn p_test = 'X')
|
||
*----------------------------------------------------------------------*
|
||
AT SELECTION-SCREEN.
|
||
IF p_test = abap_true AND sy-ucomm = 'ONLI'.
|
||
" Test-Framework aktivieren
|
||
PERFORM run_unit_tests.
|
||
ENDIF.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form run_unit_tests
|
||
*&---------------------------------------------------------------------*
|
||
FORM run_unit_tests.
|
||
DATA: lv_test_result TYPE string.
|
||
|
||
""Write: / '=== UNIT TESTS ==='.
|
||
|
||
" Test 1: Dominanzmatrix
|
||
lv_test_result = 'Test Dominanzmatrix: '.
|
||
" Implementierung der Testlogik
|
||
""Write: / lv_test_result, 'PASSED'.
|
||
|
||
" Test 2: Zirkularitätsprüfung
|
||
lv_test_result = 'Test Zirkularität: '.
|
||
" Implementierung der Testlogik
|
||
""Write: / lv_test_result, 'PASSED'.
|
||
"'KMAT', 'ZHAL', 'ZFER', 'HALB', 'FERT')
|
||
" AND zzlzcod NE 'ZZZZ'.
|
||
|
||
" Test 3: Batch-Verarbeitung
|
||
lv_test_result = 'Test Batch-Verarbeitung: '.
|
||
" Implementierung der Testlogik
|
||
""Write: / lv_test_result, 'PASSED'.
|
||
|
||
""Write: / '=================='.
|
||
ENDFORM.
|
||
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form PROTECT_VKNR_FROM_UPDATE
|
||
*& Schützt Materialien vor Update im LZCode-Modus
|
||
*&
|
||
*& Parameter iv_mode:
|
||
*& 'T' = Nur TOP-Materialien (ohne Parents in gt_bom_relations)
|
||
*& '1' = Alle mit Position 4 = '1' im Code
|
||
*& 'B' = Beides kombiniert
|
||
*&---------------------------------------------------------------------*
|
||
FORM protect_vknr_from_update USING iv_mode TYPE char1.
|
||
DATA: lv_removed_count TYPE i,
|
||
lv_code_char4 TYPE char4.
|
||
|
||
" Nur im LZCode-Modus aktiv
|
||
IF p_lzc <> abap_true.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Durchlaufe gt_results und schütze Materialien
|
||
LOOP AT gt_results ASSIGNING FIELD-SYMBOL(<fs_result>).
|
||
|
||
DATA(lv_protect) = abap_false.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" PRÜFUNG 1: TOP-Material? (keine Parents)
|
||
" ══════════════════════════════════════════════════════════════
|
||
IF iv_mode = 'T' OR iv_mode = 'B'.
|
||
READ TABLE gt_bom_relations WITH KEY child = <fs_result>-matnr
|
||
TRANSPORTING NO FIELDS.
|
||
IF sy-subrc <> 0.
|
||
" Keine Parents → TOP-Material
|
||
lv_protect = abap_true.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" PRÜFUNG 2: Position 4 = '1'?
|
||
" ══════════════════════════════════════════════════════════════
|
||
IF iv_mode = '1' OR iv_mode = 'B'.
|
||
lv_code_char4 = <fs_result>-new_code.
|
||
IF strlen( lv_code_char4 ) >= 4.
|
||
IF lv_code_char4+3(1) = '1'.
|
||
lv_protect = abap_true.
|
||
ENDIF.
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" SCHÜTZEN: changed = false, Code bleibt
|
||
" ══════════════════════════════════════════════════════════════
|
||
IF lv_protect = abap_true.
|
||
<fs_result>-changed = abap_false.
|
||
<fs_result>-new_code = <fs_result>-old_code.
|
||
ADD 1 TO lv_removed_count.
|
||
ENDIF.
|
||
|
||
ENDLOOP.
|
||
|
||
IF lv_removed_count > 0.
|
||
WRITE: / 'VKNR-Schutz (Modus:', iv_mode, '):',
|
||
lv_removed_count, 'Materialien geschützt'.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Globale Tabelle für gesicherte VKNR-Codes
|
||
*&---------------------------------------------------------------------*
|
||
TYPES: BEGIN OF ty_vknr_backup,
|
||
matnr TYPE matnr,
|
||
old_code TYPE char4,
|
||
END OF ty_vknr_backup.
|
||
|
||
DATA: gt_vknr_backup TYPE STANDARD TABLE OF ty_vknr_backup.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Globale Variablen für Job-Steuerung
|
||
*&---------------------------------------------------------------------*
|
||
DATA: gv_job_was_active TYPE abap_bool,
|
||
gv_job_count TYPE tbtcjob-jobcount.
|
||
|
||
CONSTANTS: gc_job_name TYPE tbtcjob-jobname VALUE 'VC_AUFLOESUNG_ZLO',
|
||
gc_job_user TYPE tbtcjob-authcknam VALUE 'KOI'.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form SAVE_VKNR_CODES
|
||
*& Sichert Codes von Materialien mit Position 4 = '1'
|
||
*& UND Materialien ohne Buchstaben (nur numerisch = VKNR)
|
||
*&---------------------------------------------------------------------*
|
||
FORM save_vknr_codes.
|
||
DATA: lv_code_char4 TYPE char4,
|
||
lv_matnr_str TYPE string.
|
||
|
||
CLEAR gt_vknr_backup.
|
||
|
||
LOOP AT gt_materials INTO DATA(ls_mat).
|
||
|
||
DATA(lv_save) = abap_false.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" PRÜFUNG 1: Position 4 = '1'
|
||
" ══════════════════════════════════════════════════════════════
|
||
lv_code_char4 = ls_mat-zzlzcod.
|
||
IF strlen( lv_code_char4 ) >= 4 AND lv_code_char4+3(1) = '1'.
|
||
lv_save = abap_true.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" PRÜFUNG 2: MATNR nur numerisch (keine Buchstaben = VKNR)
|
||
" ══════════════════════════════════════════════════════════════
|
||
lv_matnr_str = ls_mat-matnr.
|
||
CONDENSE lv_matnr_str NO-GAPS.
|
||
|
||
IF lv_matnr_str CO '0123456789'.
|
||
lv_save = abap_true.
|
||
ENDIF.
|
||
|
||
" ══════════════════════════════════════════════════════════════
|
||
" SICHERN
|
||
" ══════════════════════════════════════════════════════════════
|
||
IF lv_save = abap_true.
|
||
APPEND VALUE #( matnr = ls_mat-matnr
|
||
old_code = ls_mat-zzlzcod ) TO gt_vknr_backup.
|
||
ENDIF.
|
||
|
||
ENDLOOP.
|
||
|
||
IF lines( gt_vknr_backup ) > 0.
|
||
WRITE: / 'VKNR-Codes gesichert:', lines( gt_vknr_backup ), 'Materialien'.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form RESTORE_VKNR_CODES
|
||
*& Rekonstruiert gesicherte VKNR-Codes in gt_results
|
||
*&---------------------------------------------------------------------*
|
||
FORM restore_vknr_codes.
|
||
DATA: lv_restored TYPE i.
|
||
|
||
LOOP AT gt_vknr_backup INTO DATA(ls_backup).
|
||
|
||
" Finde Material in gt_results
|
||
READ TABLE gt_results ASSIGNING FIELD-SYMBOL(<fs_result>)
|
||
WITH KEY matnr = ls_backup-matnr.
|
||
|
||
IF sy-subrc = 0.
|
||
" Rekonstruiere: new_code = old_code, changed = false
|
||
<fs_result>-new_code = ls_backup-old_code.
|
||
<fs_result>-changed = abap_false.
|
||
ADD 1 TO lv_restored.
|
||
ENDIF.
|
||
|
||
ENDLOOP.
|
||
|
||
IF lv_restored > 0.
|
||
WRITE: / 'VKNR-Codes rekonstruiert:', lv_restored, 'Materialien'.
|
||
ENDIF.
|
||
|
||
ENDFORM. /// bitte das einbauen, weisst du wie ?Perfekt, jetzt habe ich alle Informationen. Hier ist der komplette, einbaufertige Code:
|
||
|
||
Zusammenfassung der Änderungen
|
||
|
||
|
||
|
||
Was
|
||
Wo
|
||
Beschreibung
|
||
|
||
|
||
|
||
|
||
Globale Variablen
|
||
Nach DATA: gt_vknr_backup...
|
||
Job-Steuerungsvariablen
|
||
|
||
|
||
FORM deactivate_job
|
||
Am Ende des Programms
|
||
Entfernt Startbedingung vor Lauf
|
||
|
||
|
||
FORM reactivate_job
|
||
Am Ende des Programms
|
||
Setzt Startbedingung nach Lauf
|
||
|
||
|
||
Aufruf in execute
|
||
In Methode execute
|
||
Aufrufe am Anfang und Ende
|
||
|
||
|
||
|
||
|
||
Code zum Einfügen
|
||
1. Globale Variablen (nach Zeile DATA: gt_vknr_backup TYPE STANDARD TABLE OF ty_vknr_backup.)
|
||
*&---------------------------------------------------------------------*
|
||
*& Globale Variablen für Job-Steuerung
|
||
*&---------------------------------------------------------------------*
|
||
DATA: gv_job_was_active TYPE abap_bool,
|
||
gv_job_count TYPE tbtcjob-jobcount.
|
||
|
||
CONSTANTS: gc_job_name TYPE tbtcjob-jobname VALUE 'VC_AUFLOESUNG_ZLO',
|
||
gc_job_user TYPE tbtcjob-authcknam VALUE 'KOI'.
|
||
|
||
|
||
2. Änderung in Methode execute (Klasse lcl_lifecycle_processor)
|
||
Ersetze die komplette Methode execute durch:
|
||
METHOD execute.
|
||
DATA: lv_start_time TYPE timestampl,
|
||
lv_end_time TYPE timestampl.
|
||
|
||
GET TIME STAMP FIELD lv_start_time.
|
||
|
||
" *** NEU: Job deaktivieren (nur produktiv) ***
|
||
PERFORM deactivate_job.
|
||
|
||
" Phase 1: Datenladen
|
||
load_materials( ).
|
||
|
||
me->debug_show_relations( ).
|
||
|
||
load_consumption_data( ).
|
||
|
||
" Phase 5: Berechnungen basierend auf Auswahl durchführen
|
||
IF p_lzc = abap_true.
|
||
PERFORM save_vknr_codes.
|
||
calculate_inheritance( ).
|
||
PERFORM restore_vknr_codes.
|
||
|
||
IF p_upda = abap_true AND p_test = abap_false.
|
||
update_database( ).
|
||
ENDIF.
|
||
|
||
ELSEIF p_sort = abap_true.
|
||
calculate_sortiment_inhe( ).
|
||
|
||
IF p_upda = abap_true AND p_test = abap_false.
|
||
update_database_sortiment( ).
|
||
ENDIF.
|
||
ENDIF.
|
||
|
||
GET TIME STAMP FIELD lv_end_time.
|
||
DATA(lv_runtime) = lv_end_time - lv_start_time.
|
||
|
||
" *** NEU: Job reaktivieren (nur produktiv) ***
|
||
PERFORM reactivate_job.
|
||
|
||
ENDMETHOD.
|
||
|
||
|
||
3. Neue FORMs (am Ende des Programms, vor AT SELECTION-SCREEN)
|
||
*&---------------------------------------------------------------------*
|
||
*& Form DEACTIVATE_JOB
|
||
*& Entfernt Startbedingung des Jobs VC_AUFLOESUNG_ZLO
|
||
*& Nur im produktiven Modus (p_test = abap_false)
|
||
*&---------------------------------------------------------------------*
|
||
FORM deactivate_job.
|
||
DATA: lt_joblist TYPE STANDARD TABLE OF tbtcjob,
|
||
ls_job TYPE tbtcjob.
|
||
|
||
" Nur im produktiven Modus
|
||
IF p_test = abap_true.
|
||
WRITE: / 'Testmodus: Job-Deaktivierung übersprungen'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
CLEAR: gv_job_was_active, gv_job_count.
|
||
|
||
" Suche freigegebenen Job (Status 'S' = Scheduled/Released)
|
||
CALL FUNCTION 'BP_JOB_SELECT'
|
||
EXPORTING
|
||
jobselect_dialog = abap_false
|
||
jobname = gc_job_name
|
||
username = gc_job_user
|
||
TABLES
|
||
jobselect_joblist = lt_joblist
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc <> 0.
|
||
WRITE: / 'WARNUNG: Job', gc_job_name, 'nicht gefunden'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Finde freigegebenen Job (Status S = Scheduled)
|
||
LOOP AT lt_joblist INTO ls_job WHERE status = 'S'.
|
||
EXIT.
|
||
ENDLOOP.
|
||
|
||
IF sy-subrc <> 0.
|
||
WRITE: / 'INFO: Kein freigegebener Job', gc_job_name, 'gefunden'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Sichere Job-Count für spätere Reaktivierung
|
||
gv_job_count = ls_job-jobcount.
|
||
gv_job_was_active = abap_true.
|
||
|
||
" Entferne Startbedingung (Job auf 'P' = Planned setzen)
|
||
CALL FUNCTION 'BP_JOB_MODIFY'
|
||
EXPORTING
|
||
jobname = gc_job_name
|
||
jobcount = gv_job_count
|
||
new_status = 'P'
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc = 0.
|
||
WRITE: / 'Job', gc_job_name, 'deaktiviert (Startbedingung entfernt)'.
|
||
ELSE.
|
||
WRITE: / 'FEHLER: Job', gc_job_name, 'konnte nicht deaktiviert werden'.
|
||
CLEAR gv_job_was_active.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
*&---------------------------------------------------------------------*
|
||
*& Form REACTIVATE_JOB
|
||
*& Setzt Startbedingung des Jobs wieder (01:00 nächster Tag, täglich)
|
||
*& Nur wenn Job vorher aktiv war und produktiver Modus
|
||
*&---------------------------------------------------------------------*
|
||
FORM reactivate_job.
|
||
DATA: lv_next_date TYPE sy-datum,
|
||
lv_start_time TYPE sy-uzeit VALUE '010000'.
|
||
|
||
" Nur im produktiven Modus und wenn Job vorher aktiv war
|
||
IF p_test = abap_true.
|
||
WRITE: / 'Testmodus: Job-Reaktivierung übersprungen'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
IF gv_job_was_active = abap_false.
|
||
WRITE: / 'INFO: Job war nicht aktiv, keine Reaktivierung nötig'.
|
||
RETURN.
|
||
ENDIF.
|
||
|
||
" Berechne nächsten Tag
|
||
lv_next_date = sy-datum + 1.
|
||
|
||
" Setze Startbedingung wieder (täglich um 01:00)
|
||
CALL FUNCTION 'BP_JOB_MODIFY'
|
||
EXPORTING
|
||
jobname = gc_job_name
|
||
jobcount = gv_job_count
|
||
new_status = 'S'
|
||
new_sdlstrtdt = lv_next_date
|
||
new_sdlstrttm = lv_start_time
|
||
new_prddays = 1
|
||
new_periodic = abap_true
|
||
EXCEPTIONS
|
||
OTHERS = 1.
|
||
|
||
IF sy-subrc = 0.
|
||
WRITE: / 'Job', gc_job_name, 'reaktiviert für', lv_next_date, lv_start_time.
|
||
ELSE.
|
||
WRITE: / 'FEHLER: Job', gc_job_name, 'konnte nicht reaktiviert werden!'.
|
||
WRITE: / 'ACHTUNG: Job muss manuell in SM37 freigegeben werden!'.
|
||
ENDIF.
|
||
|
||
ENDFORM.
|
||
|
||
|
||
Verhalten
|
||
|
||
|
||
|
||
Situation
|
||
Verhalten
|
||
|
||
|
||
|
||
|
||
Testmodus (p_test = 'X')
|
||
Job wird NICHT deaktiviert/reaktiviert
|
||
|
||
|
||
Produktiv (p_test = ' ')
|
||
Job wird vor Start deaktiviert, nach Ende reaktiviert
|
||
|
||
|
||
Job nicht gefunden
|
||
Warnung, Programm läuft weiter
|
||
|
||
|
||
Programmabbruch
|
||
Job bleibt deaktiviert (gewünscht)
|
||
|
||
|
||
Reaktivierung
|
||
Täglich 01:00 Uhr, periodisch
|
||
|
||
|
||
|
||
|
||
Einbau-Anleitung
|
||
|
||
Globale Variablen nach DATA: gt_vknr_backup... einfügen
|
||
Methode execute ersetzen (2 neue PERFORM-Aufrufe)
|
||
Zwei FORMs am Ende einfügen (vor AT SELECTION-SCREEN)
|
||
Aktivieren und testen (erst mit p_test = 'X')
|