Автоматизация работы в SAP с помощью VBScript

в 9:08, , рубрики: ERP-системы, sap, vbscript, Программирование

За несколько лет работы в SAP, как пользователя, я составил большое количество различных скриптов для облегчения работы в SAP, т.к. SAP «из коробки» довольно неудобен для быстрой и эффективной работы. Особенно раздражает то, что за один раз невозможно вставить в таблицу больше строк, чем отображается на экране. Приходится вставлять частями, прокручивая таблицу. На невысоком широкоформатном мониторе так вообще ужасно неудобно. Я как-то давно, еще до составления скриптов, свой монитор ставил вертикально и поворачивал изображение, чтобы отображалось больше строк.

Решил поделиться своими наработками с общественностью.

Добиться разработки нужных для работы вещей в компании практически (и фактически) нереально. Компания крупная, бюрократия, куча согласований, жалко денег на программистов SAP, множество посредников-отделов и т.д. Поэтому я потихоньку самостоятельно автоматизировал свою (и не только свою) работу с помощью VBScript. Запуск «левых» exe на компьютерах компании запрещен всякими политиками, настройками и сторонними программами, а вот VBScript спокойно запускается.

Основной методикой составления скриптов была запись действий в макрос средствами самого SAP с дальнейшим разбором записанного, изучением справки, поиском информации в интернете и т.д. и т.п.

В интернете множество примеров работы с SAP с помощью VBScript, который запускается двойным кликом по vbs-файлу, но они практически все рассчитаны на работу с первым окном SAP. В начале таких примеров обычно есть код session.findById(«wnd[0]»).maximize, разворачивающий окно SAP. Работать с такими скриптами, «заточенными» под первое окно – неудобно по следующим причинам:

  1. Окно SAP Logon должно быть закрыто или свернуто в трей. Иначе скрипт будет пытаться работать в нем.
  2. В первом окне может выполняться другая транзакция, или будут находиться результаты работы транзакции, или будет открыта транзакция для ввода параметров. В общем, если в первом окне будет открыто что-то, кроме меню – запуск транзакции через скрипт не сработает.
  3. В первом окне может быть открыто подключение к другому серверу.

Себе в помощь дополнительно написал скрипт, который выгружает в XML-файл дерево GUI-элементов первого окна SAP. Получается так:

image

Перебор GUI-элементов SAP

Dim currentNode

Set xmlParser = CreateObject("Msxml2.DOMDocument")

' Создание объявления XML
xmlParser.appendChild(xmlParser.createProcessingInstruction("xml", "version='1.0' encoding='windows-1251'"))

Set SapGuiAuto  = GetObject("SAPGUI")
Set application = SapGuiAuto.GetScriptingEngine
Set connection  = application.Children(0)
Set session     = connection.Children(0)

WScript.ConnectObject session,     "on"
WScript.ConnectObject application, "on"

' Максимизируем окно SAP
session.findById("wnd[0]").maximize

enumeration "wnd[0]"
'enumeration "wnd[0]/usr"

MsgBox "Готово!", vbSystemModal Or vbInformation

Sub enumeration(SAPRootElementId)

	Set SAPRootElement = session.findById(SAPRootElementId)
	
	'Создание корневого элемента
	Set XMLRootNode = xmlParser.appendChild(xmlParser.createElement(SAPRootElement.Type))
	
	enumChildrens SAPRootElement, XMLRootNode
	
	xmlParser.save("C:SAP_tree.xml")
End Sub

Sub enumChildrens(SAPRootElement, XMLRootNode) 
	For i = 0 To SAPRootElement.Children.Count - 1
		Set SAPChildElement = SAPRootElement.Children.ElementAt(i)
		
		' Создаем узел
		Set XMLSubNode = XMLRootNode.appendChild(xmlParser.createElement(SAPChildElement.Type))
		
		' Атрибут Name
		Set attrName = xmlParser.createAttribute("Name")
		attrName.Value = SAPChildElement.Name
		XMLSubNode.setAttributeNode(attrName)
		
		' Атрибут Text
		If (Len(SAPChildElement.Text) > 0) Then
			Set attrText = xmlParser.createAttribute("Text")
			attrText.Value = SAPChildElement.Text
			XMLSubNode.setAttributeNode(attrText)
		End If
		
		' Атрибут Id
		Set attrId = xmlParser.createAttribute("Id")
		attrId.Value = SAPChildElement.Id
		XMLSubNode.setAttributeNode(attrId)
		
		' Если текущий объект - контейнер, то перебираем дочерние элементы
		If (SAPChildElement.ContainerType) Then enumChildrens SAPChildElement, XMLSubNode
	Next
End Sub

Комментируя одну из строк:

  • enumeration «wnd[0]»
  • enumeration «wnd[0]/usr»

получаем результаты обхода элементов либо всего окна SAP, либо только UserArea (без меню, тулбара, строки статуса).

Несколько нюансов:

  1. Если какой-то элемент не видим на экране, то он не существует, и не найдется ни по имени, ни по ID. Чтобы до него добраться, нужно развернуть и сделать видимыми все вышестоящие элементы. Например:
    • В транзакции ME51N невозможно указать «Примечание заголовка» если заголовок свернут. Надо предварительно развернуть заголовок.
    • В транзакции ME21N, чтобы добраться до поля «Наш знак» – нужно не только развернуть заголовок, но и переключиться на вкладку «Связь».
  2. При обновлении экрана практически все GUI-объекты создаются заново, и их нужно заново искать по имени или ID. При обращении к объектам, созданным в скрипте до обновления экрана – произойдет ошибка.
  3. Все команды VBScript, взаимодействующие с GUI-элементами SAP, выполняются в синхронном режиме, т.е. выполнение кода VBScript не идет дальше, пока SAP не отработает команду. Это очень удобно, не нужно вставлять везде паузы и/или циклы, проверяя изменения на экране или ожидая появления какого-либо окна с сообщением.
  4. Во время работы скрипта и его взаимодействия с одним окном (режимом) SAP — можно спокойно работать как в других режимах SAP, так и в самой Windows или других приложениях.

Мои скрипты запускаются прямо из SAP. Для этого в SAP в избранном создается объект типа «Web-адрес или файл» с указанием полного пути к файлу скрипта.

image

Для работы скриптов я использую файлы WSF (Windows Script File), которые состоят из двух частей:

  1. Общий файл под громким названием SDK.vbs, содержащий код, используемый в каждом скрипте, плюс несколько функций и процедур, которые используются не во всех скриптах.
  2. Собственно, сам VBScript, который выполняет наши задачи. VBScript выполняет команды именно в том окне SAP, из которого запущен.

Структура файла WSF

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		…
		Код скрипта
		…
	</script>
</job>

Т.е. WSF может состоять из нескольких скриптов (на различных языках программирования), как раскиданных по отдельным файлам, так и прописанных в самом WSF. При запуске WSF сначала выполняется код из файла SDK.vbs, а потом уже код самого скрипта. Т.к. WSF запускается прямо из SAP – то он гарантированно будет выполняться именно в том же окне, из которого запущен. Активная сессия определяется командой Set session = application.ActiveSession().

SDK.vbs состоит из кода подключения к SAP и определения активной сессии, и нескольких вспомогательных процедур и функций.

Далее в скрипте для удобства работы определяются несколько объектов и переменных:

  • Wnd0 и UserArea – используются практически везде,
  • Menubar, Statusbar и UserName – в некоторых скриптах.

Дополнительные процедуры и функции:

  1. Запуск транзакции.
  2. Эмуляция нажатия кнопок Enter, F3, F5, F8.
  3. Диалог открытия csv- или txt-файла с возможностью создания файла для записи. Файл для записи создается в той же папке и имеет такое же имя, но перед расширением добавляется «out».
  4. Заполнение одной строки в таблице (для транзакции ME51N).

SDK.vbs

' Создаем объект WScript.Shell
Set WshShell = WScript.CreateObject("WScript.Shell")

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Подключение к SAP
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' Создаем объект
Set SapGuiAuto = GetObject("SAPGUI")

' Создаем объект типа GuiApplication (COM-интерфейс)
Set application = SapGuiAuto.GetScriptingEngine()

' Создаем объект типа GuiSession - это сессия, которой соответствует активное окно SAP
' Т.е. при запуске WSF сам скрипт будет выполняться в том же окне SAP, из которого запущен
Set session = application.ActiveSession()

WScript.ConnectObject session,     "on"
WScript.ConnectObject application, "on"

' Создаем объект типа GuiMainWindow
Set Wnd0 = session.findById("wnd[0]")

' Создаем объект типа GuiMenubar
Set Menubar = Wnd0.findById("mbar")

' Создаем объект типа GuiUserArea
Set UserArea = Wnd0.findById("usr")

' Создаем объект типа GuiStatusbar
Set Statusbar = Wnd0.findById("sbar")

' Определяем логин пользователя
UserName = session.Info.User

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Вспомогательные процедуры и функции
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' Запуск транзакции
Sub startTransaction(transaction_name)
	Wnd0.findById("tbar[0]/okcd").Text = transaction_name
	pressEnter()
End Sub

' Нажатие кнопки "Enter"
Sub pressEnter()
	Wnd0.sendVKey 0
End Sub

' Нажатие кнопки F2
Sub pressF2()
	Wnd0.sendVKey 2
End Sub

' Нажатие кнопки F3
Sub pressF3()
	Wnd0.sendVKey 3
End Sub

' Нажатие кнопки F5
Sub pressF5()
	Wnd0.sendVKey 5
End Sub

' Нажатие кнопки F8
Sub pressF8()
	Wnd0.sendVKey 8
End Sub

' Диалог выбора файла, создание потоков чтения из файла и записи в файл
Function selectFile(createOuputFile)
	Set objDialog = CreateObject("UserAccounts.CommonDialog")
	' Заполняем свойства и открываем диалог
	With objDialog
		.InitialDir = WshShell.SpecialFolders("Desktop") ' Начальная папка - рабочий стол
		.Filter     = "Текстовые файлы (*.csv;*.txt)|*.csv;*.txt"
		result      = .ShowOpen
	End With
	' Если файл не выбран - выходим
	If (result = 0) Then WScript.Quit
	inputFile = objDialog.FileName ' Полный путь к выбранному файлу
	Set fso = CreateObject("Scripting.FileSystemObject")
	Set inputStream = fso.OpenTextFile(inputFile)
	' Создавать выходной файл?
	If (createOuputFile) Then
		outputFile = Left(inputFile, Len(inputFile) - 3) & "out" & Right(inputFile, 4)
		Set outputStream = fso.CreateTextFile(outputFile, True)
		' Возвращаем массив из потока чтения из файла и потока записи в файл
		selectFile = Array(inputStream, outputStream)
	Else
		' Возвращаем поток чтения из файла
		selectFile = inputStream
	End If
End Function

' Заполняем одну строку в таблице (для ME51N)
Sub fill_row(row, material, kolvo, zavod, zatreboval)
	Set grid = session.findById(UserArea.findByName("GRIDCONTROL", "GuiCustomControl").Id & "/shellcont/shell")
	grid.modifyCell row, "KNTTP", "K"        ' Тип контировки
	grid.modifyCell row, "MATNR", material   ' Материал
	grid.modifyCell row, "MENGE", kolvo      ' Количество
	grid.modifyCell row, "NAME1", zavod      ' Завод
	grid.modifyCell row, "LGOBE", "0001"     ' Склад
	grid.modifyCell row, "AFNAM", zatreboval ' Затребовал
End Sub

Далее приведу примеры скриптов с комментариями и небольшим описанием.

Запуск транзакции IQ09, вставка серийников из буфера обмена, выполнение транзакции.

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		' Запускаем транзакцию
		startTransaction("IQ09")
		
		' Кнопка "Многократный выбор" серийников
		UserArea.findById("btn%_SERNR_%_APP_%-VALU_PUSH").Press()
		' Кнопка "Загрузка из буфера"
		session.findById("wnd[1]/tbar[0]/btn[24]").Press()
		' Кнопка "Скопировать" (F8)
		pressF8()
		' Кнопка "Выполнить" (F8)
		pressF8()
	</script>
</job>

Т.е. непосредственно перед запуском скрипта необходимо скопировать в буфер обмена столбец из серийных номеров, например, из Excel или текстового файла.

Поступление материала за текущий месяц (транзакция MB51).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		' Определяем первый и последний дни текущего месяца
		curr_date  = Date()
		curr_month = Month(curr_date)
		curr_year  = Year(curr_date)
		curr_day   = Day(curr_date)
		date_begin = DateSerial(curr_year, curr_month,     1)
		date_end   = DateSerial(curr_year, curr_month + 1, 0)
		
		' Запускаем транзакцию
		startTransaction("MB51")

		' Заполняем/очищаем поля ввода		
		UserArea.findById("ctxtMATNR-LOW").Text   = "100000" ' Материал
		UserArea.findById("ctxtWERKS-LOW").Text   = "9999"   ' Завод
		UserArea.findById("ctxtLGORT-LOW").Text   = "0001"   ' Склад
		UserArea.findById("ctxtBWART-LOW").Text   = "101"    ' Вид движения
		UserArea.findById("ctxtBUDAT-LOW").Text   = date_begin ' Дата проводки - начало интервала
		UserArea.findById("ctxtBUDAT-HIGH").Text  = date_end   ' Дата проводки - конец интервала
		UserArea.findById("ctxtLIFNR-LOW").Text   = ""
		UserArea.findById("ctxtBUKRS-LOW").Text   = ""
		UserArea.findById("ctxtEBELN-LOW").Text   = ""  ' Заказ на поставку
		UserArea.findById("txtMAT_KDPO-LOW").Text = ""	' Поз. заказа клиента
		UserArea.findById("txtZEILE-LOW").Text    = ""	' Поз. док. материала
		UserArea.findById("txtMBLNR-LOW").Text    = ""	' Документ материала
		UserArea.findById("txtMJAHR-LOW").Text    = ""	' ГодДокумМатериала
		pressF8()
		
		' Кнопка "Подробный список"
		Wnd0.findById("tbar[1]/btn[48]").Press()
		
		' Меню "Параметры настройки -> Вариант просмотра -> Выбрать..."
		Menubar.findById("menu[3]/menu[2]/menu[1]").Select()
		
		' Выбираем форматы "X СпецифДляПользов"
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cmbG51_USPEC_LBOX").Key = "X"
		
		' Клик по 3-му формату сверху
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").currentCellRow = 3
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").clickCurrentCell()
	</script>
</job>

В начале скрипта определяются первый и последний дни текущего месяца. Дополнительно очищаются некоторые поля ввода, которые могут быть заполнены после предыдущего запуска транзакции. Для таблицы используется предварительно созданный формат (специфический для пользователя), который должен быть третьим в списке. Порядковый номер формата легко исправить в коде.

Исходящие поставки за прошлый месяц (транзакция VL06O).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		' Определяем первый и последний дни прошлого месяца
		curr_date = Date()
		curr_month = Month(curr_date)
		curr_year  = Year(curr_date)
		curr_day   = Day(curr_date)
		curr_month = curr_month - 1
		date_begin = DateSerial(curr_year, curr_month,     1)
		date_end   = DateSerial(curr_year, curr_month + 1, 0)
		
		' Запускаем транзакцию
		startTransaction("VL06O")
		
		' Кнопка "Список поставок - исх. п"
		UserArea.findById("btnBUTTON6").Press()
		' Кнопка "Вызвать вариант..."
		Wnd0.findById("tbar[1]/btn[17]").Press()
		' Вводим имя формата
		session.findById("wnd[1]/usr/txtV-LOW").Text = "MY_FORMAT"
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Вводим интервал
		UserArea.findById("ctxtIT_WTIST-LOW").Text  = date_begin
		UserArea.findById("ctxtIT_WTIST-HIGH").Text = date_end
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Кнопка "Выбрать формат..."
		Wnd0.findById("tbar[1]/btn[33]").Press()
		' Выбираем форматы "X СпецифДляПользов"
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cmbG51_USPEC_LBOX").Key = "X"
		' Клик по 3-му формату
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").currentCellRow = 3
		session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").clickCurrentCell()
	</script>
</job>

В начале скрипта определяются первый и последний дни прошлого месяца. Остальные параметры запуска транзакции хранятся в предварительно созданном формате MY_FORMAT_2. Для таблицы так же используется предварительно созданный формат (специфический для пользователя), который должен быть третьим в списке.

Извлечение среднескользящих цен (транзакция MM43).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		zavod = InputBox("Введите код завода")
		If (zavod = "") Then WScript.Quit
		
		' Диалог выбора файла
		streams = selectFile(True)
		
		Set inputStream  = streams(0)
		Set outputStream = streams(1)
		
		' Запускаем транзакцию
		startTransaction("MM43")
		
		' Читаем строки из файла, пока не достигнем конца
		Do While (Not inputStream.AtEndOfLine)
			' Вводим завод
			UserArea.findById("ctxtRMMW1-VZWRK").Text = zavod
			' Считываем материал из файла
			material = inputStream.ReadLine()
			' Вставляем материал
			UserArea.findById("ctxtRMMW1-MATNR").Text = material
			' Кнопка "Enter"
			pressEnter()
			' Вкладка "Логистика: ЦентрРасп"
			UserArea.findById("tabsTABSPR1/tabpSP05").Select()
			' Кнопка "Бухучет"
			UserArea.findById("tabsTABSPR1/tabpSP05/ssubTABFRA1:SAPLMGMW:2004/subSUB9:SAPLMGD2:2480/btnMBEW_PUSH").Press()
			' Извлекаем цену
			price = UserArea.findById("subSUB3:SAPLMGD2:2802/txtMBEW-VERPR").Text
			' Убираем точку в цене
			price = Replace(price, ".", "")
			' Кнопка "Назад" (F3)
			pressF3()
			' Кнопка "Назад" (F3)
			pressF3()
			' Записываем в файл материал и цену
			outputStream.WriteLine(material & vbTab & price)
		Loop
		
		' Кнопка "Назад" (F3)
		pressF3()
		
		inputStream.Close()
		outputStream.Close()
		
		MsgBox "Готово!", vbSystemModal Or vbInformation
	</script>
</job>

Материалы, по которым надо получить среднескользящие цены – находятся в текстовом файле и расположены в столбец. Скрипт запускает транзакцию MM43, потом переключается по вкладкам, нажимает кнопку, чтобы добраться до нужного поля. Полученную цену записывает в новый файл вместе с материалом (через табуляцию).

Список транспортировок с датами окончания погрузки (транзакция VT16).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
	
		' Определяем первый день текущего месяца
		curr_date  = Date()
		curr_month = Month(curr_date)
		curr_year  = Year(curr_date)
		date_begin = DateSerial(curr_year, curr_month, 1)
		
		' Запускаем транзакцию
		startTransaction("VT16")
		
		' Кнопка "Enter"
		pressEnter()
		' Интервал "ТекущКонецПогрз"
		UserArea.findById("ctxtK_DALEN-LOW").Text  = date_begin
		UserArea.findById("ctxtK_DALEN-HIGH").Text = "31.12.9999"
		' Вид транспортировки
		UserArea.findById("ctxtK_SHTYP-LOW").Text = "0001"
		' Заполняем пункты отгрузки
		UserArea.findById("btn%_A_VSTEL_%_APP_%-VALU_PUSH").Press()
		Set table = session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA/ssubSCREEN_HEADER:SAPLALDB:3010/tblSAPLALDBSINGLE")
		table.findById("ctxtRSCSEL_255-SLOW_I[1,0]").Text = "1111"
		table.findById("ctxtRSCSEL_255-SLOW_I[1,1]").Text = "2222"
		table.findById("ctxtRSCSEL_255-SLOW_I[1,2]").Text = "3333"
		session.findById("wnd[1]/tbar[0]/btn[8]").Press()
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Пункт меню "Параметры настройки -> Варианты просмотра -> Выбрать..."
		Menubar.findById("menu[3]/menu[0]/menu[1]").Select()
		' Выбираем первый формат в списке
		session.findById("wnd[1]/usr/lbl[1,3]").setFocus()
		' Кнопка "Enter"
		pressEnter()
	</script>
</job>

Транспортировки выбираются из интервала – с первого дня текущего месяца по 31.12.9999. Дата первого дня текущего месяца определяется в начале скрипта. Указываются пункты отгрузки. Для сформированного отчета выбирается первый формат в списке.

Следующие три скрипта предназначены для проводки отгруженных поставок датой, равной дате окончания погрузки.

Отгруженные, но не проведенные, исходящие поставки (транзакция VL06O).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		' Запускаем транзакцию
		startTransaction("VL06O")
		
		' Кнопка "Список поставок - исх. п"
		UserArea.findById("btnBUTTON6").Press()
		' Кнопка "Вызвать вариант..."
		Wnd0.findById("tbar[1]/btn[17]").Press()
		' Вводим имя формата
		session.findById("wnd[1]/usr/txtV-LOW").Text = "MY_FORMAT_2"
		' Очищаем поле "Создал"
		session.findById("wnd[1]/usr/txtENAME-LOW").Text = ""
		' Кнопка "Выполнить"
		pressF8()
		' Кнопка "Выполнить"
		pressF8()
	</script>
</job>

Для таблицы используется предварительно созданный формат.

Запуск транзакции VT16, вставка номеров поставок из буфера обмена, выполнение транзакции.

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		' Запускаем транзакцию
		startTransaction("VT16")
		
		' Закрываем ненужное окошко
		session.findById("wnd[1]/tbar[0]/btn[0]").Press()
		' Кнопка "Многократный выбор" исходящих поставок
		UserArea.findById("btn%_S_VBELN_%_APP_%-VALU_PUSH").Press()
		' Кнопка "Загрузка из буфера"
		session.findById("wnd[1]/tbar[0]/btn[24]").Press()
		' Кнопка "Скопировать" (F8)
		pressF8()
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Пункт меню "Параметры настройки -> Варианты просмотра -> Выбрать..."
		Menubar.findById("menu[3]/menu[0]/menu[1]").Select()
		' Выбираем первый формат в списке
		session.findById("wnd[1]/usr/lbl[1,3]").setFocus()
		' Кнопка "Enter"
		pressEnter()
	</script>
</job>

Аналогично, столбец из номеров поставок необходимо предварительно скопировать в буфер обмена.

Отгруженные, но не проведенные, исходящие поставки по транспортировкам из буфера обмена (транзакция VL06O).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		' Запуск транзакции
		startTransaction("VL06O")
		
		' Кнопка "Для ОМ"
		UserArea.findById("btnBUTTON4").Press()
		' Удаление начальной и конечной дат "Дата ПлановДвижМтр"
		UserArea.findById("ctxtIT_WADAT-LOW").Text  = ""
		UserArea.findById("ctxtIT_WADAT-HIGH").Text = ""
		' Кнопка "Многократный выбор" транспортировок
		UserArea.findById("btn%_IT_TKNUM_%_APP_%-VALU_PUSH").Press()
		' Кнопка "Загрузка из буфера"
		session.findById("wnd[1]/tbar[0]/btn[24]").Press()
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Кнопка "Выполнить" (F8)
		pressF8()
		
		' Если нет кнопки "Ракурс позиции" - то и поставок нет
		If (Wnd0.findById("tbar[1]/btn[18]").Text = "Ракурс позиции") Then
			' Выделяем все
			UserArea.findById("cntlGRID1/shellcont/shell").selectAll
			' Меню "Последующие функции -> Выполнить проводку отпуска материала"
			Menubar.findById("menu[4]/menu[7]").Select()
			' В появившемся окне стираем два первых символа (день месяца) в поле ввода даты фактического движения материала
			WshShell.SendKeys "{HOME}{DELETE}{DELETE}"
		Else
			MsgBox "Поставок нет!", vbSystemModal Or vbExclamation
			' Кнопка "Назад" (F3)
			pressF3()
			' Кнопка "Назад" (F3)
			pressF3()
		End If
	</script>
</job>

Столбец с номерами транспортировок необходимо предварительно скопировать в буфер обмена из результатов работы транзакции VT16 (предыдущий скрипт). После окончания работы транзакции:

  1. Если поставок в отчете нет – выводится соответствующее сообщение, далее скрипт завершает работу.
  2. Если поставки в отчете есть – выбирается пункт меню «Выполнить проводку отпуска материала», в появившемся окошке с датой проводки стираются два первых символа (т.е. день). Далее скрипт завершает работу, а пользователю необходимо ввести нужный день и нажать Enter.

Наличие/отсутствие поставок в отчете определяется по наличию/отсутствию меню. Предварительно включается обработка ошибок скриптом.

Пока писал статью, решил объединить первые два скрипта для проводки отгруженных поставок в один.

Список транспортировок на отгруженные, но не проведенные, исходящие поставки.

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
	
		'##############################################################
		' Запускаем первую транзакцию
		startTransaction("VL06O")
		
		' Кнопка "Список поставок - исх. п"
		UserArea.findById("btnBUTTON6").Press()
		' Кнопка "Вызвать вариант..."
		Wnd0.findById("tbar[1]/btn[17]").Press()
		' Вводим имя формата
		session.findById("wnd[1]/usr/txtV-LOW").Text = "MY_FORMAT_3"
		' Очищаем поле "Создал"
		session.findById("wnd[1]/usr/txtENAME-LOW").Text = ""
		' Кнопка "Выполнить"
		pressF8()
		' Кнопка "Выполнить"
		pressF8()
		
		Set grid = UserArea.findById("cntlGRID1/shellcont/shell") ' GuiGridView
		rowCount = grid.RowCount ' Всего заполненных строк
		visibleRowCount = grid.VisibleRowCount ' Количество видимых строк
		
		' Прокручиваем таблицу до конца, чтобы скопировались все поставки
		firstVisibleRow = visibleRowCount
		Do While (firstVisibleRow < rowCount)
			grid.firstVisibleRow = firstVisibleRow
			firstVisibleRow = firstVisibleRow + visibleRowCount
		Loop
		
		' Выделяем столбец с поставками
		grid.SelectColumn("VBELN")
		
		' Копируем в буфер выделенный столбец с помощью контекстного меню
		grid.ContextMenu()
		grid.SelectContextMenuItemByText "Скопировать текст"
		
		' Выходим в меню SAP
		pressF3()
		pressF3()
		pressF3()
		
		'##############################################################
		' Запускаем вторую транзакцию
		startTransaction("VT16")
		
		' Закрываем ненужное окошко
		session.findById("wnd[1]/tbar[0]/btn[0]").Press()
		' Кнопка "Многократный выбор" исходящих поставок
		UserArea.findById("btn%_S_VBELN_%_APP_%-VALU_PUSH").Press()
		' Кнопка "Загрузка из буфера"
		session.findById("wnd[1]/tbar[0]/btn[24]").Press()
		' Кнопка "Скопировать" (F8)
		pressF8()
		' Кнопка "Выполнить" (F8)
		pressF8()
		' Пункт меню "Параметры настройки -> Варианты просмотра -> Выбрать..."
		Menubar.findById("menu[3]/menu[0]/menu[1]").Select()
		' Выбираем первый формат в списке (должен быть "Lexa_1")
		session.findById("wnd[1]/usr/lbl[1,3]").setFocus()
		' Кнопка "Enter"
		pressEnter()
		
	</script>
</job>

В скрипте используется копирование столбца с номерами поставок из транзакции VL06O с предварительной прокруткой таблицы до конца (чтобы скопировались все поставки).

Массовое прописывание даты и времени «ПланировТранс» во входящих поставках (транзакция VL32N).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		data_prihoda = InputBox("Введите дату в формате: 010213, 01022013, 01.02.13, 01.02.2013")
		If (data_prihoda = "") Then WScript.Quit
		
		vremya_prihoda = InputBox("Введите время в формате: 0130, 01:30")
		If (vremya_prihoda = "") Then WScript.Quit

		' Диалог выбора файла
		Set inputStream = selectFile(False)
		
		' Запускаем транзакцию
		startTransaction("VL32N")
		
		' Перебираем строки, пока не встретится пустая
		Do While (Not inputStream.AtEndOfLine)
			' Читаем строку из файла
			postavka = inputStream.ReadLine()
			' Вставляем номер поставки
			UserArea.findById("ctxtLIKP-VBELN").Text = postavka
			' Нажимаем "Enter"
			pressEnter()
			' Переключаемся на вкладку "Транспортировка"
			UserArea.findById("tabsTAXI_TABSTRIP_OVERVIEW/tabpT2").Select()
			' Вставляем дату
			UserArea.findById("tabsTAXI_TABSTRIP_OVERVIEW/tabpT2/ssubSUBSCREEN_BODY:SAPMV50A:1208/ctxtLIKP-TDDAT").Text = data_prihoda
			' Вставляем время
			UserArea.findById("tabsTAXI_TABSTRIP_OVERVIEW/tabpT2/ssubSUBSCREEN_BODY:SAPMV50A:1208/ctxtLIKP-TDUHR").Text = vremya_prihoda
			' Нажимаем "Enter" один раз или несколько, если проблемы с датой
			Do
				' Нажимаем "Enter"
				pressEnter()
			Loop While (Len(Statusbar.text) > 0)
			' Нажимаем "Сохранить"
			Wnd0.findById("tbar[0]/btn[11]").Press()
		Loop
		
		' Нажимаем "Назад"
		pressF3()
		
		inputStream.Close()
		
		MsgBox "Готово!", vbSystemModal Or vbInformation
	</script>
</job>

Номера входящих поставок должны быть в текстовом файле в один столбец. Скрипт запрашивает дату и время, после этого перебирает все поставки, прописывает «ПланировТранс», сохраняет поставку. Если в строке статуса появляется какое-либо сообщение – скрипт нажимает Enter до тех пор, пока строка статуса не станет пустой.

Перенос несерийного товара между складами (транзакция MB1B).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		' Завод
		zavod = InputBox("Введите код завода")
		If (zavod = "") Then WScript.Quit
		
		' Склад-отправитель
		sklad_source = InputBox("Введите код склада-отправителя")
		If (sklad_source = "") Then WScript.Quit
		
		' Склад-получатель
		sklad_destination = InputBox("Введите код склада-получателя")
		If (sklad_destination = "") Then WScript.Quit
		
		' Диалог выбора файла
		Set inputStream = selectFile(False)
		
		' Запускаем транзакцию
		startTransaction("MB1B")
		
		' Вводим вид движения
		UserArea.findById("ctxtRM07M-BWARTWA").Text = "311"
		' Вводим завод
		UserArea.findById("ctxtRM07M-WERKS").Text = zavod
		' Вводим склад-отправитель
		UserArea.findById("ctxtRM07M-LGORT").Text = sklad_source
		' Кнопка "Enter"
		pressEnter()
		' Вводим склад-получатель
		UserArea.findById("ctxtMSEGK-UMLGO").Text = sklad_destination
		' Кнопка "Enter"
		pressEnter()
		
		' Определяем количество строк в таблице
		Set simpleContainer = UserArea.findById("sub:SAPMM07M:0421")
		RowCount = simpleContainer.LoopRowCount
		
		intRow = 2
		
		' Читаем строки из файла, пока не достигнем конца
		Do While (Not inputStream.AtEndOfLine)
			' Читаем строку из файла
			stroka = inputStream.ReadLine()
			' Разбиваем строку по символам табуляции
			arr      = Split(stroka, vbTab)
			material = arr(0)
			kolvo    = Replace(arr(1), " ", "")	' Убираем пробелы в количестве
			SAP_pos  = (intRow - 2) Mod RowCount
			UserArea.findById("sub:SAPMM07M:0421/ctxtMSEG-MATNR[" & SAP_pos & ",7]").Text  = material
			UserArea.findById("sub:SAPMM07M:0421/txtMSEG-ERFMG["  & SAP_pos & ",26]").Text = kolvo
			' Нажимаем кнопку "Новый", если таблица закончилась
			If (SAP_pos = (RowCount - 1)) Then Wnd0.findById("tbar[1]/btn[19]").Press()
			intRow = intRow + 1
			If (intRow > 1000) Then
				inputStream.Close()
				MsgBox "Заполнено 999 позиций!", vbSystemModal Or vbCritical
				WScript.Quit
			End If
		Loop
		
		' Кнопка "Enter"
		pressEnter()
		
		inputStream.Close()
		
		MsgBox "Готово!", vbSystemModal Or vbInformation
	</script>
</job>

Скрипт в начале работы запрашивает завод и склады, далее читает из текстового файла материал и количество (разделенные табуляцией), вводит их в таблицу, автоматически прокручивая ее. За один запуск скрипта можно перенести не более 1000 позиций.

Перенос серийного товара между складами (транзакция MB1B).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		' Завод
		zavod = InputBox("Введите код завода")
		If (zavod = "") Then WScript.Quit
		
		' Склад-отправитель
		sklad_source = InputBox("Введите код склада-отправителя")
		If (sklad_source = "") Then WScript.Quit
		
		' Склад-получатель
		sklad_destination = InputBox("Введите код склада-получателя")
		If (sklad_destination = "") Then WScript.Quit
		
		' Диалог выбора файла
		Set inputStream = selectFile(False)
		
		' Запускаем транзакцию
		startTransaction("MB1B")
		
		' Вводим вид движения
		UserArea.findById("ctxtRM07M-BWARTWA").Text = "311"
		' Вводим завод
		UserArea.findById("ctxtRM07M-WERKS").Text = zavod
		' Вводим склад-отправитель
		UserArea.findById("ctxtRM07M-LGORT").Text = sklad_source
		' Кнопка "Enter"
		pressEnter()
		' Вводим склад-получатель
		UserArea.findById("ctxtMSEGK-UMLGO").Text = sklad_destination
		' Кнопка "Enter"
		pressEnter()
		
		' Определяем количество строк в таблице
		Set simpleContainer = UserArea.findById("sub:SAPMM07M:0421")
		RowCount = simpleContainer.LoopRowCount
		
		intRow = 2
		
		' Читаем строки из файла, пока не достигнем конца
		Do While (Not inputStream.AtEndOfLine)
			' Читаем строку из файла
			stroka = inputStream.ReadLine()
			' Разбиваем строку по символам табуляции
			arr      = Split(stroka, vbTab)
			material = arr(0)
			serial   = arr(1)
			SAP_pos  = (intRow - 2) Mod RowCount
			UserArea.findById("sub:SAPMM07M:0421/ctxtMSEG-MATNR[" & SAP_pos & ",7]").Text  = material
			UserArea.findById("sub:SAPMM07M:0421/txtMSEG-ERFMG["  & SAP_pos & ",26]").Text = "1"
			' Кнопка "Enter"
			pressEnter()
			' Вставляем серийник
			session.findById("wnd[1]/usr/tblSAPLIPW1TC_SERIAL_NUMBERS/ctxtRIPW0-SERNR[0,0]").Text = serial
			' Кнопка "ОК"
			session.findById("wnd[1]/tbar[0]/btn[0]").Press()
			' Нажимаем кнопку "Новый", если таблица закончилась
			If (SAP_pos = (RowCount - 1)) Then Wnd0.findById("tbar[1]/btn[19]").Press()
			intRow = intRow + 1
			If (intRow > 1000) Then
				inputStream.Close()
				MsgBox "Заполнено 999 позиций!", vbSystemModal Or vbCritical
				WScript.Quit
			End If
		Loop
		
		' Нажимаем "Enter"
		pressEnter()
		
		inputStream.Close()
		
		MsgBox "Готово!", vbSystemModal Or vbInformation
	</script>
</job>

Скрипт работает аналогично предыдущему скрипту, только в файле вместо количества должен быть серийник, а в табличной части скрипт сам ставит по каждому материалу количество 1.

Создание заявки (транзакция ME51N).

<job>
	<script language="VBScript" src="SDK.vbs"></script>
	<script language="VBScript">
		
		zavod      = 8888
		zatreboval = 12345
		mvz        = "7777666666"
		note       = "Примечание к заявке"
		
		' Список товаров. Если какой-то товар не нужен, нужно проставить 0 в количестве
		itemsArray = Array( _
			Array(111111, 50,	"Бумага А4"), _
			Array(222222, 50,	"Бумага туалетная"), _
			Array(333333, 0,	"Жидкое мыло"), _
			Array(444444, 0,	"Краска штемпельная"), _
			Array(555555, 10,	"Маркер"), _
			Array(666666, 0,	"Скобы для степлера"), _
		)
		
		' Запускаем транзакцию
		startTransaction("ME51N")
		
		' Выбираем нужный вид заявки - NB
		UserArea.findByName("MEREQ_TOPLINE-BSART", "GuiComboBox").Key = "NB"
		
		' Разворачиваем верхний контейнер, если он развернут
		Set buttonTop = UserArea.findByName("SUB1:SAPLMEVIEWS:4000", "GuiSimpleContainer").findByName("DYN_4000-BUTTON", "GuiButton")
		If (Right(buttonTop.Tooltip, 2) = "F2") Then buttonTop.Press()
		
		' Заполняем примечание заголовка
		guiTexteditId = UserArea.findByName("TEXT_EDITOR_0101", "GuiCustomControl").Id & "/shellcont/shell"
		UserArea.findById(guiTexteditId).Text = note
		
		pos = 0
		For Each item In itemsArray
			If (item(1) > 0) Then
				' Заполняем одну строку в таблице
				fill_row pos, item(0), item(1), zavod, zatreboval
				' Кнопка "Enter"
				pressEnter()
				' Прописываем МВЗ
				UserArea.findByName("COBL-KOSTL", "GuiCTextField").Text = mvz
				' Кнопка "Enter"
				pressEnter()
				pos = pos + 1
			End If
		Next
		
		MsgBox "Готово!", vbSystemModal Or vbInformation
		
	</script>
</job>

Предварительно в скрипте проставляются количества для материалов, которые должны быть в заявке. Если указано 0, то такой материал в заявку не добавляется. Для каждого материала в заявке автоматически прописывается МВЗ. Скрипт не рассчитан на работу с большим количеством материалов (они не влезут в видимую часть таблицы).

Есть еще другие и более сложные скрипты, где используется три транзакции в одном скрипте, но там транзакции специфические для компании.

На этом пока все...

Автор: Алексей

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js