Ho pensato di usare questa funzione da un foglio Excel per appunto, da un ordine di produzione, trovare i suoi dati di testata, le operazioni di lavoro e i componenti usati.
Queste le versioni su cui ho lavorato:
- Windows 10 pro
- Excel 2016
- SAPGui 750 comp. 2
- SAP R/3 EHP8
Ho incontrato le solite difficoltà con il passaggio dei parametri e la lettura dei dati di ritorno, inoltre ho trovato un paio di problemi che non ho risolto e che penso possano essere degli errori del FM chiamato da remoto.
Intanto faccio notare che un FM si può chiamare da remoto se è impostato questo parametro:
Per la BAPI in oggetto questi sono i parametri di importazione:
Si deve passare il numero ordine, poi c'è un parametro facoltativo, quindi una struttura dove selezionare i dati che si vogliono leggere:
In esportazione troviamo la classica struttura RETURN dei messaggi delle BAPI:
Quindi le tabelle di ritorno con i dati letti:
Questo è come ho pensato di impostare il file Excel.
All'avvio vengono puliti eventuali dati precedenti e si presenta così:
L'utente deve inserire il numero ordine da leggere e premere il pulsante "Leggi Ordine":
La macro collegata al pulsante esegue l'accesso al sistema R/3, chiama la BAPI_PRODORD_GET_DETAIL e, con i dati di ritorno, va a compilare il foglio con i dati di testata, le operazione e sotto i componenti.
Questo dopo la lettura:
(ho anonimizzato materiali, centri di lavoro e operazioni che vengono visualizzati con caratteri in corsivo)
Da notare che i componenti vengono scritti sotto le operazioni in modo dinamico, lasciando una riga vuota, creando la riga di intestazione con copia di formattazione dalla intestazione delle operazioni.
Questi i punti a cui ho dovuto prestare attenzione:
Il numero ordine deve essere passato con gli zeri iniziali (12 caratteri)
Ho utilizzato questo comando (trovato sul web):
String$(12 - Len(Sheets("ODP").Cells(2, 2).Value), "0") & Sheets("ODP").Cells(2, 2).Value
Le quantità, se copiate direttamente nelle celle, erano riportate moltiplicate per 1.000.
All'inizio avevo risolto semplicemente moltiplicando per 1 e veniva corretto, poi ho usato una funzione di conversione standard:
h_qta = CDbl(HEADER(1, 18))
che converte una stringa in un Double. Attenzione a non usare la CLng che converte in un Long ma senza decimali.
Unità di Misura.
La funzione ritorna 2 unità di misura:
- Interna SAP: è una codifica standard usata internamente da SAP, per esempio "ST" da pezzo in tedesco "Stück".
- ISO: codifica internazionale, per esempio per pezzo "PCE".
mi sono appoggiato a quella interna SAP ed ho costruito una funzione dove effettuare le conversioni:
'------------------------------------------------
' Conversione UM
'------------------------------------------------
Function conv_um(ByVal um_inp As String) As String
If um_inp = "ST" Then
um_inp = "PZ"
End If
conv_um = um_inp
End Function
Eventualmente da estendere con la conversione di altre sigle.
Adesso i problemi incontrati:
1) La struttura RETURN risulta sempre vuota.
Ho fatto diversi tentativi, questo il comando che dovrebbe popolare la struttura:
Set PRETURN = myFuncBPGD.imports("RETURN")
Come indicato in vari forum deve essere usato prima della chiamata alla funzione, ma ho provato sia prima, che dopo, che entrambe, ma la struttura risulta sempre vuota.
Chiaramente, se si chiama la funzione da dentro SAP, la struttura restituisce correttamente i messaggi di ritorno, per esempio se un ordine non esiste:
A questo problema ho trovato soluzione controllando se la struttura HEADER di ritorno aveva almeno un record, allora la chiamata era corretta altrimenti invio un generico messaggio di errore:
num_Row_head = HEADER.RowCount
If num_Row_head = 0 Then
MsgBox "Errore lettua ODP!!!"
GoTo FINE
End If
2) Quantità superiori a 99.999 non vengono rilevate.
Questo purtroppo è un errore grave a cui non ho trovato soluzione.
Tutte le quantità, sia nella testata che nelle operazioni o componenti, presentano questo problema.
Ho provato a creare un ordine con quantità grandi sia in testata che nei componenti, da dentro R/3 i dati ritornano correttamente, per esempio sui componenti:
mentre da chiamata Excel ritorna:
Non mi sembra un problema di conversione poichè, anche in debug sulla struttura di ritorno dei dati, il campo 12 che contiene la quantità è a 0:
Qui potete scaricare il foglio excel di esempio: Lettura_ODP.xls
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.
Riporto le macro contenute nel foglio.
Macro collegata al pulsante:
Public ok_login As Integer
Sub main()
Dim R3 As Object
' Pulisco dati
Range("C2:I2").Select
Selection.ClearContents
Rows("6:200").Select
Selection.Delete Shift:=xlUp
Range("A6").Select
Set R3 = CreateObject("SAP.Functions")
zlogin R3
If ok_login = 0 Then
LeggiODP R3
R3.Connection.Logoff
End If
End Sub
Macro Login a R/3:
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
'R3.Connection.ApplicationServer = ""
'R3.Connection.SystemNumber = nn
'R3.Connection.User = ""
'R3.Connection.Password = ""
'R3.Connection.client = nnn
'R3.Connection.Language = ""
' Logon a SAP e controllo se la connessione è stabilita
If R3.Connection.Logon(0, True) <> True Then
MsgBox "Impossibile collegarsi a SAP!" 'Messaggio di errore
ok_login = 1
Exit Sub
Else
'MsgBox "OK: Collegato a SAP!"
ok_login = 0
End If
End Sub
Nota: se volete riportare direttamente nel codice i parametri di accesso commentate le righe sopra, che vanno a prendere i dati dal foglio Logon, e riportateli nel codice commentato sotto.
Macro lettura ODP:
Sub LeggiODP(R3 As Object)
' Controllo inserimenti
odp = Sheets("ODP").Cells(2, 2).Value
If odp = "" Then
MsgBox "Scegliere ODP!!!"
Exit Sub
End If
' Definizione funzione
Set myFuncBPGD = R3.Add("BAPI_PRODORD_GET_DETAIL")
Dim NUMBER, ORDER_OBJECTS As Object
Dim PRETURN As Object
Dim HEADER, POSITION, SEQUENCE, OPERATION, TRIGGER_POINT, COMPONENT, PROD_REL_TOOL, FSH_BUNDLES, FSH_PRODORD_SEASON As Object
' Imposto parametri in ingresso della Funzione
' Parametri in ingresso
Set NUMBER = myFuncBPGD.exports("NUMBER")
Set ORDER_OBJECTS = myFuncBPGD.exports("ORDER_OBJECTS")
' Esito chiamata alla BAPI
Set PRETURN = myFuncBPGD.imports("RETURN")
' Valorizzo i parametri in ingresso con i campi del foglio
NUMBER.Value = String$(12 - Len(Sheets("ODP").Cells(2, 2).Value), "0") & Sheets("ODP").Cells(2, 2).Value
ORDER_OBJECTS.Value(1) = "X" ' Header
ORDER_OBJECTS.Value(4) = "X" ' Operations
ORDER_OBJECTS.Value(5) = "X" ' Components
' Chiamata alla funzione SAP
If myFuncBPGD.call = False Then
MsgBox myFunc.Exception
GoTo FINE
End If
' Ricezione dati di ritorno dalla Funzione
Set HEADER = myFuncBPGD.Tables("HEADER")
Set OPERATION = myFuncBPGD.Tables("OPERATION")
Set COMPONENT = myFuncBPGD.Tables("COMPONENT")
' Leggo eventuali messaggi di ritorno e li mostro all'utente
Dim msx As String
'----------- NOTA: non funziona! PRETURN ritorna sempre vuoto.
'num_Row = PRETURN.RowCount
'
'For iRow = 1 To num_Row
' msx = PRETURN.Value(iRow, 4)
' MsgBox msx
' riga = "Messaggi =" & msx: Print #1, riga
'Next
'----------- FINE
num_Row_head = HEADER.RowCount
If num_Row_head = 0 Then
MsgBox "Errore lettua ODP!!!"
GoTo FINE
End If
'------------------------------------------------
' Ricavo i dati di testata
'------------------------------------------------
h_werks = HEADER(1, 2)
h_matnr = HEADER(1, 5)
h_data = HEADER(1, 11)
h_qta = CDbl(HEADER(1, 18))
h_um = HEADER(1, 19)
h_tipo = HEADER(1, 22)
h_descr = HEADER(1, 41)
' Conversione UM
h_um = conv_um(h_um)
' Metto i dati di testata nel foglio
Sheets("ODP").Cells(2, 3) = h_werks
Sheets("ODP").Cells(2, 4) = h_matnr
Sheets("ODP").Cells(2, 5) = h_descr
Sheets("ODP").Cells(2, 6) = h_qta
Sheets("ODP").Cells(2, 7) = h_um
Sheets("ODP").Cells(2, 8) = h_data
Sheets("ODP").Cells(2, 9) = h_tipo
'------------------------------------------------
' compilo operazioni
'------------------------------------------------
num_Row_ope = OPERATION.RowCount
For iRow = 1 To num_Row_ope
' Ricavo i dati delle operazioni
o_fase = OPERATION(iRow, 11)
o_chiave = OPERATION(iRow, 12)
o_werks = OPERATION(iRow, 13)
o_testo = OPERATION(iRow, 14)
o_um = OPERATION(iRow, 23)
o_qta = CDbl(OPERATION(iRow, 25))
o_data = OPERATION(iRow, 31)
o_cdl = OPERATION(iRow, 43)
o_cdl_desc = OPERATION(iRow, 44)
' Conversione UM
o_um = conv_um(o_um)
' Metto i dati delle operazioni nel foglio
Sheets("ODP").Cells(5 + iRow, 1) = o_fase
Sheets("ODP").Cells(5 + iRow, 2) = o_chiave
Sheets("ODP").Cells(5 + iRow, 3) = o_cdl
Sheets("ODP").Cells(5 + iRow, 4) = o_cdl_desc
Sheets("ODP").Cells(5 + iRow, 5) = o_testo
Sheets("ODP").Cells(5 + iRow, 6) = o_qta
Sheets("ODP").Cells(5 + iRow, 7) = o_um
Sheets("ODP").Cells(5 + iRow, 8) = o_data
Next
'------------------------------------------------
' Inserisco intestazione per componenti
'------------------------------------------------
n_riga_h_c = num_Row_ope + 7
Sheets("ODP").Cells(n_riga_h_c, 1) = "COMPONENTI"
n_riga_h_com = n_riga_h_c + 1
Sheets("ODP").Cells(n_riga_h_com, 1) = "Pos"
Sheets("ODP").Cells(n_riga_h_com, 2) = "Magazzino"
Sheets("ODP").Cells(n_riga_h_com, 3) = "Fase"
Sheets("ODP").Cells(n_riga_h_com, 4) = "Materiale"
Sheets("ODP").Cells(n_riga_h_com, 5) = "Descrizione"
Sheets("ODP").Cells(n_riga_h_com, 6) = "Q.tà"
Sheets("ODP").Cells(n_riga_h_com, 7) = "Um"
Sheets("ODP").Cells(n_riga_h_com, 8) = "Data"
' copio formato intestazione
Rows("4:5").Select
Selection.Copy
Rows(n_riga_h_c & ":" & n_riga_h_com).Select
Selection.PasteSpecial Paste:=xlPasteFormats, OPERATION:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
'------------------------------------------------
' compilo componenti
'------------------------------------------------
num_Row_c = COMPONENT.RowCount
For iRow = 1 To num_Row_c
' Ricavo i dati dei componenti
c_matnr = COMPONENT(iRow, 5)
c_lgort = COMPONENT(iRow, 7)
c_data = COMPONENT(iRow, 11)
c_qta = CDbl(COMPONENT(iRow, 12))
c_um = COMPONENT(iRow, 13)
c_pos = COMPONENT(iRow, 22)
c_fase = COMPONENT(iRow, 24)
c_desc = COMPONENT(iRow, 28)
c_um = conv_um(c_um)
' Metto i dati dei componenti nel foglio
Sheets("ODP").Cells(n_riga_h_com + iRow, 1) = c_pos
Sheets("ODP").Cells(n_riga_h_com + iRow, 2) = c_lgort
Sheets("ODP").Cells(n_riga_h_com + iRow, 3) = c_fase
Sheets("ODP").Cells(n_riga_h_com + iRow, 4) = c_matnr
Sheets("ODP").Cells(n_riga_h_com + iRow, 5) = c_desc
Sheets("ODP").Cells(n_riga_h_com + iRow, 6) = c_qta
Sheets("ODP").Cells(n_riga_h_com + iRow, 7) = c_um
Sheets("ODP").Cells(n_riga_h_com + iRow, 8) = c_data
Next
FINE:
Set myFuncBGC = Nothing
Range("b2:b2").Select
End Sub
'------------------------------------------------
' Conversione UM
'------------------------------------------------
Function conv_um(ByVal um_inp As String) As String
If um_inp = "ST" Then
um_inp = "PZ"
End If
conv_um = um_inp
End Function
Nessun commento:
Posta un commento