Administration
Speicherdienst
Anmelden
Hilfe
Skript
Name des Skripts
Skript
/* * This JavaScript program can be invoked from OrderEditPage to display * invoice data from SAP. * * For this to work, there must be a DataProvider configuration named "SAP" * (see DELEGATE_CONFIGURATION_NAME below), to which this script delegates. * The script can then be run through ScriptDataProvider, by creating a * configuration called "SAP-Link". * * The script does two important things: * * 1. it invokes the "SAP" DataProvider configuration for a given Order id, * retrieves the data, and re-orders it to match the predefined meta data, * with which the results are then displayed to the user. * * 2. it amends this retrieved data with an additional column, which when * clicked calls this script's prepareDownload() function, which will retrieve * images (usually invoice scans) from SAP. */ // For compatibility with Java7/Java8 (Rhino vs. Nashorn) if (typeof importClass != "function") { load("nashorn:mozilla_compat.js"); } importClass(Packages.java.util.Arrays); importClass(Packages.de.devbrain.bw.app.universaldata.meta.identifier.PlainIdentifier); importClass(Packages.de.devbrain.bw.app.universaldata.data.FieldDef); importClass(Packages.de.devbrain.bw.app.universaldata.data.RecordDef); importClass(Packages.de.devbrain.bw.app.universaldata.data.FunctionDef); importClass(Packages.de.devbrain.bw.app.universaldata.type.string.VarCharType); importClass(Packages.de.devbrain.bw.app.universaldata.type.BigDecimalType); importClass(Packages.de.devbrain.bw.app.universaldata.type.ByteArrayType); importClass(Packages.de.devbrain.bw.app.universaldata.type.temporal.DateOnlyType); importClass(Packages.de.devbrain.bw.app.universaldata.type.string.StringType); importClass(Packages.de.devbrain.bw.app.universaldata.provider.manager.CallType); importClass(Packages.de.devbrain.bw.app.universaldata.provider.manager.UnknownConfigurationException); importClass(Packages.de.devbrain.bw.app.universaldata.provider.DataProvider); importClass(Packages.de.devbrain.bw.app.universaldata.provider.value.Value); importClass(Packages.de.devbrain.bw.app.universaldata.provider.value.Literal); /** * The name of the DataProviderManager configuration under which this script * is accessible. */ var CONFIGURATION_NAME = "SAP-Link"; /** * The name of the DataProviderManager configuration to which this script * delegates, thus under which SAP can be accessed. */ var DELEGATE_CONFIGURATION_NAME = "SAP"; /** * Parameters for the INFO FunctionDef. */ var INFO_PARAMETERS = Arrays.asList([ new FieldDef( new PlainIdentifier("IM_ITEMSNR", "ITEMS-Auftragsnummer"), new VarCharType(255)) ]); /** * FunctionDef that reads the order data for parameters INFO_PARAMETERS and * returns them in rows resembling INFO_FIELDS. */ var INFO = new FunctionDef( new PlainIdentifier("Z_FI_GET_ACCDOC_INFO", "Auftragsdaten lesen"), new RecordDef(INFO_PARAMETERS), "T_RETURN", "T_BSEG"); /** * FieldDefs for the result returned from rechnungsDaten(). The Identifier * names match those of the result of INFO. */ var INFO_FIELDS = [ new FieldDef( new PlainIdentifier("BELNR", "Belegnummer", "Belegnummer eines Buchhaltungsbeleges", false), new VarCharType(10)), new FieldDef( new PlainIdentifier("GJAHR", "Geschäftsjahr", false), BigDecimalType.INSTANCE), new FieldDef( new PlainIdentifier("BLDAT", "Rechnungsdatum", "Belegdatum im Beleg", true), DateOnlyType.INSTANCE), new FieldDef( new PlainIdentifier("XBLNR", "Rechnungsnummer", true), new VarCharType(16)), new FieldDef( new PlainIdentifier("BKTXT", "ITEMS-Auftragsnummer", false), new VarCharType(25)), new FieldDef( new PlainIdentifier("AUGDT", "Datum des Ausgleichs", true), DateOnlyType.INSTANCE), new FieldDef( new PlainIdentifier("AUGBL", "Ausgleichsbeleg", "Belegnummer des Ausgleichsbelegs", false), new VarCharType(10)), new FieldDef( new PlainIdentifier("DMBTR", "Betrag (brutto)", "Betrag in Hauswährung", true), BigDecimalType.INSTANCE), new FieldDef( new PlainIdentifier("ZUONR", "Zuordnungsnummer", false), new VarCharType(18)), new FieldDef( new PlainIdentifier("SAKNR", "Sachkonto", "Nummer des Sachkontos", true), new VarCharType(10)), new FieldDef( new PlainIdentifier("KOSTL", "Kostenstelle", true), new VarCharType(10)), new FieldDef( new PlainIdentifier("AUFNR", "Auftragsnummer", true), new VarCharType(12)), new FieldDef( new PlainIdentifier("ANLN1", "Anlagen-Hauptnummer", false), new VarCharType(12)), new FieldDef( new PlainIdentifier("ANLN2", "Anlagenunternummer", false), new VarCharType(4)), new FieldDef( new PlainIdentifier("LIFNR", "Kontonummer", "Kontonummer des Lieferanten bzw. Kreditors", false), new VarCharType(10)), new FieldDef( new PlainIdentifier("FISTL", "Drittmittel", true), new VarCharType(16)), new FieldDef( new PlainIdentifier("ARCH", "Scan", true), CallType.INSTANCE) ]; /** * Parameters for the ARCH FunctionDef. */ var ARCH_PARAMETERS = Arrays.asList([ new FieldDef( new PlainIdentifier("IM_BELNR", "Belegnummer", "Belegnummer eines Buchhaltungsbeleges"), new VarCharType(10)), new FieldDef( new PlainIdentifier("IM_GJAHR", "Geschäftsjahr"), new VarCharType(4)) ]); /** * FunctionDef that reads the binary scan for parameters ARCH_PARAMETERS and * returns them in a single row resembling ARCH_FIELDS. */ var ARCH = new FunctionDef( new PlainIdentifier("Z_FI_GET_ACCDOC_ARCH", "Archivierte Kreditorenrechnung"), new RecordDef(ARCH_PARAMETERS), "T_RETURN", null); /** * FieldDefs for the result returned from prepareDownload(). The Identifier * names match those of the result of ARCH. */ var ARCH_FIELDS = [ new FieldDef( new PlainIdentifier("EX_EXTENSION", "Dateiendung", true), StringType.INSTANCE), new FieldDef( new PlainIdentifier("EX_DATA", "Binärdaten", false), ByteArrayType.OCTET_STREAM) ]; /** * Asks the delegate DataProvider for records for the given order id, and * re-orders the returned data to match INFO_FIELDS and amends the returned * row with a call to prepareDownload(). */ function rechnungsDaten(orderId) { var dataProvider = getDataProvider(DELEGATE_CONFIGURATION_NAME); var delegateResult = dataProvider.execute(INFO, Arrays.asList([orderId.toString()])); var srcFields = delegateResult.getMetaData().getFields().toArray(); var result = []; var producer = new AmendmendProducer(srcFields); var rows = delegateResult.getIterator(); while (rows.hasNext()) { var nativeResult = producer.amend(rows.next()); result.push(convertArray(nativeResult, "java.io.Serializable")); } /* * Nashorn from JDK-1.8.40 cannot cast an Array to a Java array anymore, * and instead requires Java.to(..). The version of Nashorn in JDK-1.8.31 * knows Java.to and still can cast the Array, so it doesn't matter there. * * Rhino from JDK-1.7 doesn't know the "Java" class at all, but can cast the * Array. */ var fields; if (typeof Java != "object") { fields = INFO_FIELDS; } else { fields = Java.to(INFO_FIELDS, "de.devbrain.bw.app.universaldata.data.FieldDef[]"); } return DataProvider.Result.ofArray(result, new RecordDef(Arrays.asList(fields))); } function AmendmendProducer(srcFields) { this.map = new FieldDefsMap(srcFields, INFO_FIELDS.slice(0, -1)); this.indexOfBELNR = indexByName("BELNR", srcFields); this.indexOfGJAHR = indexByName("GJAHR", srcFields); } AmendmendProducer.prototype.amend = function(srcRow) { var parameters = [ new Literal(srcRow[this.indexOfBELNR].toString(), StringType.INSTANCE), new Literal(srcRow[this.indexOfGJAHR].toString(), StringType.INSTANCE) ]; /* * Check whether we run on Rhino (JDK-1.7) or on Nashorn (JDK-1.8), since * Java Class lookup differs between them. */ if (typeof Java != "object") { /* * We run on Rhino. * * The explicit overload resolution is required by older versions of the * JDK. */ var call = new Packages.de.devbrain.bw.app.universaldata.provider.manager.Call[ "(java.lang.String," + "de.devbrain.bw.app.universaldata.data.FunctionDef," + "de.devbrain.bw.app.universaldata.provider.value.Value[])"]( CONFIGURATION_NAME, PREPARE_DOWNLOAD, parameters); } else { /* * We run on Nashorn. */ var Call = Java.type("de.devbrain.bw.app.universaldata.provider.manager.Call"); var call = new Call(CONFIGURATION_NAME, PREPARE_DOWNLOAD, parameters); } var dstRow = this.map.mapRow(srcRow); dstRow.push(call); return dstRow; }; /** * A FunctionDef to call the prepareDownload() function. We re-use the * parameters for the ARCH call, as we can match the parameters of the * prepareDownload() function to match those. */ var PREPARE_DOWNLOAD = new FunctionDef( new PlainIdentifier("prepareDownload", ""), new RecordDef(ARCH_PARAMETERS), null, null); /** * Invokes the delegate DataProvider with the ARCH call. * * If there is no data, returns null, causing the DataProvider to also return * null from its execute method. * * Otherwise, extracts the fields from the returned data, translating the * extension field value to a MIME type and returning a new Result with the * value from the binary data field and a ByteArrayType FieldDef with the * determined MIME type. */ function prepareDownload(IM_BELNR, IM_GJAHR) { var dataProvider = getDataProvider(DELEGATE_CONFIGURATION_NAME); var delegateResult = dataProvider.execute(ARCH, Arrays.asList([IM_BELNR, IM_GJAHR])); var srcRows = delegateResult.toList(); if (srcRows.size() == 0) return null; var srcFields = delegateResult.getMetaData().getFields().toArray(); var indexEXTENSION = indexByName("EX_EXTENSION", srcFields); var indexDATA = indexByName("EX_DATA", srcFields); var srcRow = srcRows.get(0); var data = srcRow[indexDATA]; var extension = srcRow[indexEXTENSION]; var fieldDef = new FieldDef( new PlainIdentifier("EX_DATA", "Binärdaten"), new ByteArrayType(determineMimeType(extension))); return new DataProvider.Result( convertArray([data], "java.io.Serializable"), new RecordDef(Arrays.asList([fieldDef]))); } function determineMimeType(extension) { var actual = extension.toString().toLowerCase(); if (actual.equals("pdf")) return "application/x-pdf"; if (actual.equals("tif")) return "image/tiff"; return "application/octet-stream"; } function getDataProvider(configurationName) { var remoteDP = backend.getStorage().getDataProviderManager(). getProvider(configurationName); if (remoteDP == null) throw new UnknownConfigurationException(configurationName); return remoteDP; } function indexByName(fieldName, fields) { for (var x = 0; x < fields.length; ++x) { if (fields[x].getIdentifier().getName() == fieldName) return x; } return -1; } /** * Rhino in the version distributed with OpenJDK 1.6.0_30 cannot convert * native JavaScript arrays to Java arrays; later versions can. * * This method explicitly converts the given native array to a Java array of * the class with the given (fully qualified) name. */ function convertArray(array, type) { var javaClass = java.lang.Class.forName(type); var result = java.lang.reflect.Array.newInstance(javaClass, array.length); for (var i = 0; i < array.length; i++) { result[i] = array[i]; } return result; } /** * A class to map destination fields to source fields. Both parameters contain * an array of FieldDefs, which are matched according to their Identifier * name. */ function FieldDefsMap(srcFields, dstFields) { // For each element in dstFields in the same order, contains the index of // this field in srcFields. this.srcIndexes = []; for (var x = 0; x < dstFields.length; ++x) { var fieldName = dstFields[x].getIdentifier().getName(); this.srcIndexes.push(indexByName(fieldName, srcFields)); } } /** * Returns the index of the source field, given this destination field index, * or -1 if there is none. */ FieldDefsMap.prototype.getSourceIndex = function(dstFieldIndex) { return this.srcIndexes[dstFieldIndex]; }; /** * Translates the given whole row of data from the order given as source * fields to the order given as destination fields. */ FieldDefsMap.prototype.mapRow = function mapRow(srcRow) { var result = []; for (var x = 0; x < this.srcIndexes.length; ++x) { var index = this.getSourceIndex(x); result.push(index == -1 ? null : srcRow[index]); } return result; };