Files
Facilitor/APPL/IMP/imp_shared.js
2016-08-23 08:45:55 +00:00

527 lines
22 KiB
JavaScript

/*
$Revision$
$Id$
File: imp_shared.js
Description: Helper functie
Parameters:
import_app_key Importfunctie key
Context:
Note: ========= LET OP LET OP =========
Dit bestand wordt ook gebruikt door ../../utils/gen_import/gen_import.wsf
Daar hebben we door CScript maar heel weinig functies!
DUS: Geen L() functie voor locale!!!!
EN: Geen Server.CreateObject maar new ActiveXObject
Dit bestand wordt ook gebruikt door ../api/api_gen_import.asp
========= LET OP LET OP =========
*/
// fileStream moet onze data bevatten
// import_app_key de import-functie
// params import_key mag/zal leeg zijn voor het eerste bestand
// customerId: "UWVA"
// ref_key: optioneel een key die in FAC_IMPORT_REFKEY gaat.
// fac_home: net boven de APPL dus bijv. "d:/apps/facilitor/"
// filepathname: optioneel: stoppen we in fac_import_filenaam
// keep_old: oude imports van dezelfde soort van maximaal keep_old
// seconden worden nog even aangehouden. Default 0
// Nodig als je meerdere parallelle imports wilt ondersteunen
// (vanuit api_gen_import bijvoorbeeld)
// keep_backup: als fac_import_app_folder gezet is wordt een backup
// van de import file gemaakt onder fac_import_app_folder/BACKUP2015/....
// Merk op: vanuit gen_import.wsf wordt keep_backup *niet* gezet
// omdat gen_import.wsf (nog) zelf de backup maakt
function impReadStream(fileStream, import_app_key, params)
{
params = params || {};
if (!("keep_old" in params))
params.keep_old = 0;
var import_key = params.import_key || -1;
if (import_key < 0)
{
var this_import_where = " fac_import_app_key = " + import_app_key;
if (params.keep_old > 0)
this_import_where += " AND fac_import_datum_gelezen <= SYSDATE - " + parseInt(params.keep_old) + " /60/60/24";
// Hadden we nog iets openstaan voor deze import_app_key? Dan wissen we die eerst
// We ondersteunen namelijk geen *twee* openstaande imports van dezelfde functie
var sql = "DELETE FROM fac_import"
+ " WHERE " + this_import_where
+ " AND fac_import_datum_verwerkt IS NULL";
Oracle.Execute(sql); // cascadeert vanzelf FAC_IMP_LOG en FAC_IMP_FILE
// Merk op: verwerkte FAC_IMPORT records blijven eeuwig bestaan
// TODO: Ooit saved files opruimen op basis van fac_import_backupdir
// Ook: *alle* oude FAC_IMP_FILE voor deze import gaan nu weg
var sql = "DELETE FROM fac_imp_file"
+ " WHERE fac_import_key IN"
+ " (SELECT fac_import_key"
+ " FROM fac_import"
+ " WHERE " + this_import_where + ")";
Oracle.Execute(sql);
// Tenslotte oude logging opruimen. De laatste laten we compleet staan,
// van de rest alleen de samenvatting regels.
var sql = "DELETE FROM imp_log"
+ " WHERE imp_log_status <> 'S'"
+ " AND fac_import_key IN"
+ " (SELECT fac_import_key"
+ " FROM fac_import"
+ " WHERE " + this_import_where + ")" // dezelfde soort
+ " AND fac_import_key < "
+ " (SELECT MAX(fac_import_key)"
+ " FROM fac_import"
+ " WHERE " + this_import_where + ")";
Oracle.Execute(sql);
var sql = "SELECT fac_s_fac_import_key.nextval FROM DUAL";
var oRs = Oracle.Execute(sql);
import_key = oRs(0).Value;
oRs.Close;
sql = "INSERT INTO fac_import"
+ " (fac_import_key,"
+ " fac_import_app_key,"
+ " fac_import_refkey,"
+ " fac_import_filenaam)"
+ " VALUES( " + import_key + ", "
+ import_app_key + ", "
+ (params.ref_key||"NULL") + ","
+ safe.quoted_sql(params.filepathname, 256) + ")";
Oracle.Execute(sql);
}
else
{ // Mocht een import-functie *echt* iets met de filenaam willen doen dan kan hij hem zelf opzoeken
// in fac_import. Voor multi-import slaan we daartoe de laatste filenaam op.
sql = "UPDATE fac_import"
+ " SET fac_import_filenaam = " + safe.quoted_sql(params.filepathname,256)
+ " WHERE fac_import_key = " + import_key;
Oracle.Execute(sql);
}
var sql = "SELECT i.fac_import_app_code "
+ ", i.fac_import_app_prefix "
+ ", i.fac_import_app_xsl "
+ ", i.fac_import_app_charset "
+ ", i.fac_import_app_action "
+ ", i.fac_import_app_folder" // voor eventuele backup
+ " FROM fac_import_app i"
+ " WHERE i.fac_import_app_key = " + import_app_key;
var oRs = Oracle.Execute(sql);
var import_app = oRs("fac_import_app_code").value;
var import_action = oRs("fac_import_app_action").value;
var procprefix = oRs("fac_import_app_prefix").value||"FAC";
var charset = oRs("fac_import_app_charset").value;
var xslfile = oRs("fac_import_app_xsl").value?params.fac_home + "CUST/" + params.customerId + "/" + oRs("fac_import_app_xsl").value:"";
var folder = oRs("fac_import_app_folder").Value;
oRs.Close();
var backDir = backupfolder(import_app, folder, import_key);
// Registeren (ook al doen we er verder nog niets mee)
sql = "UPDATE fac_import"
+ " SET fac_import_backupdir = " + safe.quoted_sql(backDir)
+ " WHERE fac_import_key = " + import_key;
Oracle.Execute(sql);
if (backDir && params.keep_backup)
{
var oZIP = new ActiveXObject("SLNKDWF.Zip");
oZIP.New(backDir + safe.filename(params.filepathname) + ".zip");
fileStream.Position = 0;
oZIP.ZipFromStream(params.filepathname, fileStream);
oZIP.Close();
}
if( !fileStream.Size ) // Leeg bestand?
{
return { success: false,
backDir: backDir, // Ongeldig bestand wel weg-moven
warning: "Import error: file is empty" };
}
// Altijd naar win1252 converteren, dat heeft onze database ook
fileStream.Position = 0;
fileStream.Type = 2; // adTypeText nu
fileStream.Charset = charset || 'Windows-1252'; // de bron
var win1252Stream = new ActiveXObject("ADODB.Stream");
win1252Stream.Open();
win1252Stream.CharSet = "Windows-1252"; // het doel
fileStream.copyTo(win1252Stream);
//win1252Stream.SaveToFile(Server.MapPath("./win1252Stream.txt"), 2);
if (xslfile)
{
// Zet xml om in string
__Log("XML2STR: with XSL: " + xslfile);
// Load the XML
var source = new ActiveXObject("MSXML2.DOMDocument.6.0");
source.async = false;
source.resolveExternals = false; // van mij hoef je geen DTD's te controleren
source.validateOnParse = false; // van mij hoef je geen DTD's te controleren
source.setProperty("ProhibitDTD", false); // niet moeilijk doen over DTD's
var fileData; // Hier gaan we de data als string in stoppen
win1252Stream.Position = 0
if( ! source.loadXML(win1252Stream.ReadText) ) // Het bestand laden
{
return { success: false,
backDir: backDir, // Ongeldig bestand wel weg-moven
warning: "XML error: " + source.parseError.reason + " @ " + source.parseError.line + "." + source.parseError.linepos };
}
// Load the XSLT
var style = new ActiveXObject("MSXML2.DOMDocument.6.0");
style.async = false;
style.resolveExternals = false; // van mij hoef je geen DTD's te controleren
style.validateOnParse = false; // van mij hoef je geen DTD's te controleren
style.setProperty("ProhibitDTD", false); // niet moeilijk doen over DTD's
if( ! style.load(xslfile) ) // De stylesheet laden
{
return { success: false,
// backDir: backDir, // het import bestand is gewoon geldig dus laten staan tot iemand de xsl-error oplost
warning: xslfile + "\nXSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos };
}
fileData = source.transformNode(style); // terugstoppen in de fileData string
// of?: xslproc.output = win1252Stream;
// xslproc.transform();
}
else {
win1252Stream.Position = 0;
fileData = win1252Stream.ReadText;
}
//
// fileData bevat nu de geimporteerde file. Wat gaan we er mee doen
// A) Naar Oracle (via table fac_imp_file) en daar laten verwerken
// B) Doorsturen naar SOAP service (want daar hebben we al afhandeling gebouwd)
//
var read_lines = -1;
if (import_action == 'ORACLE')
{
// data naar de database naar FAC_IMP_FILE
teller = 1;
var sqls = [];
//var fileDataArr = fileData.split(/[\r\n]/); // gaat mis bij CSV's met newline binnen quotes
var fileDataArr = fileData.split(/\x0D\x0A/); // CR LF is verplicht. Unix heeft alleen LF en dat ondersteunen we niet
// (alleen LF kan wel binnen quotes voor een newline in een veld) FSN#18597
if (fileDataArr.length == 1)
var fileDataArr = fileData.split(/\x0A/); // Misschien alleen UNIX LF's?
if (fileDataArr.length == 1)
var fileDataArr = fileData.split(/\x0D/); // Misschien alleen MAC CR's?
var sqllen = 0;
for (var regel in fileDataArr)
{
var line = fileDataArr[regel];
if (line != "")
{
sql = teller + ", " + safe.quoted_sql(line) + "," + import_key;
sqllen += sql.length + 28; // grove schatting
sqls.push(sql);
teller = teller + 1;
}
if (sqllen > 10000) // empirisch bepaald: fatsoenlijke performance
{
Oracle.Execute("INSERT INTO fac_imp_file"
+ " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
+ sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL");
sqls = [];
sqllen = 0;
}
}
if (sqls.length) // de laatste
{
Oracle.Execute("INSERT INTO fac_imp_file"
+ " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
+ sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL");
}
// De _IMPORT_ proc zal doorgaans de data van FAC_IMP_FILE naar bijv. FAC_IMP_CATALOGUS overzetten
sql = "BEGIN " + procprefix + "_IMPORT_" + import_app + "(" + import_key + "); END;"
Oracle.Execute(sql);
var read_lines = teller-1;
var summarytxt = "Ingelezen regels: " + read_lines;
}
else if (import_action.substr(0,5) == 'SOAP:') // Wordt gebruikt vanuit ge-e-mailde XML's
{ // JGL: FSN#25603, volgens mij sinds 2013 nooit in de productie gebruikt
var url = import_action.substring(5);
var http_request = new ActiveXObject("Microsoft.XMLHTTP");
//SXH_PROXY_SET_PROXY = 2;
//http_request.setProxy(SXH_PROXY_SET_PROXY, "127.0.0.1:8888"); // Fiddler
http_request.open("POST", url, false); // false zodat sync
http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8")
http_request.send(fileData);
if ( http_request.status != 200)
{
var txt = "Status: " + http_request.status
+ "\nStatusText: " + http_request.statusText
+ "\nResponseText " + http_request.responseText;
sql = "BEGIN fac.imp_writelog (" + import_key + ","
+ "'E',"
+ safe.quoted_sql(txt) + ", NULL);"
+ "END;"
Oracle.Execute(sql)
}
var summarytxt = "Via SOAP doorgestuurd.";
}
sql = "UPDATE fac_import SET"
+ " fac_import_datum_gelezen = SYSDATE"
+ (params.user_key > 0 ? ", prs_perslid_key = " + params.user_key : "")
+ " WHERE fac_import_key = " + import_key;
Oracle.Execute(sql);
var read_lines = teller-1;
sql = "BEGIN fac.imp_writelog (" + import_key + ","
+ "'S',"
+ safe.quoted_sql(summarytxt) + ", NULL);"
+ "END;"
Oracle.Execute(sql)
return { success: true, import_key: import_key, backDir: backDir, read_lines: read_lines }
}
// import_key bevat een te verwerken key
// params: customerId: "UWVA"
function impProcessStream(import_key, params)
{
var result = {message: "", success: false};
var sql = "SELECT ia.fac_import_app_key"
+ ", fac_import_app_code"
+ ", fac_import_app_prefix"
+ ", fac_import_app_folder"
+ ", fac_import_filenaam"
+ " FROM fac_import_app ia"
+ ", fac_import i"
+ " WHERE ia.fac_import_app_key = i.fac_import_app_key"
+ " AND i.fac_import_key = " + import_key;
var oRs = Oracle.Execute(sql);
var import_app_key = oRs("fac_import_app_key").Value;
var impcode = oRs("fac_import_app_code").Value;
var prefix = oRs("fac_import_app_prefix").Value||"FAC";
var filenaam = oRs("fac_import_filenaam").Value;
var folder = oRs("fac_import_app_folder").Value;
oRs.Close();
// Start de UPDATE procedure om de database aan te passen
sql = "BEGIN "
+ prefix + "_UPDATE_" + impcode
+ "(" + import_key + ");"
+ " END;"
Oracle.Execute(sql);
// Optioneel kan er nog klantspecifiek wat achteraan komen in geval van Catalogus-import
// Bijv. NFIB#21853
if (impcode == 'CATALOGUS')
{
var sql = "SELECT object_type"
+ " FROM user_objects"
+ " WHERE object_name = " + safe.quoted_sql(customerId + "_UPDATE_CATALOGUS");
var oRs = Oracle.Execute(sql);
if (!oRs.eof && oRs(0).Value == "PROCEDURE")
{ // Geldige aanwezig, roep aan
sql = "BEGIN "
+ customerId + "_UPDATE_CATALOGUS"
+ "(" + import_key + ");"
+ " END;"
Oracle.Execute(sql);
}
oRs.Close();
}
// Nu kijken of we nog iets met flexbijlagen moeten doen
//
// LET OP(?): Vanuit gen_import.wsf hebben we geen S() noch allerlei
// andere functies die we hier gebruiken!!
// Dit werkt dus (vooralsnog) alleen vanuit de GUI en via SOAP!!
//
var flexcode = null;
var sql = "SELECT fac_result_waarde"
+ " FROM fac_result"
+ " WHERE fac_result_naam = 'flexcode'"
+ " AND fac_result_sessionid = 'IMPORT:{0}'".format(import_key);
var oRs = Oracle.Execute(sql);
if (!oRs.Eof)
{ // melding S undefined? We ondersteunen dit nog niet vanuit gen_import.wsf
flexcode = oRs("fac_result_waarde").Value;
}
oRs.Close();
if (flexcode) // FIN:12345:18:F (12345=factuurkey, 18=kenmerkkey, F=factuur (versus R=regels)
{
if (!folder)
abort_with_warning("IMPORT CONFIGURATION ERROR:\nflexcode result but FAC_IMPORT_APP_FOLDER is not set");
if (!this.flexProps)
INTERNAL_ERROR_FLEXCODE; // Niet ondersteund vanuit gen_import.wsf
var backDir = backupfolder(impcode, folder, import_key);
var res = extractAttachments(backDir + safe.filename(filenaam) + ".zip", safe.filename(filenaam), flexcode);
if (!res.success)
return res;
}
sql = "UPDATE fac_import"
+ " SET fac_import_datum_verwerkt = SYSDATE"
+ (params.user_key > 0 ? ", prs_perslid_key = " + params.user_key : "")
+ " WHERE fac_import_key = " + import_key
+ " AND fac_import_datum_verwerkt IS NULL";
Oracle.Execute(sql);
// Van elke import bewaren we in IMP_FILE maximaal één verwerkte
// import (en maximaal één *niet verwerkte*)
// Aangezien de onze net verwerkt is gooien we de vorige weg
sql = "DELETE FROM fac_imp_file"
+ " WHERE fac_import_key IN"
+ " (SELECT ander.fac_import_key"
+ " FROM fac_import ander"
+ " WHERE ander.fac_import_app_key = " + import_app_key
+ " AND ander.fac_import_key <> " + import_key
+ " )";
Oracle.Execute(sql);
// Tenslotte hele fac_import (en automatisch bijbehorende S logging) opruimen.
// De laatste 10 laten we altijd compleet staan, evenals alles van de laatste week
var sql = "DELETE fac_import fi1"
+ " WHERE fi1.fac_import_app_key = " + import_app_key
+ " AND fac_import_datum_verwerkt < SYSDATE - 7" // Laatste week blijft
+ " AND fac_import_key < (SELECT MIN(fac_import_key)"
+ " FROM ( SELECT fac_import_key"
+ " FROM fac_import fi2"
+ " WHERE fi2.fac_import_app_key = " + import_app_key
+ " ORDER BY fac_import_key DESC)"
+ " WHERE ROWNUM <= 10)"; // Laatste 10 blijven
Oracle.Execute(sql);
result.message = (typeof L != "undefined"?L("lcl_imp_inf_processed"):"Ingelezen gegevens zijn verwerkt"); // Geen L vanuit gen_import.wsf!
result.success = true;
return result;
}
function backupfolder(import_app, folder, import_key)
{
if (!folder)
return null;
// <folder>\BACKUP<jaar>-<maand>\<import_app>\<fulldate>_<import_key>
var fso = new ActiveXObject("Scripting.FileSystemObject");
var jsDate = new Date();
folder = folder.replace("/", "\\");
if (folder.substr(folder.length-1) != "\\")
folder += "\\";
var backDir = folder + "BACKUP" + jsDate.getFullYear() + "-" + padout(jsDate.getMonth() + 1) + "\\";
if (!fso.FolderExists(backDir))
fso.CreateFolder(backDir);
backDir += import_app + "\\";
if (!fso.FolderExists(backDir))
fso.CreateFolder(backDir);
var dtstring = padout(jsDate.getFullYear()) + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getDate());
backDir += dtstring + "_" + import_key + "\\";
if (!fso.FolderExists(backDir))
fso.CreateFolder(backDir);
return backDir;
}
function XMLval(xml, tag)
{
if (!xml)
return null;
var xx = xml.getElementsByTagName(tag);
if (!xx || !xx.length)
return null;
xx = xx[0].childNodes
if (!xx || !xx.length)
return null;
return xx[0].nodeValue;
}
function extractAttachments(safeSourceXML, filename, flexcode)
{
__Log("Attachments uit {0} extraheren en opslaan volgens {1}".format(safeSourceXML, flexcode));
var arr = flexcode.split(":"); // FIN:12345:18:F
var pModule = arr[0];
if (arr.length > 1)
var pKey = parseInt(arr[1], 10);
if (arr.length > 2)
var pKenmerkKey = parseInt(arr[2], 10);
if (arr.length > 3)
var pNiveau = parseInt(arr[3], 10);
var params = flexProps(pModule, pKey, pKenmerkKey, pNiveau);
//fileStream.CharSet = charset; // Vroeg, anders krijgen we een BOM?
var oStream = new ActiveXObject("ADODB.Stream");
oStream.Type = 1; //adTypeBinary
oStream.Open;
var oZIP = new ActiveXObject("SLNKDWF.Zip");
oZIP.Open(safeSourceXML);
oZIP.UnzipToStream(oStream, filename);
var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
xmldoc.async = false;
xmldoc.resolveExternals = false; // van mij hoef je geen DTD's te controleren
xmldoc.validateOnParse = false; // van mij hoef je geen DTD's te controleren
xmldoc.setProperty("ProhibitDTD", false); // niet moeilijk doen over DTD's
oStream.Position = 0;
if( ! xmldoc.load(oStream) ) // De XML terug laden
{
return { success: false, warning: filename + "\nXML error: " + xmldoc.parseError.reason + " @ " + xmldoc.parseError.line + "." + xmldoc.parseError.linepos };
}
oZIP.Close();
oStream.Close();
//
// Dit is het enige formaat wat we (hardcoded) ondersteunen. DocumentType en FileType negeren we
// <Attachment>
// <AttachedData>JVBERi0xLjQKJcfsjCi9<....></AttachedData>
// <DocumentType>FAC</DocumentType>
// <FileType>PDF</FileType>
// <FileName>0123456789123_201403159.pdf</FileName>
// </Attachment>
var Attachments = xmldoc.getElementsByTagName("Attachment");
for (var i=0; i < Attachments.length; i++)
{
var safefilename = safe.filename(XMLval(Attachments[i], "FileName"));
if (!params.isAllowedName(safefilename))
{
// TODO: Misschien ook terugkoppelen in IMP_LOG?
__Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
}
else
{
__Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
var AttachmentData = XMLval(Attachments[i], "AttachedData");
if (AttachmentData)
{
CreateFullPath(params.AttachPath);
encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
__Log("Done saving: " + params.AttachPath + safefilename);
}
else
__Log("Empty file skipped: " + safefilename);
}
}
return { success: true };
}