martedì 8 novembre 2016

Scarico magazzino SAP da file Excel

Tramite un foglio Excel è possibile collegarsi ad un sistema SAP e movimentare un magazzino chiamando la BAPI_GOODSMVT_CREATE.

Con questa funzione è possibile infatti registrare dei movimenti magazzino MM in pochi passaggi; ho già usato questa BAPI per costruire dei programmi ABAP da usare con terminali in RF o per dei module-pool semplificati di trasferimento tra magazzini interni.

In questo caso era richiesto di poter eseguire degli scarichi su centro di costo in un magazzino secondario usato da operatori saltuari che, normalmente, non conoscono il gestionale SAP.
Ho pensato quindi di costruire un foglio Excel dove l'operatore inserisce codice materiale, quantità e un riferimento (il suo nome o un altro testo da riportare poi come nota di testo nel movimento registrato in SAP) e poi premere un pulsante per scaricare il materiale.

Questa la pagina principale


Questa la pagina con i dati di login al sistema e il percorso di un file di log delle operazioni eseguite (consiglio un percorso di rete condivisa).



Il pulsante è collegato alla seguente Macro:

Sub main()

Dim R3 As Object
Set R3 = CreateObject("SAP.Functions")

zlogin R3
LanciaScarico R3

R3.Connection.Logoff

End Sub

Questo il codice delle 2 funzioni richiamate:

Sub zlogin(R3 As Object)

R3.Connection.ApplicationServer = Sheets("Logon").Cells(1, 2).Value
R3.Connection.SystemNumber = Sheets("Logon").Cells(2, 2).Value
R3.Connection.User = Sheets("Logon").Cells(3, 2).Value
R3.Connection.Password = Sheets("Logon").Cells(4, 2).Value
R3.Connection.client = Sheets("Logon").Cells(5, 2).Value
R3.Connection.Language = Sheets("Logon").Cells(6, 2).Value

'Login al sistema SAP
If R3.Connection.Logon(0, True) <> True Then
  MsgBox "Cannot Log on to SAP" 'Invia messaggio in caso di errore
  Exit Sub
Else
  'MsgBox "Accesso a SAP effettuato con successo!"
End If

End Sub

-------------------------------------------------

Sub LanciaScarico(R3 As Object)

Dim oggi, ora, data_foglio, data_mov, data_log, riga, materiale, riferimento As String
Dim i As Integer

oggi = Date
ora = time

' Controllo inserimenti
materiale = Sheets("Scarico").Cells(2, 2).Value
riferimento = Sheets("Scarico").Cells(4, 2).Value

If materiale = "" Then
   MsgBox "Scegliere Materiale!!!"
   Exit Sub
End If

If Sheets("Scarico").Cells(3, 2).Value = "" Then
   MsgBox "Inserire Quantità!!!"
   Exit Sub
End If

If riferimento = "" Then
   MsgBox "Inserire Riferimento!!!"
   Exit Sub
End If

' Gestione LOG
Dim filePath, nome_file As String

' Un file di LOG al mese
data_log = Mid(oggi, 7, 4) & "-" & Mid(oggi, 4, 2)

' tolgo l'estensione del file
nome_file = ActiveWorkbook.Name

i = Len(nome_file)

' cerco il punto partendo da fine nome file
For ind = i To 1 Step -1
   If Mid(nome_file, ind, 1) = "." Then
     nome_file = Mid(nome_file, 1, (ind - 1))
     Exit For
   End If
Next

filePath = Sheets("Logon").Cells(8, 2).Value & nome_file & "-" & data_log & ".txt"

Open filePath For Append As #1

riga = "--------INIZIO------" & oggi & "-" & ora: Print #1, riga

' Definizione funzione
Set myFuncBGC = R3.Add("BAPI_GOODSMVT_CREATE")

Dim goodsmvt_header, goodsmvt_code, goodsmvt_headret, goodsmvt_item, MATERIALDOCUMENT, preturn As Object

' Imposto parametri in ingresso della Funzione
Set goodsmvt_header = myFuncBGC.exports("goodsmvt_header")
Set goodsmvt_code = myFuncBGC.exports("goodsmvt_code")
Set goodsmvt_item = myFuncBGC.Tables("goodsmvt_item")
    
goodsmvt_header.Value("HEADER_TXT") = Sheets("Scarico").Cells(4, 2).Value
    
' gestione data movimento
data_foglio = Sheets("Scarico").Cells(5, 2).Value
    
If data_foglio = "" Then
   data_foglio = oggi
End If
        
data_mov = Mid(data_foglio, 7, 4) & Mid(data_foglio, 4, 2) & Mid(data_foglio, 1, 2)
    
goodsmvt_header.Value("PSTNG_DATE") = data_mov
goodsmvt_header.Value("DOC_DATE") = data_mov
    
' gestione dati del movimento
goodsmvt_item.Rows.Add
    
goodsmvt_item.Value(1, "material") = materiale
goodsmvt_item.Value(1, "plant") = Sheets("Scarico").Cells(7, 2).Value
goodsmvt_item.Value(1, "stge_loc") = Sheets("Scarico").Cells(8, 2).Value
goodsmvt_item.Value(1, "entry_qnt") = Sheets("Scarico").Cells(3, 2).Value
goodsmvt_item.Value(1, "move_type") = "201"
goodsmvt_item.Value(1, "COSTCENTER") = Sheets("Scarico").Cells(6, 2).Value
    
goodsmvt_code.Value("GM_CODE") = "03"
    
' scrivo tutti i dati nel LOG
riga = "HEADER_TXT = " & goodsmvt_header.Value("HEADER_TXT"): Print #1, riga
riga = "PSTNG_DATE = " & goodsmvt_header.Value("PSTNG_DATE"): Print #1, riga
riga = "HDOC_DATE  = " & goodsmvt_header.Value("DOC_DATE"): Print #1, riga
riga = "GM_CODE    = " & goodsmvt_code.Value("GM_CODE"): Print #1, riga
riga = "MATERIAL   = " & goodsmvt_item.Value(1, "material"): Print #1, riga
riga = "PLANT      = " & goodsmvt_item.Value(1, "plant"): Print #1, riga
riga = "STGE_LOC   = " & goodsmvt_item.Value(1, "stge_loc"): Print #1, riga
riga = "ENTRY_QNT  = " & goodsmvt_item.Value(1, "entry_qnt"): Print #1, riga
riga = "MOVE_TYPE  = " & goodsmvt_item.Value(1, "move_type"): Print #1, riga
riga = "COSTCENTER = " & goodsmvt_item.Value(1, "COSTCENTER"): Print #1, riga

' Chiamata alla funzione SAP
If myFuncBGC.call = False Then
   MsgBox myFuncBGC.Exception
End If
    
' Ricezione dati di ritorno dalla Funzione
Set preturn = myFuncBGC.Tables("RETURN")
Set MATERIALDOCUMENT = myFuncBGC.imports("MATERIALDOCUMENT")
    
' Leggo eventuali messaggi di ritorno e li mostro all'utente
Dim msx As String
    
num_Row = preturn.RowCount
    
For iRow = 1 To num_Row
   msx = preturn.Value(iRow, 4)
   MsgBox msx
   riga = "Messaggi =" & msx:   Print #1, riga
Next
    
If MATERIALDOCUMENT <> "" Then
    
' Chiamo la funzione per il commit BAPI
   Set myFuncCOMMIT = R3.Add("BAPI_TRANSACTION_COMMIT")
   If myFuncCOMMIT.call = False Then
      MsgBox myFuncBGC.Exception
   End If
       
   msx = "Scarico eseguito con numero documento " & MATERIALDOCUMENT
   MsgBox msx

   riga = "Messaggi =" & msx:   Print #1, riga
       
End If
    
Set myFuncBGC = Nothing
      
riga = "--------FINE--------" & oggi & "-" & ora: Print #1, riga

Close #1

End Sub

Ho previsto una macro da eseguire all'avvio per pulire i dati eventualmente presenti:

Sub Auto_Open()
'
' Auto_Open Macro
' Macro all'avvio
'
' Pulisco celle di input
Sheets("Scarico").Cells(2, 2).Value = ""
Sheets("Scarico").Cells(3, 2).Value = ""
Sheets("Scarico").Cells(4, 2).Value = ""
Sheets("Scarico").Cells(5, 2).Value = ""

End Sub

Se il movimento viene eseguito con successo l'operatore riceve il messaggio:


Il testo di riferimento viene inserito nella testata del movimento:


In caso di errori l'operatore riceve il messaggio di errore di ritorno dalla chiamata:


Potete scaricare il foglio excel di esempio: Scarico_CDC.xls

Note:
  • Il centro di costo deve essere inserito con tutti gli zeri non significativi. Nel mio caso era un valore da mantenere fisso pertanto CDC, divisione e magazzino sono già compilati ed ho protetto tutto il foglio in  modo che l'operatore possa inserire solo Materiale, quantità, riferimento ed eventualmente la data di registrazione.
  • Se la data non viene inserita si prende automaticamente la data del giorno.
  • Dopo aver effettuato il movimento, se andato a buon fine, la macro chiama anche la BAPI_TRANSACTION_COMMIT in modo da effettuare il COMMIT della transazione, altrimenti si perde la registrazione.
  • Non sono molto pratico di VB e di chiamate a funzioni con parametri, strutture e array, pertanto ho fatto un po' fatica a capire come passare correttamente i dati alla funzione e come ricevere correttamente i valori di ritorno (tra l'altro senza la possibilità di vedere realmente cosa arrivava lato SAP). Ero quasi sul punto di costruire una funzione Z con pochi e più semplici parametri e, da questa, di chiamare la BAPI_GOODSMVT_CREATE. Invece ho costruito una funzione di Test che usava gli stessi parametri di ingresso e uscita con la scrittura di un LOG così che potevo tracciare, lato SAP, i dati ricevuti; quindi con un po' di tentativi sono riuscito a capire come trasferire correttamente i dati che mi interessavano.
  • Nel foglio Excel finale ho costruito qualcosa di un po' più sofisticato: nel caso in questione si trattava di una lista di materiali relativamente piccola, pertanto li ho inclusi nel foglio Excel e inserito una activex combobox dove l'operatore sceglie il materiale da menù tendina, quando scelto il materiale scatta la chiamata ad una funzione SAP per leggere la giacenza magazzino e riporta il dato in una cella del foglio a fianco del materiale. Se siete interessati a questa versione contattatemi via mail o con un commento.
  • Il parametro GM_CODE pilota il tipo di movimento da eseguire
GM_CODETCODEDescription
01MB01Goods receipt for purchase order
02MB31Goods receipt for production order
03MB1AGoods issue
04MB1BTransfer posting
05MB1COther goods receipt
06MB11Reversal of goods movements
07MB04Subsequent adjustment with regard to a subcontract order

per altri tipi movimento potrebbe essere richiesto di passare altri parametri alla funzione.
  • Esempio di quanto scritto nel LOG
--------INIZIO------08/11/2016-09:58:09
HEADER_TXT = Prova scarico da Excel
PSTNG_DATE = 08/11/2016
HDOC_DATE  = 08/11/2016
GM_CODE    = 03
MATERIAL   = materiale
PLANT      = divisione
STGE_LOC   = magazzino
ENTRY_QNT  = 1,000
MOVE_TYPE  = 201
COSTCENTER = CDC
Messaggi =Scarico eseguito con numero documento 4900600976
--------FINE--------08/11/2016-09:58:09

Alcune annotazioni generali:
  • Di norma includo le informazioni di Logon nel codice VBA e poi lo proteggo con una password poichè altrimenti sono in chiaro. Anche così non è che sia una protezione molto solida, ma mi sembra il minimo.
  • nel PC deve essere installata la SAPGUI (che contiene le DLL per poter attivare il collegamento).
  • se nel server SAP è attivo il firewall deve essere aperta in ingresso la porta tcp 33nn (nn=<system number>), normalmente per le connessioni sapgui è aperta la 32nn.
  • consiglio di definire un utente di tipo S (servizio) che, in pratica, non può effettuare il logon da GUI.

Nessun commento:

Posta un commento