cancel
Showing results for 
Search instead for 
Did you mean: 

send idoc flat file from non-sap to sap server

Former Member
0 Kudos

Hello,

Is there anyone who knows how to send an idoc file from non-sap to sap server.

Not sure if this is a problem, but our idoc file is some kind of flat file like you can see underneath

EDI_DC 003 J3AORD012SAPPEU ....

E2EDK01 003 E2EDK01 00000001 EUR ...

E2EDK14 003 E2EDK14 00000002 012

E2EDK14 003 E2EDK14 00000002 019EDI

It should be running on a unix server (non-sap) and I prefer to use java so I was thinking to use

SAP Java Connector (jco)

Until now, I still didn't find a nice example to do this while I don't think I'm the first one to achieve this.

I've read something about function module IDOC_INBOUND_ASYNCHRONOUS but in that case I don't know how I need to fit

in my idoc file in this function module

I've also read something about SAP IDOC Clas library and had a look at an example but again I don't know how

my idoc file should fit in. It looks like there is a nice example for idoc xml but then I suppose I need to be able to convert my

idoc flat file to an idoc xml file.

Please let me know how to do this and it would be nice to have an example for doing this.

thanks in advance

Steven

Accepted Solutions (0)

Answers (8)

Answers (8)

Former Member
0 Kudos

Hi Harald,

thanks a lot for your help. Once I've access to the sap server I will give it a try.

To be honest I really thought it would have been much easier and I wouldn't have to read the different structures but only load my idoc flat file together with the right funcion module.

regarding the formatting of the code. It took me half an hour to paste the code into the forum since it didn't accept it when putting it in a nice format so at the end I had to put it in some stream format. sorry about this. Hopefully the administrators of this forum will read this so they can do some changes

thanks,

Steven

Former Member
0 Kudos

Hello,

thanks for that.

So I suppose I should get the structure of EDI_DC or EDI_DC40 like you can see underneath and loop through the different fields and get name, offset and length of the different fields. So this way I don't need to set the different fields manually like I did in my previous example.

Similar for the data records where I will get the structure of EDI_DD or EDI_DD40.

The length of the different segments can be different and as far as I know the default length of SDATA is 1000 bytes so I hope it won't be a problem in case the segment is not that long.

I suppose SAP will do the rest whatever idoc type is involved.

Does this look ok to you ?


***
int docnum = 0;
for(String s = br.readLine(); br!=null; s = br.readLine()) {
  String tabname = s.substring(0, Math.min(10,s.length())).trim();
  boolean use40 = false;
  if(tabname.equals("EDI_DC40")) {
    use40 = true;
    docnum++;
    idocControl.setValue("DOCNUM", docnum);
    // Set remaining fields of control record (source has structure EDI_DC40)

   JCoMetaData meta = destination.getRepository().getStructureDefinition("EDI_DC40");

   for (int j=0 ; j < meta.getFieldCount() - 1 ; j++)
     idocControl.setValue(meta.getName(i),s.substring(meta.getOffset(i),meta.getLength(i)))

  } else if(tabname.equals("EDI_DC")) {
    docnum++;
    idocControl.setValue("DOCNUM", docnum);
    // Set remaining fields of control record (source has structure EDI_DC)

    JCoMetaData meta = destination.getRepository().getStructureDefinition("EDI_DC");

    for (int j=0 ; j < meta.getFieldCount() - 1 ; j++)
      idocControl.setValue(meta.getName(i),s.substring(meta.getOffset(i),meta.getLength(i)))

  } else {
    idocData.appendRow();
    idocData.setRow(docnum-1);
    if(use40) {
      // Set fields in idocData from source with structure EDI_DD40

    JCoMetaData meta = destination.getRepository().getStructureDefinition("EDI_DD40");

    for ( int j=0 ; j < meta.getFieldCount() - 1 ; j++)
      idocControl.setValue(meta.getName(i),s.substring(meta.getOffset(i),meta.getLength(i)))


    } else {
      // Set fields in idocData from source with structure EDI_DD

      JCoMetaData meta = destination.getRepository().getStructureDefinition("EDI_DD");

      for ( int j=0 ; j < meta.getFieldCount() - 1 ; j++)
        idocControl.setValue(meta.getName(i),s.substring(meta.getOffset(i),meta.getLength(i)))

    }
  }
}

thanks in advance,

Steven

Edited by: Steven Wouters on Nov 20, 2009 3:59 PM

Former Member
0 Kudos

Hi Steven,

had a little bit difficulty reading your coding, please try to paste it properly formatted next time...

This looks more like what I had in mind. I'm not sure though, if this meets your requirements. I.e. I preferred a more general solution, that reflects in a way the different possibilities for input data for RSEINB00. So your last version accomplishes that, but it also means, that you have more overhead due to the additional dictionary look-ups of the EDI_D* structures.

This should be negligible as long as your program keeps running and parses multiple files (since the meta data will be cached in the repository, so only the first lookup will be slow).

Cheers, harald

Former Member
0 Kudos

test

Former Member
0 Kudos

Hello,

1) using rfcsdk. So in worst case I suppose I would need to ftp the file to the sap server and call startrfc with function module

EDI_DATA_INCOMING on my unix server with the pathname referring to the location on sap server.

2) using jco. So would the code look like this? I also don't understand why I can't find an example to do this since this would have made life much easier. As you can see in my code, the first lline in my idoc flat file contains the EDI_DC.

JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);

JCoFunction function = destination.getRepository().getFunction("IDOC_INBOUND_SINGLE");

if(function == null)

throw new RuntimeException("IDOC_INBOUND_SINGLE not found in SAP.");

JCoStructure idocControl = function.getImportParameterList().getStructure("PI_IDOC_CONTROL_REC_40");

JCoTable idocData = function.getTableParameterList().getTable("PT_IDOC_DATA_RECORDS_40");

FileReader fr = new FileReader("idocflat.txt");

BufferedReader br = new BufferedReader(fr);

String s;

int i=0;

while((s = br.readLine()) != null) {

If ( i == 0)

{

idocControl.setValue("MANDT", s.substring(10,13));

idocControl.setValue("DOCTYP", s.substring(35,43));

idocControl.setValue("DIRECT", s.substring(43,44));

idocControl.setValue("RCVPOR", s.substring(44,54));

idocControl.setValue("RCVPRT", s.substring(54,56));

idocControl.setValue("RCVPRN", s.substring(56,66));

idocControl.setValue("STD", s.substring(157,158));

idocControl.setValue("SNDPOR", s.substring(178,188));

idocControl.setValue("SNDPRT", s.substring(188,190));

idocControl.setValue("SNDPRN", s.substring(190,200));

idocControl.setValue("REFINT", s.substring(291,305));

idocControl.setValue("REFMES", s.substring(319,333));

idocControl.setValue("CREDAT", s.substring(403,411));

idocControl.setValue("CRETIM", s.substring(411,417));

idocControl.setValue("MESTYP", s.substring(417,423));

idocControl.setValue("IDOCTYP", s.substring(423,432));

idocControl.setValue("SNDPFC", s.substring(441,443));

}

i=i+1;

idocData.appendRow();

idocData.setValue("SEGNAM",s.substring(0,7));

idocData.setValue("SDATA",s.substring(55));

}

fr.close();

try

{

function.execute(destination);

}

catch(AbapException e)

{

System.out.println(e.toString());

return;

}

thanks,

Steven

Former Member
0 Kudos

Input to RSEINB00 (which should reflect your existing file format) consists of the following lines:

  • IDoc control record (1 line) with format EDI_DC40 or EDI_DC

  • IDoc data record (1 or more lines) with format EDI_DD40 or EDI_DD

EDI_DC40 and EDI_DD40 are the newer structures and EDI_DC and EDI_DD were used in releases prior to 4.x (old structures can still be used in newer releases). Your file is using the old structure, as you can see by the first line, which says EDI_DC (new would be EDI_DC40). Instead of using a counter to distinguish control and data records it would be better to trigger of the first 10 characters (same what RSEINB00 is doing):


int docnum = 0;
for(String s = br.readLine(); br!=null; s = br.readLine()) {
  String tabname = s.substring(0, Math.min(10,s.length())).trim();
  boolean use40 = false;
  if(tabname.equals("EDI_DC40")) {
    use40 = true;
    docnum++;
    idocControl.setValue("DOCNUM", docnum);
    // Set remaining fields of control record (source has structure EDI_DC40)
  } else if(tabname.equals("EDI_DC")) {
    docnum++;
    idocControl.setValue("DOCNUM", docnum);
    // Set remaining fields of control record (source has structure EDI_DC)
  } else {
    idocData.appendRow();
    idocData.setRow(docnum-1);
    if(use40) {
      // Set fields in idocData from source with structure EDI_DD40
    } else {
      // Set fields in idocData from source with structure EDI_DD
    }
  }
}

If you stick to your coding, add at least the missing setRow call. The offsets of the individual fields could be retrieved from the repository:


JCoMetaData meta = destination.getRepository().getStructureDefinition("EDI_DC");

Using the meta data you could basically determine all the details you need (field name via method getName(i), offset via getByteOffset(i) and length via getLength(i) assuming you iterate over all fields of the structure via i from 0 to getFieldCount()-1 ). Note that you have in SAP for example function IDOC_INBOUND_SYNCHRONOUS, which utilizes the old structures EDI_DC and EDI_DD. In most cases though you should be able to use those interchangeably.

Former Member
0 Kudos

Hello,

thanks for that. Note that I've got no access yet to the sap server, so I can't test it at the moment.

- I've got the startrfc running on my unix machine but when I would use startrfc with module EDI_DATA_INCOMING, does this require to have the file on the sap server ?

- when using jco, would the code look something like this ?

JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);

JCoFunction function = destination.getRepository().getFunction("IDOC_INBOUND_SINGLE");

if(function == null)

throw new RuntimeException("IDOC_INBOUND_SINGLE not found in SAP.");

JCoStructure idocControl = function.getImportParameterList().getStructure("PI_IDOC_CONTROL_REC_40");

JCoTable idocData = function.getTableParameterList().getTable("PT_IDOC_DATA_RECORDS_40");

FileReader fr = new FileReader("idocflat.txt");

BufferedReader br = new BufferedReader(fr);

String s;

int i=0;

while((s = br.readLine()) != null) {

If ( i == 0)

idocControl.setValue(s.substring(0,6),s.substring(6));

i=i+1;

idocData.appendRow();

idocData.setValue(s.substring(0,7),s.substring(7));

}

fr.close();

try

{

function.execute(destination);

}

catch(AbapException e)

{

System.out.println(e.toString());

return;

}

thanks

Steven

Former Member
0 Kudos

EDI_DATA_INCOMING expects a file in a location accessible via the SAP application server where the RFC module is invoked. So you'd still have to (s)ftp the file or make it available via NFS mount or network share. That's why I thought it's not a great solution, but at least a cheap one...

As far as your coding is concerned, it looks like you should familiarize yourself with IDocs in SAP. An IDoc essentially consists of one IDoc control record (envelope identifying message type, sender, recipient, etc.), one or more data records (actual content of the IDoc, which is arranged in a hierarchical manner) and one or more status records (a process log capturing latest processing status and what messages occurred during processing). From your perspective the status records are irrelevant, since you're trying to create a new IDoc, so you just need to send the control record (envelope) and the data records (content).

The control record is the line in the file starting with EDI_DC; any other lines are the data records. When filling the control record you should already have some loop or multiple statements for setting each individual value. I.e. message type, sender information, etc. must be filled, so you might have statements like:


idocControl.setValue("SNDPRN", "<sender>");
idocControl.setValue("SNDPRT", "<senderType>");

For the data records it is much simpler, since it should be sufficient to fill record field values SEGNAM (segment name) and SDATA (data segment content as fixed length record string). Since your IDoc file seems to have the correct structure of RSEINB00, you could even retrieve the metadata from SAP for the control record and data records utilized by RSEINB00 (don't have a SAP system right now, otherwise I'd give you the names) and then iterate over the metadata (check out class JCoMetaData ). So your EDI_DC line is the IDoc control record with many fields, that you'll have to set using substring method invocations (the corresponding field names, length and offset) could either be hardcoded or retrieved via metadata.

Former Member
0 Kudos

thanks for that.

I'm still not convinced that this is the way forward.

I'm new to sap so do you have an example how to parse the file and fill the control record and data record.

Is this something like the code underneath where pi_idoc_control_rec_40 will be filled with the first line of my idoc flat file containing the EDI_DC and where pt_idoc_data_records_40 will be filled with all other lines of my idoc flat file containing the segments different from EDI_DC ???

Don't understand where/why to split segments and data.

...

JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);

JCoFunction function = destination.getRepository().getFunction("IDOC_INBOUND_SINGLE");

if(function == null)

throw new RuntimeException("IDOC_INBOUND_SINGLE not found in SAP.");

function.getImportParameterList().setValue("pi_idoc_control_rec_40", ... );

function.getImportParameterList().setValue("pt_idoc_data_records_40", ... );

try

{

function.execute(destination);

}

catch(AbapException e)

{

System.out.println(e.toString());

return;

}

....

btw, maybe I just need the function module EDI_DATA_INCOMING with my idoc flat file as input parameter.

thanks,

Steven

Former Member
0 Kudos

HHmm, ok, for little SAP experience it might be best to start off with a trivial solution that doesn't require any coding. You can achieve this by utilizing your existing solution (ftp file) and then invoke EDI_DATA_INCOMING via an executable, startrfc, distributed by SAP in the SAP RFC SDK. E.g. in your case it would probably look something like this:


startrfc -3 -h <hostname> -s <systemNo> -u <userID> -p <passwd> -c <client>
         -F EDI_DATA_INCOMING -E PATHNAME=<absoluteFilename> -E PORT=<usedPort> 

OSS note [27517|http://service.sap.com/sap/support/notes/27517] describes where to get the latest RFC SDK version. I'd recommend to download the RFC SDK and try this out (e.g. I used "classic" RFC SDK 7.10). Your custom should provide you the logon details and also which inbound port they're using (PORT parameter of function module, which corresponds to port used in RSEINB00).

Another alternative would be to simply trigger a job event (via startrfc using RFC function BP_EVENT_RAISE), which in turn would trigger inbound IDoc processing via RSEINB00. Basically your customer would have to define a job that is triggered by the raised event.

As I remember startrfc cannot be utilized for functions with "complex" parameters (e.g. TABLES parameters in case of the RFC modules for sending IDocs).

Now a short comment on your pasted JCo coding. In case of complex parameters you don't invoke the set_value directly on the parameter. Instead you first grab a reference to the corresponding object, i.e.:


JCoStructure idocControl = function.getImportParameterList().getStructure(
                               "PI_IDOC_CONTROL_REC_40");
JCoTable idocData = function.getTableParameterList().getTable(
                               "PT_IDOC_DATA_RECORDS_40");

Now on the structure you can directly set the fields via method setValue. For the table it's similar, though first you should create the rows (using one by one via appendRow or all at the same time via appendRows ). Before setting the values for each row via method setValue define which row values you want to change via method setRow (as in Java arrays, table rows start from 0). The parsing of the file is the usual Java coding for handling fixed length records.

Hope this helps, harald

Former Member
0 Kudos

Hi Harald,

first of all thanks for your reply and I will give you some background info.

We currently create the idoc flat files at our end and send them to the customer via ftp who will load these files into sap.

Now they want us to send the files and load them straight away in sap so that's why I'm looking at it to see how we would need to do this.

The flat files we create contains fixed length records and our customer is able to import them so there shouldn't be any problem with the files.

Because of this, I would have expected that it should be possible to work with these idoc flat files so we wouldn't need to create xml-idoc files.

Regarding the SAP IDOC Clas library I've only found these two ways.

// a) create new idoc

IDocDocument doc = iDocFactory.createIDocDocument(iDocRepository, "MATMAS02");

IDocSegment segment = doc.getRootSegment();

segment = segment.addChild("E1MARAM");

// and so on. See Idoc Specification .....

JCoIDoc.send(doc, IDocFactory.IDOC_VERSION_DEFAULT, destination, tid);

// b) use existent xml file

IDocXMLProcessor processor=iDocFactory.getIDocXMLProcessor();

IDocDocumentList iDocList=processor.parse(iDocRepository, iDocXML);

JCoIDoc.send(iDocList, IDocFactory.IDOC_VERSION_DEFAULT, destination, tid);

destination.confirmTID(tid);

In the first case it seems I would need to create the idoc file from scratch while I already have the idoc file.

so as far as I understand I would need to read my flat file segment per segment, field per field to create this idoc document object

so I don't think I want to do this.

In the second case it seems I need an idoc-xml file so that's why I mentioned that I could only find a nice example for idoc xml

So how do I need to read in the idoc flat file and fill the corresponding data structures?

I hope you don't refer to that first example because I think it would be a big job to do this (note that we have lots of different

idoc files), it would mean lots of rework since we already translate the files in the right idoc flat file format and this looks to me that it could cause a lot of production issues.

thanks

Steven

Former Member
0 Kudos

Ok, excellent, sounds like you should get away with some minimal coding. I'd leave the JCo IDoc library aside and instead use the standard SAP function modules for posting IDocs via RFC. I'd pick either IDOC_INBOUND_ASYNCHRONOUS or IDOC_INBOUND_SINGLE. The first one has the disadvantage that you don't get any information back, whereas with the latter one you get the IDoc number of the newly created IDoc . Both essentially have the same parameters for specifying the IDoc, main difference is that IDOC_INBOUND_ASYNCHRONOUS allows you to send multiple IDocs within one RFC call and thus has a table parameter for the IDoc control records.

Your flat file should more or less match already the parameters that both function modules expect. So all you need to do is parse the file and fill IDoc control record (idoc_control_rec_40) whenever you have a control record and otherwise add a IDoc data record (idoc_data_rec_40). Note that for IDoc data records you don't have to fill most of the meta data information, i.e. segment name (SEGNAM) and data (SDATA) should be sufficient. The latter basically contains the segment data as a fixed length record, so exactly what you have already in the file (so simple substring with the right offset should do to identify segment names and data).

Note that IDOC_INBOUND_ASYNCHRONOUS is a bit of a misnomer in my opinion: It is not really asynchronous, so if in the receiving system the immediate processing is set in the partner profile, the function module waits until the IDoc is completely processed and only then returns (though there's no return parameters that provide any feedback).

Let me know if you need further details.

Cheers, harald

Former Member
0 Kudos

Hi Steven,

If you flat file (fixed length records) would have the right layout you could simply use standard SAP program RSEINB00 to process the file. That's the traditional method from the olden days, where usually the IDocs were transferred via ftp/sftp...

However, looking at your pasted file contents I doubt that your generated file has the right format. As you mentioned you could utilize JCo along with RFC function module IDOC_INBOUND_ASYNCHRONOUS or alternatively the IDoc class library. Either way, you'd have to read in the file and fill the corresponding data structures (so that's where you need to understand the existing file layout and how IDocs are structured in general in SAP). There's no requirement to use XML with the IDoc class library; if you'd have an IDoc file in (the right) XML format, you could utilize the IDoc class library to directly parse the file and return an IDoc (i.e. get an IDocXMLProcesser instance and utilize the parse methods). You can always follow the "manual" approach though and get an IDocDocument from an IDocFactory and then add the data from your file).

It might also be worthwhile to check whether the file generation is necessary. E.g. often the intermediate file creation steps create more worries than any benefit (but sometimes due to restrictions on the "sending" applications it is not possible to avoid this).

Cheers, harald