HEX
Server: Apache/2.4.41 (Ubuntu)
System: Linux ip-172-31-42-149 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 07:00:04 UTC 2025 aarch64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/vhost/disk-apps/agile-selling-wpb/resources/views/route_assistant/route_planning.blade.php
@extends('modules.head')
@section('contenido')
@include('route_assistant.modal_new_delivery')
@include('modules.modal_loading')

<div class="box">
    <div class="box-header with-border">
        <h3 class="box-title">
            @lang('messages.route_simulation')
        </h3>
        <div class="row">
            <div class="col-6 col-md-6">
                    <select id="current_sucursal" class="form-control">
                            @foreach ($sucursals as $s)
                            @if(Auth::user()->userInfo->sucursal_id == $s->id)
                            <option value="{{$s->id}}" data-lat="{{$s->address->lat}}" data-lng="{{$s->address->long}}" selected="selected">{{$s->code}} - {{$s->name}}</option>
                            @else
                            <option value="{{$s->id}}" data-lat="{{$s->address->lat}}" data-lng="{{$s->address->long}}">{{$s->code}} - {{$s->name}}</option>
                            @endif
                            @endforeach
                        </select>

            </div>
            <div class="col-3 col-md-3">
                    <button class="btn btn-default btnBox pull-right" onclick="planningRoute()">
                            <i class="fa fa-map-signs"></i>
                            @lang('messages.simulate_route')
                        </button>
            </div>
            <div class="col-3 col-md-3">
                    <button class="btn btn-default btnBox pull-right" onclick="onTakeAndAssign()">
                            <i class="fa fa-check"></i>
                            @lang('messages.take_and_simulate_route')
                        </button>
            </div>

        </div>





    </div>
    <div class="box-body" style="height: 300px; overflow: scroll;">
        <div class="row">
            <div class="col-sm-6">
                <table class="table table-striped table-responsive" style="max-height:100px;" id="pending_orders">
                    <thead>
                        <h4 style="display: inline;">@lang('messages.pending_orders')</h4>
                        <tr>
                            <th width="8px"><button class="btn btn-default btnBox pull-left" style="font-size: 10px;" onclick="selectAllOrders(this)">Todos</button></th>
                            <th>@lang('messages.code')</th>
                            <th>@lang('messages.address')</th>
                            <th style="display: none;" >Lat</th>
                            <th style="display: none;">Lng</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach($orders as $o)
                        <tr>
                            <td>
                                <div style="margin-left: 30%;">
                                    <input type="checkbox" id="order_check{{$loop->iteration}}">
                                    <label for="order_check{{$loop->iteration}}"></label>
                                </div>
                            </td>
                            <td>{{$o->code}}</td>
                            <td>{{$o->address->direction}}@if($o->transferences->count() > 0 )  <strong> ({{ $o->transferences->count() }}) Transferencia</strong> @endif </td>
                            <td style="display: none;">{{$o->address->lat}}</td>
                            <td style="display: none;">{{$o->address->long}}</td>
                            <td style="display: none;">{{$o->transferences}}</td>
                        </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
            <div class="col-sm-6">
                <table class="table table-striped table-responsive" style="max-height:100px;" id="delivery_availables">
                    <thead>
                        <tr>
                            <h4 style="display: inline;">
                                @lang('messages.dealers')
                                <button type="button" class="btn pull-right" id="btn-open-modal-direction" style="font-size: 12px; padding: 0px" data-toggle="modal"
                                data-target="#modal_new_delivery">
                                <i class="fa fa-plus"></i>
                                Repartidor externo
                            </button>
                        </h4>
                        <th width="8px"><button class="btn btn-default btnBox pull-left" style="font-size: 10px;" onclick="selectAllDeliveries(this)">Todos</button></th>
                        <th>@lang('messages.name')</th>
                        <th>@lang('messages.company')</th>
                        <th>@lang('messages.phone')</th>
                        <th style="display: none;" >ID</th>
                    </tr>
                </thead>
                <tbody id="tbody_delivery">
                    @foreach($deliveries as $d)
                    <tr>
                        <td>
                            <div style="margin-left: 30%; margin-top: 20px;">
                                <input type="checkbox" class="custom-control-input" id="delivery_check{{$loop->iteration}}">
                                <label class="custom-control-label" for="delivery_check{{$loop->iteration}}"></label>
                            </div>
                        </td>
                        <td><img src="{{ asset('img/no_image2.png') }}" style="display: inline;" width="50px" alt="Producto 1"> {{$d->first_name}}</td>
                        <td style="padding-top: 20px;">{{$d->userInfo->company->name}}</td>
                        <td style="padding-top: 20px;">{{$d->phone}}</td>
                        <td style="display: none;">{{$d->id}}</td>
                    </tr>
                    @endforeach
                </tbody>
            </table>
        </div>
    </div>

</div>
</div>

<div class="maps" id="map_zone">

</div>
@endsection

<script>
    function onTakeAndAssign(){
        var routes = window.finalRoute,
        deliveries = window.finalDeliveries;

        if(!routes || !deliveries){
            swal("Por favor realice al menos una simulación para continuar.", "", "error");
            return;
        }

        swal("Desea tomar la ruta y realizar la asignación de los pedidos ?", {
            buttons: {
                cancel: "No.",
                si: {
                    text: "Si, aceptar",
                    value: "si",
                }
            },
        })
        .then((value) => {
            switch (value) {

                case "si":
                makeAssign(routes, deliveries);
                break;

                default:
                swal("Se ha cancelado el proceso.");
            }
        });
    }

    function makeAssign(routes, deliveries){
        swal("Se ha realizado la asignación correctamente.", "", "success");

        // Ajax con las rutas y los mensajeros.
        $.when(sendToServerAssign(routes, deliveries)).done(function(data, textStatus, jqXHR){

        });

    }

    function sendToServerAssign(routes, deliveries){
        var data = {
            routes: routes,
            deliveries: deliveries,
        };

        return  $.ajax({
            type: "POST",
            data: JSON.stringify(data),
            dataType: "json",
            url: "/assignPlan",
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            },
            contentType: 'application/json',
            success: function(r) {
                $(location).attr('href', '/route/planning');
            },
            error: function(err){
                swal('Ha ocurrido un error por favor intente nuevamente en un momento.');
                hideModal();
            }
        });
    }

    function putGmaps(currentRoute, round, markers, distanceEstimation, timeEstimation){
        // Fix decimals
        timeEstimation = Number((timeEstimation).toFixed(2));
        distanceEstimation = Number((distanceEstimation).toFixed(2));

        var a = `<div class="box">
            <div class="box-body" id="map_zone">
                <a class="btn btn-block btn-social btn-bitbucket" href='${currentRoute}' target='_blank'>
                    <i class="fa fa-flag-checkered"></i> ABRIR RUTA SUGERIDA # ${round}
                </a>
                <p><b>Recorrido estimado:${distanceEstimation} km</b></p>
                <p><b>Tiempo estimado:${timeEstimation} min</b></p>
                <div id="map_canvas${round}" style="width:100%; height:500px;"></div>
            </div>
        </div>`;

        $('#map_zone').append(a);
        initializeMap('map_canvas' + round, round, markers);
    }

    function initializeMap(mapId, round, markers) {

        // create the maps
        var myOptions = {
            zoom: 11,
            center: new google.maps.LatLng(parseFloat(markers[0].lat), parseFloat(markers[0].lng)),
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        var map = new google.maps.Map(document.getElementById(mapId), myOptions);
        putMarkersOnMap(map, markers);
    }

    function putMarkersOnMap(map, markers){
        var cont = 0,
        initialPoint = null,
        myLatLng = null;
        markers.forEach(function(m){
            cont++;

            myLatLng = {lat: parseFloat(m.lat), lng: parseFloat(m.lng)};
            if(m.name == "home"){
                map.setCenter(myLatLng);
                var marker = new google.maps.Marker({
                    position: myLatLng,
                    map: map,
                    title: "Home",
                    label: "Home"
                });
            }else{
                var marker = new google.maps.Marker({
                    position: myLatLng,
                    map: map,
                    title: "" + cont,
                    label: "" + cont,
                    animation: google.maps.Animation.DROP,
                });
            }
        });
    }

    function selectAllOrders(me){
        var isAll = true;
        if(me.innerText == "Todos"){
            me.innerText = "Ninguno"
        }else{
            me.innerText = "Todos"
            isAll = false;
        }

        $('#pending_orders tr').each(function(i) {
            var $chkbox = $(this).find('input[type="checkbox"]');
            if(isAll){
                $chkbox.prop('checked', true);
            }else{
                $chkbox.prop('checked', false);
            }
        });
    }

    function selectAllDeliveries(me){
        var isAll = true;
        if(me.innerText == "Todos"){
            me.innerText = "Ninguno"
        }else{
            me.innerText = "Todos"
            isAll = false;
        }

        $('#delivery_availables tr').each(function(i) {
            var $chkbox = $(this).find('input[type="checkbox"]');
            if(isAll){
                $chkbox.prop('checked', true);
            }else{
                $chkbox.prop('checked', false);
            }

        });
    }

    function addNewDelivery(){
        var name = $('#new_name_delivery').val(),
        enterprise = $('#new_enterprise').val(),
        phone =  $('#new_phone').val();

        if(!name || !enterprise || !phone ){
            swal('', 'Por favor complete todos los campos','error');
            return;
        }

        var d = new Date();
        var n = d.getTime();

        var s = `<tr><td><div class="custom-control custom-checkbox">
            <div style="margin-left: 30%; margin-top: 20px;">
                <input type="checkbox" class="custom-control-input" id="delivery_check${n}"><label class="custom-control-label" for="delivery_check${n}"></label></div>
            </div>
        </td><td><img src="/img/no_image2.png" style="display: inline;" width="50px" alt="Producto 1">${name}</td>
        <td style="padding-top: 20px;">${enterprise}</td>
        <td style="padding-top: 20px;">${phone}</td>
        <td style="display: none;">free</td>
    </tr>`;

    $('#tbody_delivery').append(s);
    $('#modal_new_delivery').modal('hide');
}

function getLocationsAndOrders(){
    var locationsAndOrders = [],
    locations = [],
    orders = [],
    transferences = [];

    $('#pending_orders tr').each(function(i) {
        var $chkbox = $(this).find('input[type="checkbox"]');

        if($chkbox.prop('checked')){
            var code = $(this).find("td:eq(1)").text(),
            address = $(this).find("td:eq(2)").text(),
            lat = $(this).find("td:eq(3)").text(),
            long = $(this).find("td:eq(4)").text();

            locations.push({ address: code, lat: lat, lng: long });
            orders.push( {code: code} );
            if(JSON.parse($(this).find("td:eq(5)").text()).length > 0){
                transferences[code] = JSON.parse($(this).find("td:eq(5)").text());
            }
        }
    });

    locationsAndOrders['locations'] = locations;
    locationsAndOrders['orders'] = orders;
    locationsAndOrders['transferences'] = transferences;

    return locationsAndOrders;
}


function countKeys(obj) {
    var count=0;
    for(var prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            ++count;
        }
    }
    return count;
}

function getRounds(){
    var rounds = 0;
    window.finalDeliveries = [];
    $('#delivery_availables tr').each(function(i) {
        var $chkbox = $(this).find('input[type="checkbox"]');
        var id = $(this).find("td:eq(4)").text();

        if($chkbox.prop('checked')){
            var oDelivery = {
                id: id
            };
            window.finalDeliveries.push(oDelivery);
            rounds++;
        }
    });

    return rounds;
}

function setRoutes(data, transferences){
    var round = 1,
    cont = 0,
    initialPoint,
    currentRoute = "",
    currentOrders = [],
    goAndBack = true,
    isTransferenceRoute = false,
    transferencesRoute = [],
    distanceEpsilon = 0.5,
    timeEpsilon = 5,
    baseGmaps = "https://www.google.com/maps/dir/",
    initialPoint = { name: 'home', lat: $('#current_sucursal').find(':selected').data('lat'), lng:$('#current_sucursal').find(':selected').data('lng'), arrival: 0, distance: 0 };

    // Clear div
    $('#map_zone').html("");

    for(var r in data.route){

        // Se verifica si la ruta contiene transferencias, si esta contiene, se debe recalcular.
        if(transferences[data.route[r].name]){
            transferencesRoute = transferences[data.route[r].name];
            isTransferenceRoute = true;
        }

        // Si el punto evaluando actualmente es el inicial indica que inicia otro tramo.
        if(initialPoint.name == data.route[r].name && cont != 0){

            if(isTransferenceRoute){
                recalculateWithTransferences(currentOrders, transferencesRoute, initialPoint);
                currentRoute = "";
                currentOrders = [];
                transferencesRoute = [];
                isTransferenceRoute = false;
                continue;
            }

            if(goAndBack){
                currentRoute = baseGmaps + currentRoute + initialPoint.lat + "," + initialPoint.lng;
                currentOrders.push(initialPoint);
            }else{
                currentRoute = baseGmaps + currentRoute;
            }

            var distanceEstimation = data.route[r].distance - initialPoint.distance + distanceEpsilon,
            timeEstimation = data.route[r].arrival - initialPoint.arrival + timeEpsilon;

            initialPoint = data.route[r];
            // Capture Route
            window.finalRoute.push(currentOrders);
            putGmaps(currentRoute, round, currentOrders, distanceEstimation, timeEstimation);

            round++;
            currentRoute = "";
            currentOrders = [];
            currentOrders.push(initialPoint);
            continue;
        }

        currentOrders.push(data.route[r]);
        currentRoute = currentRoute + data.route[r].lat + "," + data.route[r].lng + "/";
        cont++;
    }

    window.maxRounds = round;
}

// Para este proceso se tiene en cuenta el "Due" para dar prioridad a las transferencias antes de realizar la entrega del pedido que la requiere.
function recalculateWithTransferences(route, transferencesRoute, initialPoint){
    var due = 5;
    for(var i =0; i < transferencesRoute.length; i++){
        route.push({ address:transferencesRoute[i].description, lat: transferencesRoute[0].sucursal.address.lat, lng: transferencesRoute[0].sucursal.address.long, restrictions: { due: due } });
        due+=5;
    }
    route.push(initialPoint);

    $.when(ajaxRouteXL(route, 1)).done(function(data, textStatus, jqXHR){
        hideModal();
        setRouteTransference(data);
    });
}

// Asignar una ruta de transferencia, especificando en el encabezado que es una ruta con transferencia
function setRouteTransference(data, transferences){
    var round = window.maxRounds,
    cont = 0,
    initialPoint,
    currentRoute = "",
    currentOrders = [],
    distanceEpsilon = 0.5,
    timeEpsilon = 5,
    goAndBack = true,
    baseGmaps = "https://www.google.com/maps/dir/",
    initialPoint = { name: 'home', lat: $('#current_sucursal').find(':selected').data('lat'), lng:$('#current_sucursal').find(':selected').data('lng'), arrival: 0, distance: 0 };

    for(var r in data.route){

        // Si el punto evaluando actualmente es el inicial indica que inicia otro tramo.
        if(initialPoint.name == data.route[r].name && cont != 0){

            if(goAndBack){
                currentRoute = baseGmaps + currentRoute + initialPoint.lat + "," + initialPoint.lng;
                currentOrders.push(initialPoint);
            }else{
                currentRoute = baseGmaps + currentRoute;
            }

            var distanceEstimation = data.route[r].distance - initialPoint.distance + distanceEpsilon,
            timeEstimation = data.route[r].arrival - initialPoint.arrival + timeEpsilon;

            initialPoint = data.route[r];

            // Capture Route
            window.finalRoute.push(currentOrders);
            putGmaps(currentRoute, round + ' (Incluye transferencias)', currentOrders, distanceEstimation, timeEstimation);

            round++;
            currentRoute = "";
            currentOrders = [];
            currentOrders.push(initialPoint);
            continue;
        }

        currentOrders.push(data.route[r]);
        currentRoute = currentRoute + data.route[r].lat + "," + data.route[r].lng + "/";
        cont++;
    }

    window.maxRounds = round;
}

function showModal(){
    $('#modalLoader').modal('show');
    $('#modalLoader').css('display', 'flex');
}

function hideModal(){
    $('#modalLoader').modal('hide');
    $('#modalLoader').css('display', 'none');
}

function planningRoute(){
    var arrLocations = getLocationsAndOrders(),
    locations = arrLocations.locations,
    orders = arrLocations.orders,
    transferences = arrLocations.transferences,
    rounds = getRounds();

    window.finalRoute = [];

    if(countKeys( locations ) < 2){
        swal('Por favor indique al menos 2 pedidos para realizar la planificación.', '', 'error');
        return;
    }

    if(rounds == 0){
        swal('Por favor indique al menos 1 repartidor para realizar la planificación.', '', 'error');
        return;
    }

    // Add Start and End point
    var selected_sucursal = $('#current_sucursal').find(':selected'),
    home_location = { address: "home", lat: selected_sucursal.data('lat')+"", lng:selected_sucursal.data('lng')+"" };

    // Start
    locations.unshift(home_location);
    //End
    locations.push(home_location);

    showModal();
    $.when(ajaxRouteXL(locations, rounds)).done(function(data, textStatus, jqXHR){
        setRoutes(data, transferences);
        hideModal();
    });
}

function ajaxRouteXL(locations, rounds){
    var data = {
        locations: locations,
        rounds: rounds
    };

    return $.ajax({
        type: "POST",
        data: JSON.stringify(data),
        dataType: "json",
        url: "/calculate_route",
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        },
        contentType: 'application/json',
        error: function(err){
            swal('Ha ocurrido un error por favor intente nuevamente en un momento.');
            hideModal();
        }
    });
}
</script>

</script>
<!--Load the API from the specified URL
    * The async attribute allows the browser to render the page while the API loads
    * The key parameter will contain your own API key (which is not needed for this tutorial)
    * The callback parameter executes the initMap() function
-->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBkgxwtElGfHz5bnO9u5zypbzy9IrfflM0">
</script>