diff --git a/jobcontrol.md b/jobcontrol.md new file mode 100644 index 0000000..fc44732 --- /dev/null +++ b/jobcontrol.md @@ -0,0 +1,4001 @@ +*&---------------------------------------------------------------------* +*& 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. + + " 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|. + + 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(). + IF -mstae = '99'. + " Status 99 = Ausgelaufen → ZZLZCODSORT leeren + -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(). + + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + " *** NEU: Status 99 = Ausgelaufen → Überspringen *** + IF -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 = -zzlzcodsort + new_code_sort = '' + changed_sort = COND #( WHEN -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( -zzlzcodsort ) >= 4 AND + -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 -zzlzcodsort <> lv_new_code. + " Speichere Änderung in Ergebnistabelle + READ TABLE gt_results ASSIGNING FIELD-SYMBOL() + WITH KEY matnr = lv_matnr. + + IF sy-subrc = 0. + -old_code_sort = -zzlzcodsort. + -new_code_sort = lv_new_code. + -changed_sort = abap_true. + ELSE. + APPEND VALUE #( + matnr = lv_matnr + old_code_sort = -zzlzcodsort + new_code_sort = lv_new_code + changed_sort = abap_true + ) TO gt_results. + ENDIF. + + " Update Material mit neuem Code + -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. +*----------------------------------------------------------------------* +* 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(). + + 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 = -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 = -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. + -changed = abap_false. + -new_code = -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. + +*&---------------------------------------------------------------------* +*& 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() + WITH KEY matnr = ls_backup-matnr. + + IF sy-subrc = 0. + " Rekonstruiere: new_code = old_code, changed = false + -new_code = ls_backup-old_code. + -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')