We figured out a way to post IDocs to SAP with JIDocLib

Just wanted to share a method to post IDocs to SAP from Ignition since I was not able to find sample code online that worked. It's been 3+ years and almost 5000 views ago since @phyatt shared his knowledge via Connecting to SAP with 3rd party Python lib or SAP JCo jar that allowed us to call SAP BAPIs via Ignition. But recently we were challenged with interfacing to SAP via IDocs.

Follow the same steps in the referenced post for RFC BAPI calls but add the additional sapidoc3.jar file to the same folder C:\SAPJCo3

image
Then modify ignition.conf to add the classpath and restart the Gateway.
image
I am using JCo version 3.1.8 and JCoIDocLib 3.1.3. Current SAP customers can download these from their website but you do need a username and password.

def handleMessage(payload):
	from com.sap.conn.idoc import IDocFactory
	from com.sap.conn.idoc import IDocRepository
	from com.sap.conn.idoc import IDocSegmentIterator
	from com.sap.conn.idoc import IDocRecord
	from com.sap.conn.idoc import IDocDocumentList
	from com.sap.conn.idoc import IDocDocument
	from com.sap.conn.idoc.jco import JCoIDoc
	from com.sap.conn.jco import JCoDestinationManager
	try:
		sapSystem="QST"
		destination = JCoDestinationManager.getDestination(sapSystem)
		iDocRepository=JCoIDoc.getIDocRepository(destination)
		tid=destination.createTID()
		iDocFactory=JCoIDoc.getIDocFactory()
		iDocList=iDocFactory.createIDocDocumentList(iDocRepository, "DELFOR02")
		doc=iDocList.addNew()
		
		#Use the one below (non-IDocList to investigate metadata)
		#doc=iDocFactory.createIDocDocument(iDocRepository, "DELFOR02")
		#SAP will generate IDocNumber automatically
		#doc.setIDocNumber("0000000009472075")
		#I think client is determined from destimation connection
		#doc.setClient("400")
		#Direction is 2 for inbound but seems to be determined correctly if not set
		#doc.setDirection("2")
		doc.setMessageType("DELINS")
		doc.setMessageCode("")
		doc.setMessageFunction("")
		doc.setSenderAddress("")
		doc.setSenderLogicalAddress("")
		doc.setSenderPartnerFunction("")
		doc.setSenderPort("QST")
		doc.setSenderPartnerType("KU")
		doc.setSenderPartnerNumber("2001")
		doc.setRecipientAddress("")
		doc.setRecipientLogicalAddress("")
		doc.setRecipientPartnerFunction("")
		doc.setRecipientPort("SAPQST")
		doc.setRecipientPartnerType("LS")
		doc.setRecipientPartnerNumber("SAPQST400")
		#Date and Time are set by System but can override if needed 
		#doc.setCreationDate("20240107")
		#doc.setCreationTime("134400")
		#This doesn't seem to require serialization but might be good to add milliseconds for unique
		doc.setSerialization("20240105103051")
		#Not sure if needed to set TABNAM.  Doesn't appear to be needed
		#doc.setValue("TABNAM", "EDI_DC40")		
		segment=doc.getRootSegment()
		segment=segment.addChild("E1EDK09")
		segment.setValue("VTRNR", "SFYW9U")
		segment.setValue("BSTDK", "20230106")
		segment.setValue("LABNK", "912-25")
		segment.setValue("ZEICH", "20230106")
		segment.setValue("USR01", "test@test.com")
		segment.setValue("USR02", "555-555-5555")
		segment.setValue("MFEIN", "1")
		segment.setValue("FFEIN", "1")
		
		segment=segment.addChild("E1EDKA1")
		segment.setValue("PARVW", "WE")
		segment.setValue("PARTN", "IE0GA")
		
		segment=segment.addSibling("E1EDKA1")
		segment.setValue("PARVW", "LF")
		segment.setValue("PARTN", "FGJN3")
			
		segment=segment.addSibling("E1EDKA1")
		segment.setValue("PARVW", "AG")
		segment.setValue("PARTN", "2001")
			
		segment=segment.addSibling("E1EDP10")
		segment.setValue("IDNKD", "L3IU1 ANF")
		segment.setValue("VRKME", "EA")
		segment.setValue("KWERK", "IE0GA")
		segment.setValue("BELNR", "0000979271")
		segment.setValue("LFIMG", "65")
		segment.setValue("LIDTL", "20221208")
		segment.setValue("ABHOR", "20230119")
		segment.setValue("LABKY", "2")
		segment.setValue("ABRAB", "20230106")
		segment.setValue("ABRBI", "20230119")
		segment.setValue("SCREL", "02")
		segment.setValue("VTRNR", "SFYW9U")
		segment.setValue("KZAUS", "1")
		
		segment=segment.addChild("E1EDP16")
		segment.setValue("ETTYP", "2")
		segment.setValue("PRGRS", "D")
		segment.setValue("EDATUV", "20230106")
		segment.setValue("EDATUB", "20230106")
		segment.setValue("WMENG", "0")
		#Uncomment below to see the XML representation of the iDoc
		#xml = JCoIDoc.getIDocFactory().getIDocXMLProcessor().render(doc)
	except:
		return "Unexpected error connecting or building iDoc: ", str(sys.exc_info()[0]), str(sys.exc_info()[1])
	try:
		JCoIDoc.send(iDocList,IDocFactory.IDOC_VERSION_DEFAULT,destination,tid)
		#This cleans up the Transaction manager.  Probably not needed but only execute if no issue with .send() above
		destination.confirmTID(tid)
		return tid, doc.checkMandatoryFields(), doc.checkSyntax(), doc.getCreationDate(), doc.getCreationTime()
	except:
		return "Unexpected error sending iDoc: ", str(sys.exc_info()[0]), str(sys.exc_info()[1])

Remember this is Gateway-scoped so you will need to add a Gateway Message Handler if you want to call it from Designer or a client.

I am not a Developer so feel free to provide suggestions and improvements. Hope this helps someone.

4 Likes

First, kudos for giving back to the community :slight_smile: Never hurts to share things.

I don't see anything significant to change or improve. One minor optimization, both typing and performance wise - all of the single argument doc.setX(Y) arguments can be rewritten due to Jython's understanding of JavaBean properties - so you could instead write doc.x = Y, as in doc.messageType = "DELINS", to make your code look a little more like Python and save some typing.

5 Likes

Thanks @PGriffith. I didn't know you could do that!