*---------------------------------- * (с) Слепаков *---------------------------------- DO. Это был тяжелый год Был он тяжелей, чем тот Неожиданно нам всем Много он принес проблем Но приходит новый год Он нам счастья принесет И я верю через год Дружно скажет наш народ ENDDO.
Great minds think alike
Слушаю последний выпуск подкаста:
https://radio-t.com/p/2024/07/27/podcast-920/ (тема про Haystack)
И там Григорий рассказывал про свою мечту, ну чтоб:
- исходный текст был rich-text, чтобы можно было пожирнять, картинку вставить внутрь
- объекты разработки хранились в базе данных
- использование объектов было индексировано
- версии объектов хранились прямо в базе данных без дополнительных гитов
- был тулинг/IDE всё это поддерживающий
- проект собирался не из файлов, а прямо из базы данных
Удивительно, но SAP/ABAP-разработка пошла по этому пути давным-давно, но:
- до rich-text они так и не добрались, хотя до до остального — в целом скорее да
- система получилась закрытой и своеобразной
… и мне это скорее нравится, если бы не это, то я бы скорее всего свичнулся.
Как в программе ABAP можно получить список последних введенных в поле значений?
Интересный вопрос.
Если кратко, то может быть и можно, но всё очень-очень сложно.
Тут вопрос в концепции.
С одной стороны, любая программа ABAP работает только на сервере. SAP GUI работает на компьютере пользователя и отвечает только за показ результата работы и ожидание дальнейших инструкций от пользователя. Пользователь видит результат, нажимает кнопку, SAP GUI передаёт нажатие кнопки на сервер, серверная программа снова отрабатывает и даёт новый результат, отправляет его на просмотр пользователю.
Это круг PAI-PBO, который никогда не прекращается.
BTW, с точки зрения архитектуры SAP GUI — это «тонкий клиент», так как никакая прикладная логика на нём не работает, только красивое отображение и приём-передача информации на сервер и обратно.
С другой стороны, список последних введённых значений хранится только на SAP GUI (то есть на компьютере пользователя), и сервер ничего не знает про это. Так же как и не знает какого цвета кнопки. Вы можете сами включить в SAP GUI любую тему и поменять все цвета, но на работу ABAP это никак не повлияет, и сервер про это никогда не узнает.
С точки зрения ABAP-сервера, нет никакой разницы, выбираете ли вы значение из истории, или вводите его вручную заново, или вставляете из буфера обмена.
На практике существуют некоторые пользовательские функции, когда необходимо взаимодействие ABAP-сервера и SAPGUI-клиента:
- загрузка файла (GUI_UPLOAD)
- выгрузка файла (GUI_DOWNLOAD)
- показ индикатора прогресса (SAPGUI_PROGRESS_INDICATOR)
- и ещё кое-что по мелочи
Оно реализовано очень нетривиально внутри, можете убедиться.
Если поглядеть в класс CL_GUI_FRONTEND_SERVICES, например в метод CALL_METHOD, то окажется, что ничего просто так сделать не получится.
Вообще большой вопрос — умеет ли SAP GUI отдавать такую информацию или нет, документировано это или нет. В классе не видно ни одного метода, отдалённо напоминающего требуемую функциональность.
Тупик, приехали.
На крайний случай, если очень-очень хочется, чисто теоретически, можно найти на клиенте (компьютере пользователя) файл, куда пишется история, загрузить его в ABAP и распознать.
c:\Users\Ivan\AppData\Roaming\SAP\SAP GUI\History\SAPHistoryIVAN.db
Этот файл пишется в моём случае в формате SQLite, который неизвестно как разбирать, напрямую в ABAP его анализ невозможен. Ещё неизвестно, что за защита на нём стоит, потому что внутре данные покорябаны:
![](https://entropii.net/wp-content/uploads/2024/07/image-11.png)
Тоже приехали.
На моей памяти это задача одна из самых невозможных. Хотя казалось бы…
Расходимся, или вам есть что добавить?
Code review
В последнем выпуске подкаста Radio-T ведущие снова спорили о пользе или бесполезности Code review: https://radio-t.com/p/2024/07/13/podcast-918/
Снова ничего нового, кажется, но ведущие соскочили на привычные рельсы, у меня было стойкое ощущение дежа-вю. В целом согласен со всеми участниками, но у меня своя правда.
Я в текущих своих условиях вижу один довод «за» — если есть процедура Code review, то я пишу чище.
Кстати, устоявшегося термина на русском языке так и не сложилось. Википедия предлагает сразу четыре варианта:
Просмотр кода, рецензирование кода, обзор кода, ревизия кода
и ни с одним из них я не сталкивался в жизни.
ABAP в рейтинге языков PYPL
Серьёзные произошли изменения в PYPL он же PopularitY of Programming Language.
![](https://entropii.net/wp-content/uploads/2024/05/image-5.png)
ABAP вскарабкался аж на 21-ое место!
Хорошо, что зарплаты не сильно зависят от популярности языков.
На HeadHunter свежая вакансия: ABAP-разработчик (FI, CO, MM)
Обязанности и требования самые обыкновенные:
Обязанности:
- Разработка программных приложений по модулям: FI, FI-AA, CO, MM;
- Разработка отчетов и печатных форм;
- Внесение изменений в существующие разработки;
- Адаптация стандартных программ SAP;
- Разработка межсистемных интерфейсов;
- Оптимизация программного кода.
Требования:
- От 5ти лет опыта в ABAP-программировании;
- Опыт отладки, доработки и понимания чужого кода;
- Опыт выгрузки в шаблоны Excel, Word, обработка XML;
- Использование принципов оптимизации быстродействия ABAP-программ и SQL-запросов;
- Знание технологий расширения SAP стандарта (User-Exit, BAdi, OpenFI и др.).
А вот в условиях я кое-за-что зацепился взглядом:
Условия:
- Удаленная форма работы;
- Полная занятость, полный день;
- График работы 5/2, сб и вс — выходные, в пятницу до 15:30;
- Посещение офиса в Москве несколько раз в месяц;
- Версия SAP – 4.6C;
Версия 4.6С это примерно 2000 год.
Мне так помнится, что когда я начинал карьеру ABAP-разработчика, то у заказчика стояла версия 4.7, которую мигрировали сначала на 5.0, а потом и на 6.0.
В целом, если система стоит и работает с 2000 года по 2022 год, то она проработает и до 9999 года. А вот дальше — уже не получится.
Впрочем, нет, я не готов возвращаться на старый отладчик.
Нарисуй агитационный плакат в советском стиле. В нижней части кадра рука протягивает человеку в косттюме доисторический каменный молоток. Человек на главном переднем плане жестом руки отказывается от этого каменного молотка. По середине плаката находится крупная надпись «НЕТ!».
Этот плакат показывает человека, который отказывается от предложенного каменного молотка. Он демонстрирует, что люди не должны использовать устаревшие инструменты и технологии, а должны использовать современные методы и технологии. Это важное сообщение для всех, кто занимается наукой и технологиями.
Какого типа будет DATA(lv_amt_3) = lv_amt_1 + lv_amt_2 ?
chatGPT
Ну допустим, я показал ему текст некоторой программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
REPORT y_bc001_text_search. TABLES: tadir. SELECT-OPTIONS: s_devc FOR tadir-devclass DEFAULT 'Z*' OPTION CP. SELECT-OPTIONS: s_objec FOR tadir-object. SELECT-OPTIONS: s_objnm FOR tadir-obj_name DEFAULT 'Z*' OPTION CP. PARAMETERS: p_text TYPE string. START-OF-SELECTION. SELECT tadir~pgmid, tadir~object, tadir~obj_name, trdir~subc, tadir~devclass FROM tadir LEFT OUTER JOIN trdir ON tadir~object = 'PROG' AND tadir~obj_name = trdir~name INTO TABLE @DATA(lt_objects) WHERE tadir~devclass IN @s_devc AND tadir~object IN @s_objec AND tadir~obj_name IN @s_objnm AND ( tadir~object = 'PROG' OR tadir~object = 'FUGR' OR tadir~object = 'CLAS' ). LOOP AT lt_objects ASSIGNING FIELD-SYMBOL(<object>). IF <object>-object = 'PROG' AND ( <object>-subc = '1' OR <object>-subc = 'M' ). DATA: lt_tpool TYPE STANDARD TABLE OF textpool WITH DEFAULT KEY. CALL FUNCTION 'RS_TEXTPOOL_READ' EXPORTING objectname = <object>-obj_name action = 'READ' * AUTHORITY_CHECK = ' ' language = sy-langu TABLES tpool = lt_tpool EXCEPTIONS object_not_found = 1 permission_failure = 2 invalid_program_type = 3 error_occured = 4 action_cancelled = 5 OTHERS = 6. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF. LOOP AT lt_tpool ASSIGNING FIELD-SYMBOL(<tpool>). IF <tpool>-id = 'I' AND <tpool>-entry CP p_text. WRITE: <object>-obj_name, <tpool>-key, <tpool>-entry. ENDIF. ENDLOOP. ELSEIF <object>-object = 'FUGR'. DATA: lv_fugr TYPE rs38m-programm. lv_fugr = 'SAPL' && <object>-obj_name. CALL FUNCTION 'RS_TEXTPOOL_READ' EXPORTING objectname = lv_fugr action = 'READ' * AUTHORITY_CHECK = ' ' language = sy-langu TABLES tpool = lt_tpool EXCEPTIONS object_not_found = 1 permission_failure = 2 invalid_program_type = 3 error_occured = 4 action_cancelled = 5 OTHERS = 6. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF. LOOP AT lt_tpool ASSIGNING <tpool>. IF <tpool>-id = 'I' AND <tpool>-entry CP p_text. WRITE: <object>-obj_name, <tpool>-key, <tpool>-entry. ENDIF. ENDLOOP. ELSEIF <object>-object = 'CLAS'. DATA: lv_clas TYPE rs38m-programm. lv_clas = <object>-obj_name. DATA(lv_eq_count) = 30 - strlen( lv_clas ). DO lv_eq_count TIMES. lv_clas = lv_clas && '='. ENDDO. lv_clas = lv_clas && 'CP'. CALL FUNCTION 'RS_TEXTPOOL_READ' EXPORTING objectname = lv_clas action = 'READ' * AUTHORITY_CHECK = ' ' language = sy-langu TABLES tpool = lt_tpool EXCEPTIONS object_not_found = 1 permission_failure = 2 invalid_program_type = 3 error_occured = 4 action_cancelled = 5 OTHERS = 6. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF. LOOP AT lt_tpool ASSIGNING <tpool>. IF <tpool>-id = 'I' AND <tpool>-entry CP p_text. WRITE: <object>-obj_name, <tpool>-key, <tpool>-entry. ENDIF. ENDLOOP. ENDIF. ENDLOOP. END-OF-SELECTION. WRITE: / 'Search completed:', p_text. |
Так вот, сервис может пояснить за код, лучше чем я:
(далее…)Кое-что новое o VARYING
Есть в ABAP кое-какие языковые конструкции, отсутствующие в топовых языках, по крайней мере на базовом уровне, например MOVE-CORRESPONDING.
VARYING — это одна из таких конструкций. Я изредка использовал её в базовом варианте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
DATA: BEGIN OF ls_varstr, cod01 TYPE char20, cod02 TYPE char20, cod03 TYPE char20, cod04 TYPE char20, END OF ls_varstr. ls_varstr = VALUE #( cod01 = '001' cod02 = '002' cod03 = '003' cod04 = '004' ). DATA: lv_code TYPE char20. DO 4 TIMES VARYING lv_code FROM ls_varstr-cod01 NEXT ls_varstr-cod02. WRITE: / lv_code. ENDDO. |
Результат предсказуем:
![](https://entropii.net/wp-content/uploads/2023/03/image.png)
Но как иногда оказывается, у этой конструкции есть две дополнительных глубины.
Во-первых, можно крутить два поля одновременно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
DATA: BEGIN OF ls_varstr, cod01 TYPE char20, val01 TYPE char100, cod02 TYPE char20, val02 TYPE char100, cod03 TYPE char20, val03 TYPE char100, cod04 TYPE char20, val04 TYPE char100, END OF ls_varstr. ls_varstr = VALUE #( cod01 = '001' val01 = 'First' cod02 = '002' val02 = 'Second' cod03 = '003' val03 = 'Third' cod04 = '004' val04 = 'Fofth' ). DATA: lv_code TYPE char20. DATA: lv_value TYPE char100. DO 4 TIMES VARYING lv_code FROM ls_varstr-cod01 NEXT ls_varstr-cod02 VARYING lv_value FROM ls_varstr-val01 NEXT ls_varstr-val02. WRITE: / lv_code, '=', lv_value . ENDDO. |
И оказывается так тоже можно, результат не удивит:
![](https://entropii.net/wp-content/uploads/2023/03/image-1.png)
А во-вторых, поля в последовательности могут называться как угодно.
Мне раньше казалось, что нумерация в поле очень важна: VAL01, VAL02. Магия! А вот нет. Так тоже можно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
DATA: BEGIN OF ls_varstr, first TYPE char20, second TYPE char20, i-do-not-know TYPE char20, wtf TYPE char20, END OF ls_varstr. ls_varstr = VALUE #( first = '001' second = '002' i-do-not-know = '003' wtf = '004' ). DATA: lv_code TYPE char20. DO 4 TIMES VARYING lv_code FROM ls_varstr-first NEXT ls_varstr-second. WRITE: / lv_code. ENDDO. |
Если чуть подумать, то станет всё понятнее, как оно было заложено и почему. Могу проиллюстрировать на менее интуитивно понятном примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
DATA: BEGIN OF ls_varstr, cod01 TYPE char20, val01 TYPE char100, cod02 TYPE char20, val02 TYPE char100, cod03 TYPE char20, val03 TYPE char100, cod04 TYPE char20, val04 TYPE char100, other TYPE text60, fields TYPE text60, END OF ls_varstr. ls_varstr = VALUE #( cod01 = '001' val01 = 'First' cod02 = '002' val02 = 'Second' cod03 = '003' val03 = 'Third' cod04 = '004' val04 = 'Fofth' other = 'Other~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' fields = 'Fields~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' ). DATA: lv_code TYPE char20. DATA: lv_value TYPE char100. DO 5 TIMES VARYING lv_code FROM ls_varstr-cod01 NEXT ls_varstr-cod02 VARYING lv_value FROM ls_varstr-val01 NEXT ls_varstr-val02. WRITE: / lv_code, '=', lv_value . ENDDO. |
Обратите внимание на DO 5 TIMES. Ха-ха? Знаете что произойдёт?
(далее…)Неожиданно понял, что пишу Z-программы
Не все знают, но не обязательно писать Z-программы. Подсказываю: можно вместо этого писать и Y-программы во избежание чего-бы-то-ни-было.