<?xml version="1.0"?>
<component>
<?component error="true" debug="true"?>
<registration
    description="custfunctions"
    progid="custfunction.wsc"
    version="1.00"
    classid="{C068EBA0-5AA9-438C-AD4F-8F909E5787C6}"
>
</registration>

<public>

<!-- properties -->
    <property name="Oracle">
        <put/>
    </property>
    <property name="__Log">
        <put/>
    </property>
    <property name="__DoLog">
        <put/>
    </property>
    <property name="safe">
        <put/>
    </property>

<!-- methods -->
    <method name="mld_urenpostsave">
        <PARAMETER name="prskey_array"/>
        <PARAMETER name="datumarray"/>
        <PARAMETER name="pResult"/>
    </method>
    <method name="mld_urenkostenpostsave">
        <PARAMETER name="p_mld_opdruren_kosten_key"/>
        <PARAMETER name="p_mld_opdruren_key"/>
        <PARAMETER name="p_kosten_key"/>
        <PARAMETER name="p_kosten"/>
        <PARAMETER name="p_actie"/>
        <PARAMETER name="pResult"/>
    </method>
    <method name="fclt_calc_dist_from_google">
        <PARAMETER name="p_prs_perslid_key"/>
        <PARAMETER name="p_mld_opdr_uren_datum"/>
        <PARAMETER name="p_actie"/>
    </method>
</public>

<script language="javascript">
<![CDATA[

/*  properties  */
function put_Oracle(newValue)   {Oracle     = newValue};
function put___Log(newValue)    {__Log      = newValue};
function put___DoLog(newValue)  {__DoLog    = newValue};
function put_safe(newValue)     {safe       = newValue};

/*  methods */
/*
    $Revision$
    $Id$

    File:   CARE custfunction.wsc

*/

/* Dit zijn de enige drie functies die je mag gebruiken */
var Oracle;
var __Log;
var __DoLog;
var safe;

care_km_cache_days = 56; // we kunnen niet bij S() dus maar hard
function padout(number) { return (number < 10) ? "0" + number : number; }
Date.prototype.toSQL = function toSQL(withTime)
{
    var str = padout(this.getDate()) + "-" + padout(this.getMonth() + 1) + "-" + padout(this.getFullYear())
    if (withTime)
    {
        str += " " + padout(this.getHours()) + ":" + padout(this.getMinutes());
        return "to_date(" + safe.quoted_sql(str) + ", 'DD-MM-YYYY HH24:MI')";
    }
    else
        return "to_date(" + safe.quoted_sql(str) + ", 'DD-MM-YYYY')";
}

// CARE: doorloop de week van deze persoon en bepaal de dagtotaalkilometers
// om op te slaan in mld_opdruren_dagkosten

//mld_urenpostsave = function (prskey_array, datumarray, pResult) {
mld_urenpostsave = function (p_prs_key, p_datum, p_opdr_key, p_impropdr_key, p_actie, presult){
    __Log('HOOKFUNCTION: mld_urenpostsave');
    __Log('HOOKFUNCTION parameter p_prs_key='+p_prs_key);
    __Log('HOOKFUNCTION parameter p_datum='+p_datum);
    __Log('HOOKFUNCTION parameter p_opdr_key='+p_opdr_key);
    __Log('HOOKFUNCTION parameter p_impropdr_key='+p_impropdr_key);
    __Log('HOOKFUNCTION parameter p_actie='+p_actie);

    if (p_actie == "DD"){
        //Alleen bij verwijderen van uren waarbij kosten zaten, gaan we Google opnieuw raadplegen
        l_result = fclt_calc_dist_from_google (p_prs_key, p_datum, "D");
    }
    return true;
  }

//mld_urenpost2save = function (p_prskey, p_datumfrom, presult) {
//    Log('NEW HOOKFUNCTION: mld_urenpostsave');
//    Log('HOOKFUNCTION parameter p_prskey='+p_prskey);
//    Log('HOOKFUNCTION parameter p_datumfrom='+p_datumfrom);
//    return true;
//}


mld_urenkostenpostsave = function (p_mld_opdruren_kosten_key, p_mld_opdruren_key, p_kosten_key, p_kosten, p_actie, pResult){
    __Log('HOOKFUNCTION: mld_urenKOSTENpostsave');
    __Log('HOOKFUNCTION parameter p_mld_opdruren_kosten_key='+p_mld_opdruren_kosten_key);
    __Log('HOOKFUNCTION parameter p_mld_opdruren_key='+p_mld_opdruren_kosten_key);
    __Log('HOOKFUNCTION parameter p_kosten_key='+p_kosten_key);
    __Log('HOOKFUNCTION parameter p_kosten='+p_kosten);
    __Log('HOOKFUNCTION parameter p_actie='+p_actie);

    var sql;
    var oRs;


    if (p_kosten_key == 1 || p_kosten_key == 2 || p_kosten_key == 3) // auto/bromfiets/fiets
    {

        sql=" select prs_perslid_key, mld_opdr_uren_datum" +
            " from mld_opdr_uren" +
            " where mld_opdr_uren_key = " + p_mld_opdruren_key;
        oRs = Oracle.Execute(sql);
        var l_prs_perslid_key = oRs("prs_perslid_key").Value;
        var l_mld_opdr_uren_datum = new Date(oRs("mld_opdr_uren_datum").Value);
        oRs.Close();

        l_result = fclt_calc_dist_from_google (l_prs_perslid_key, l_mld_opdr_uren_datum, p_actie);

    }
    else {
    // Andere kosten dan kilometers / fiets / bromfiets: doen we niets mee.
    }

    return true;
}


// Actie p_actie = I, U of D (insert/update/delete), andere codes werken niet (doen niets)
// p_prs_perslid_key is de key van de verzorger (route van verzorger)
// p_mld_opdr_uren_datum is de datum behorende bij de route (dag van de route)
fclt_calc_dist_from_google = function (p_prs_perslid_key, p_mld_opdr_uren_datum, p_actie)
{
    p_mld_opdr_uren_datum = new Date(p_mld_opdr_uren_datum); // is binnen wsc anders een object?
    // URL voor het berekenen van de dag-route
    var oRs = Oracle.Execute("SELECT  COALESCE(fac_setting_pvalue, fac_setting_default) fac_setting_value FROM fac_setting WHERE fac_setting_name = 'fg_maps_distance_apikey'");
    var v_GoogleAPI_URL = "https://maps.googleapis.com/maps/api/directions/xml?key="+ oRs("fac_setting_value").Value;
    oRs.Close();

    // URL tbv het tonen van de dag-route
    var v_url_google_maps = "https://maps.google.nl/maps";

    var sql;
    var oRs;

    if (p_actie == "U") {
        // Een update van kilometer is niet interessant om de kilometers opnieuw te berekenen, dus hier doen we dan niets!!
        // NOOP
        __Log("NOOP");
    }
    else{
        if (p_actie == "I" || p_actie == "D") {
            //  Er zijn kilometers op de dag bijgeschreven (insert) of verwijderd (delete), dus de route opnieuw berekenen...
            // Daartoe eerst de clienten verzamelen die VERZORGER op de DATUM met auto heeft bezocht.
            //var sql="select verzorger, datum, adres, huisnr, woonplaats, prs_client_naam, prs_client_voorletters, client_adres, client_huisnr, client_woonplaats from care_v_km_weekbrief" +
            //        " where prs_perslid_key = "+ p_prs_perslid_key +
            //        " and datum =" + p_mld_opdr_uren_datum.toSQL() +
            //        " order by client_woonplaats";
            // care_v_km_weekbrief bevat alleen auto-kilometers. Daarom view hieronder uitgeschreven
            var sql = "SELECT v.prs_perslid_key,"
                    + "       v.prs_verzorger_naam || ',' || v.prs_verzorger_voorletters || ' ' || v.prs_verzorger_tussenvoegsel verzorger,"
                    + "       v.verzorger_adres adres,"
                    + "       v.verzorger_huisnr huisnr,"
                    + "       v.verzorger_postcode,"
                    + "       v.verzorger_woonplaats woonplaats,"
                    + "       uur.datum_gewerkt datum,"
                    + "       c.prs_client_naam,"
                    + "       c.prs_client_tussenvoegsel,"
                    + "       c.prs_client_voorletters,"
                    + "       c.client_adres,"
                    + "       c.client_huisnr,"
                    + "       c.client_postcode,"
                    + "       c.client_woonplaats"
                    + "  FROM care_v_totaal_uren_all uur, care_v_verzorger v, mld_opdruren_kosten ouk, mld_kosten k, mld_opdr o, mld_melding m, care_v_client c"
                    + " WHERE uur.prs_perslid_key = v.prs_perslid_key"
                    + "   AND uur.mld_opdr_uren_key = ouk.mld_opdr_uren_key"
                    + "   AND ouk.mld_kosten_key = k.mld_kosten_key"
                    + "   AND k.mld_kosten_verwijder IS NULL"
                    // niet alleen auto's + "   AND   k.mld_kosten_code = 'A'"
                    + "   AND opdracht_type = 'P'"
                    + "   AND uur.opdracht_key=o.mld_opdr_key"
                    + "   AND o.mld_melding_key = m.mld_melding_key"
                    + "   AND m.prs_perslid_key = c.prs_client_key"
                    + "   AND v.prs_perslid_key = " + p_prs_perslid_key
                    + "   AND uur.datum_gewerkt= " + p_mld_opdr_uren_datum.toSQL()
                    + " ORDER BY client_woonplaats";
            oRs = Oracle.Execute(sql);

            if (!oRs.Eof)
            {
                // Er is minimaal 1 client waar verzorger naar toe gaat op de dag, dus het record mld_opdruren_dagkosten inserten (nog niet aanwezig) of updaten (indien aanwezig)
                // Laten we eerst Google Maps maar eens vragen naar het aantal kilometers...

                __Log("Berekening op:" + oRs("datum").Value + " voor: " + oRs("verzorger").Value);
                var l_huis=oRs("adres").Value + " " + oRs("huisnr").Value + ", " + oRs("verzorger_postcode").Value + " " + oRs("woonplaats").Value;

                var v_url_google_huis1 = "?saddr=" + l_huis;
                var v_url_google_huis2 = "+to:" + l_huis;
                var v_url_google_route = "";
                __Log ("Huis van verzorger: "+ l_huis);
                var p_adresARR = new Array();
                while (!oRs.eof)
                {   var l_client_huis = oRs("client_adres").Value + " " + oRs("client_huisnr").Value + ", " + oRs("client_postcode").Value + " " + oRs("client_woonplaats").Value;
                    __Log ("Huis van client: " + l_client_huis);
                    if (p_adresARR.length == 0){
                        // Eerste keer: daddr-argument in de URL!!
                        v_url_google_route += "&daddr=" + encodeURIComponent(l_client_huis);
                    }
                    else{
                        // Volgende keren: :to in het daddr-argument (van de URL)!!!
                        v_url_google_route += "+to:" + encodeURIComponent(l_client_huis);
                    }
                    p_adresARR[p_adresARR.length]= l_client_huis;
                    oRs.MoveNext();
                }
                var v_db_url_google = v_url_google_maps + v_url_google_huis1 + v_url_google_route + v_url_google_huis2 + "&hl=nl";

                // We proberen eerst de afstand uit de "cache" van maximaal 4 weken geleden te halen, zodat we niet van Google gebruik hoeven te maken.
                __Log ("CACHING - Aantal dagen in het verleden: care_km_cache_days: " + care_km_cache_days);
                var cached_OracleDistance = Get_Cached_OracleDistance(v_db_url_google, care_km_cache_days);
                var l_afst = cached_OracleDistance.dagkosten;
                var l_afstinter = cached_OracleDistance.inter_afstand;
                // BONS#78434: 0 km gecached maar wel meerdere clienten is verdacht ==> opnieuw berekenen
                if (l_afst == -1 || l_afstinter == -1 || (l_afstinter == 0 && p_adresARR.length > 1))
                {
                    // Er is geen cache km in DB gevonden, laat Google maar eens rekenen...

                    // Return-codes van Get_OPTIMIZED_GoogleDistance:
                    // -2: Communicatiefout/connectieprobleem Google-server/internet
                    // -1: Een van de adressen (verzorger of client) is onbekend/kan niet door Google gevonden worden
                    // 0: Afstand = 0 km
                    // > 0: Aantal meters dat volgens Google gereden kan worden (optimaal).
                    l_afst = Get_OPTIMIZED_GoogleDistance(v_GoogleAPI_URL, l_huis, l_huis, p_adresARR, "");
                    __Log ("Google-afstand:" + l_afst + " meter");
                    l_afstinter = 0;
                    if (p_adresARR.length > 1)
                    {
                        l_afstinter = Get_interclient_GoogleDistance(v_GoogleAPI_URL, l_huis, l_huis, p_adresARR, "");
                        __Log ("Google-afstand interclient:" + l_afstinter + " meter");
                    }
                }
                else if (p_adresARR.length < 2)
                {
                    l_afstinter = 0; // is in het verleden wel eens foutief gecached
                }

                var l_route = l_huis + " - " + v_url_google_route + " - " + l_huis;

                l_route= l_route.replace('+to:' , ' - ');
                l_route= l_route.replace('&daddr=' , ' - ');

                sql=" select mld_opdruren_dagkosten_key" +
                    " from mld_opdruren_dagkosten" +
                    " where prs_perslid_key = "+ p_prs_perslid_key +
                    " and mld_opdruren_dagkosten_datum =" + p_mld_opdr_uren_datum.toSQL();
                var oRs2 = Oracle.Execute(sql);

                if (!oRs2.Eof)
                {
                    // Registratie van dagtotaal bestaat al, dus een update...
                    // Hier een kilometer-berekening vanuit Google...
                    // Indien l_afst >= 0, dan een geldige afstandsberekening, dan is een van de adressen (verzorger/clienten) niet in orde, althans Google kan ze niet vinden
                    // agv b.v. spelfout in straatnaam is dan zeer waarschijnlijk.
                    // De kilometers worden op -1 (negatief getal) gezet (ongeldig cq. onbepaald).
                    // Via een rapport met filter hierop kan er door ADMIN van de CARE-klant een correcie op adresgegevens worden gedaan....
                    sql = " UPDATE mld_opdruren_dagkosten"
                        + "    SET mld_opdruren_dagkosten_totaal = " + l_afst
                        + "      , mld_opdruren_dagkosten_opm    = " + safe.quoted_sql(v_db_url_google + "#" + l_afstinter)
                        + "      , mld_opdruren_dagkosten_aantal = " + p_adresARR.length
                        + "  WHERE mld_opdruren_dagkosten_key = " + oRs2("mld_opdruren_dagkosten_key").Value;
                }
                else
                {
                    // Registratie van dagtotaal bestaat nog niet, dus een insert..
                    sql  = "INSERT INTO mld_opdruren_dagkosten"
                         + "( prs_perslid_key"
                         + ", mld_opdruren_dagkosten_datum"
                         + ", mld_opdruren_dagkosten_aantal"
                         + ", mld_opdruren_dagkosten_totaal"
                         + ", mld_opdruren_dagkosten_opm"
                         + ")"
                         + " VALUES "
                         + "( " + p_prs_perslid_key
                         + ", " + p_mld_opdr_uren_datum.toSQL()
                         + ", " + p_adresARR.length
                         + ", " + l_afst
                         + ", " + safe.quoted_sql(v_db_url_google + "#" + l_afstinter)
                         + ")";
                }
                oRs2.Close();
            }
            else
            {
                // Er zijn geen clienten meer die verzorger op die dag met de auto bezocht heeft...
                // Dat betekent een simpele DELETE van het record mld_opdruren_dagkosten
                // Als het goed is, moet de p_actie gelijk aan "D" zijn (er is een delete gedaan van de laatste kilometers op die dag
                // hetzij direct de kilometers wissen of indirect, via een delete van de uren van die dag (die dan bijbehorende kilometers had).
                sql = " DELETE FROM mld_opdruren_dagkosten"
                    + "  WHERE prs_perslid_key = " + p_prs_perslid_key
                    + "    AND mld_opdruren_dagkosten_datum = " + p_mld_opdr_uren_datum.toSQL();
            }
            oRs.Close();
            // Hier volgt een INSERT, UPDATE of DELETE van mld_opdruren_dagkosten...
            Oracle.Execute(sql);
        }
    }
    return true;
}


// Parameters:
// p_db_url_google: exacte Google-URL voor een dagroute, indien deze tussen nu en p_aant_dagen geleden gevonden, dan levert deze functie het daarbij bijbehorende aantal kilometers op.
// p_aant_dagen: integer >=0, indien 0, dan geen limiet, en anders zoeken tot zoveel dagen terug
// JGL Ik zou verwachten dat elke p_aant_dagen de kilometers opnieuw worden berekend
//     maar het is zo dat als binnen p_aant_dagen de cache wordt gebruikt de waarde nog
//     p_aant_dagen dagen langer gebruikt wordt. Een regelmatige route wordt effectief *nooit*
//     opnieuw berekend. Maar ach....
function Get_Cached_OracleDistance(p_db_url_google, p_aant_dagen)
{
    var l_result = { dagkosten: -1      // Initieel -1, d.w.z. geen afstand in cache gevonden...
                   , inter_afstand: -1  // Deze route is nog niet bekend.
                   };

    var wildurl = p_db_url_google.replace(/_/g, '\'_'); // Escape de underscore
    wildurl = p_db_url_google.replace(/\%/g, '\\%'); // Escape de procent

    var sql = "SELECT mld_opdruren_dagkosten_totaal"
            + "     , mld_opdruren_dagkosten_datum"
            + "     , mld_opdruren_dagkosten_opm"
            + "     , prs_perslid_key"
            + "  FROM mld_opdruren_dagkosten"
            + " WHERE mld_opdruren_dagkosten_opm LIKE " + safe.quoted_sql(wildurl + "#%") + " ESCAPE '\\'"
            + "   AND mld_opdruren_dagkosten_totaal is not null"
            + "   AND mld_opdruren_dagkosten_totaal >= 0"
            + (p_aant_dagen > 0 ? " AND TRUNC(mld_opdruren_dagkosten_datum) >= TRUNC(SYSDATE - " + p_aant_dagen + ")" : "")
            + " ORDER BY mld_opdruren_dagkosten_datum DESC";
    var oRs = Oracle.Execute(sql);
    if (!oRs.Eof)
    {
        // Er bestaat minimaal 1 gecach-te kilometer aantal
        // Pak dan de laatste berekende/ingevulde stand...

        l_result.dagkosten = oRs("mld_opdruren_dagkosten_totaal").Value;

        var opm = oRs("mld_opdruren_dagkosten_opm").Value;
        var afst = opm.substr(opm.indexOf("#")+1);
        if (!isNaN(afst))
            l_result.inter_afstand = afst;

        __Log("----");
        __Log("Kilometers uit cache");
        __Log("Perslid_key:" + oRs("prs_perslid_key").Value);
        __Log("Datum:" + oRs("mld_opdruren_dagkosten_datum").Value);
        __Log("Afstand (meters):" + oRs("mld_opdruren_dagkosten_totaal").Value);
        __Log("Afstand inter-client (meters):" + l_result.inter_afstand);
    }
    oRs.Close();
    return (l_result);
}

// Parameters:
// p_locatie1 is start-node
// p_locatie2 is eind-node
// p_AdresArr zijn tussennodes
// p_output_xmlfile is logbestand xml, iniden "" dan geen logging, anders result-xml vanuit Google loggen

// Return-codes:
// -2: Communicatiefout/connectieprobleem Google-server/internet
// -1: Een van de adressen (verzorger of client) is onbekend/kan niet door Google gevonden worden
// 0: Afstand = 0 km
// > 0: Aantal meters dat volgens Google gereden kan worden (optimaal).
function Get_OPTIMIZED_GoogleDistance(p_GoogleAPI_URL, p_locatie1, p_locatie2, p_AdresArr, p_output_xmlfile){
   if (p_AdresArr.length == 0){
      l_result = 0}
   else{
      l_result = GetGoogleDistance(p_GoogleAPI_URL, p_locatie1, p_locatie2, p_AdresArr, 1, p_output_xmlfile);
   }
   return (l_result);
}

// Get_interclient_GoogleDistance: als minstens twee klanten dan de afstand van die route
// (zonder aanrijd/ terugrijd km van/naar huis)
// Return-codes:
// -2: Communicatiefout/connectieprobleem Google-server/internet
// -1: Een van de adressen (verzorger of client) is onbekend/kan niet door Google gevonden worden
// 0: Afstand = 0 km
// > 0: Aantal meters dat volgens Google gereden kan worden (optimaal).
function Get_interclient_GoogleDistance(p_GoogleAPI_URL, p_locatie1, p_locatie2, p_AdresArr, p_output_xmlfile)
{
   if (p_AdresArr.length < 2)
   {
      l_result = 0
   }
   else
   {
      var eerste = p_AdresArr.shift();
      var laatste = p_AdresArr.pop();
      // Als er precies twee clienten waren is p_AdresArr nu leeg maar dat maakt niet uit
      l_result = GetGoogleDistance(p_GoogleAPI_URL, eerste, laatste, p_AdresArr, 1, p_output_xmlfile);
   }
   return (l_result);
}


// Haal de XML van URL p_GoogleAPI_URL met p_locatie1 (=huis) en p_locatie2 (=huis), en een array van tussennodes (clienten)
// en sla op in bestand p_output_xmlfile (indien != "", als p_output_xmlfile =="" dan geen logging)
// Parameter optimize = 1 gaat Google vertellen om van de nodes de meest optimale route qua minst aantal km te berekenen).

function GetGoogleDistance(p_GoogleAPI_URL, p_locatie1, p_locatie2, p_AdresArr, p_optimize, p_output_xmlfile)
{
    __Log({ from: p_locatie1, to: p_locatie2, waypoints: p_AdresArr });
    var objXMLHTTP = new ActiveXObject("MSXML2.ServerXMLHTTP.6.0");
    l_waypoints = p_AdresArr.join("|");
    if (p_optimize == 1){
      l_waypoints = "optimize:true|" + l_waypoints;
    }
     __Log(l_waypoints);
    var l_GoogleAPI_URL = p_GoogleAPI_URL
                         + '&origin=' + encodeURIComponent(p_locatie1)
                         + '&destination=' + encodeURIComponent(p_locatie2)
                         + '&waypoints=' + encodeURIComponent(l_waypoints)
    objXMLHTTP.open("GET", l_GoogleAPI_URL, false);
    objXMLHTTP.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    objXMLHTTP.send();
     __Log("HTTP-status: " + objXMLHTTP.status);
    if (objXMLHTTP.status==200)
    {   // Geldigheid XML wordt gecontroleerd door GEN_IMPORT
         __Log("Gelukt, status = " + objXMLHTTP.status);
        if (p_output_xmlfile != ""){
           oStream = new ActiveXObject("ADODB.Stream");
           oStream.Open();
           oStream.Type = 1; // adTypeBinary
           oStream.Write(objXMLHTTP.responseBody); // responseText geeft encoding problemen!
           oStream.SaveToFile (p_output_xmlfile, 2); // adSaveCreateOverWrite
           oStream.Close();
        }
        var xmlnode;
        var l_result = 0;

        xmlResp = objXMLHTTP.responseXML;

        statusnode = "DirectionsResponse/status";
        l_status = xmlResp.selectSingleNode(statusnode);
        if (l_status.text == "OK"){
            for (var i=0; i<= p_AdresArr.length;i++){
                  xmlnode = "DirectionsResponse/route/leg[" +  (i + 1) +  "]/distance/value";
                l_dist = xmlResp.selectSingleNode(xmlnode);
                l_result = l_result + parseInt(l_dist.text);
              }
            }
          else {
            __DoLog("Maps aanroep faalde met " + l_status.text, "#FF00FF");
            __DoLog(l_GoogleAPI_URL);
            __DoLog(xmlResp.xml);
            __DoLog({ from: p_locatie1, to: p_locatie2, waypoints: p_AdresArr });
            l_result = -1;
          }
            return (l_result);
    }
    else
    {
         __Log("Mislukt, status = " + objXMLHTTP.status);
         __Log(objXMLHTTP.statusText);
        return(-2);
    }
}

]]>
</script>
</component>

