<?php

namespace App\Http\Controllers;

use App\Customer;
use App\OrderFollowUp;
use App\OrderPaymentStatus;
use App\OrderPositions;
use App\OrderStatus;
use App\OrderTypes;
use App\User;
use App\Client;
use App\OrderCreditNote;

use App\Operator;
use App\OrderAddresses;
use App\OrderLogs;
use App\Orders;
use App\OrderTags;
use App\OrderDocuments;

use App\Http\Controllers\OrderLogsController;
use App\Http\Controllers\DropzoneController;
use App\Http\Controllers\OrderDocumentsController;
use App\Http\Controllers\OrderFollowUpController;

use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Storage;
use JsValidator;
use Illuminate\Support\Facades\DB;

use PDF;


class OrdersController extends Controller {
    const ORDER_FILES_PATH = "/files/order_files/";

    private $orderCreateSensibleValidationRules = [
        'customer_id' => 'required|min:1',
        'order_type_id' => 'required|min:1',
        'order_value' => 'required|regex:/^\d*(\,\d{1,2})?$/',
        'end_date' => 'required|date_format:"Y-m-d"',
        'export_date' => 'required|date_format:"Y-m-d"',
    ];

    private $orderCreateValidationRules = [];

    /**
     * Display a listing of the resource.
     *
     */
    public function index($customerId = NULL) {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("seeOrdersList", $loggedUser))
            return redirect()->route('index');

        $parameters = self::getParametersForOrdersList($loggedUser);

        # Ggf. nach Auftraggeber vorsortieren
        if ($customerId <> NULL) {
            $customer = Customer::find($customerId);
            if ($customer <> NULL) {
                $parameters["preFilters"]["customer"] = $customerId;
                $parameters["preFilteredByCustomer"] = true;
                $parameters["preFilteredCustomer"] = $customer->company;
            }
        }

        # Darf nur Aufträge sehen, bei denen verantwortlich
        if (UserController::checkRight("seeOnlyOwnOrders", $loggedUser))
            $parameters["preFilters"]["responsible"] = $loggedUser->operator->id;


        return View::make('order.list')->with($parameters);
    }

    public static function getParametersForOrdersList($user) {
        # Für Filterung alle Auftragstypen etc. laden
        $parameters = [
            "orderStatusList" => $user->client->order_status,
            "orderPaymentStatusList" => $user->client->order_payment_status,
            "orderTypesList" => $user->client->order_types,
            "orderTagsList" => $user->client->order_tags];

        $parameters = array_merge(UserController::convertOrderRightsToJs($user), $parameters);

        return $parameters;
    }

    /**
     * Show the form for creating a new resource.
     *
     */
    public function create() {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("createOrders", $loggedUser))
            return redirect()->route('index');

        # Zugehörigen Mandanten bestimmen
        $client = $loggedUser->client;

        # Auftraggeberliste laden
        $customerList = $client->customer;

        # Auftragstypen laden
        $orderTypesList = $client->order_types;

        # Auftragskennzeichen laden
        $orderTagsList = $client->order_tags;

        # Auftragsstatus laden
        $orderStatusList = $client->order_status;

        # Bezahlstatus laden
        $orderPaymentStatusList = $client->order_payment_status;

        # Bearbeiter laden
        $operators = $client->operatorsWithUser;

        # Validierung
        $frontValidator = JsValidator::make($this->orderCreateSensibleValidationRules, [], [], "#createOrder");

        $arguments = ["customerList" => $customerList,
            "orderTypesList" => $orderTypesList,
            "orderTagsList" => $orderTagsList,
            "orderStatusList" => $orderStatusList,
            "orderPaymentStatusList" => $orderPaymentStatusList,
            "operatorsList" => $operators,
            "validator" => $frontValidator

        ];

        return View::make('order.create')->with($arguments);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     */
    public function store(Request $request) {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("createOrders", $loggedUser))
            return redirect()->route('index');

        # Input validieren, soweit möglich
        $backValidator = Validator::make($request->all(), $this->orderCreateSensibleValidationRules);

        if ($backValidator->fails()) {
            Log::error("Fehler beim Anlegen eines neuen Auftrags: Validierungsfehler");
            return redirect()->back()->withErrors($backValidator->errors());
        }

        $newOrder = new Orders();

        # Daten setzen
        $newOrder->client_id = $loggedUser->client->id;
        $newOrder->order_number = $loggedUser->client->numberRangeOrders->getNextNumber();
        $newOrder->customer_id = $request->get("customer_id");
        $newOrder->description = $request->get("description_text");
        $newOrder->order_type_id = $request->get("order_type_id");
        $newOrder->order_status_id = $request->get("order_status_id");
        $newOrder->order_payment_status_id = $request->get("order_payment_status_id");
        $newOrder->apartment_units = $request->get("apartment_units");
        #$newOrder->responsible_operator_id = $request->get("responsible_operator_id");
        #$newOrder->fetch_number = $request->get("fetch_number");

        # Endtermin
        $orderEndDateError = false;
        try {
            $newEndDate = Carbon::createFromFormat("Y-m-d", $request->get("end_date"));
            $newEndDate->setTime(0, 0, 0);
            $newEndDate = $newEndDate->toDateTimeString();
            $newOrder->end_date = $newEndDate;
        } catch (\Exception $e) {
            Log::error("Fehler beim Erstellen eines Datumsobjekts: " . $e->getMessage());
            $orderEndDateError = true;
        }

        # Exporttermin
        $orderExportDateError = false;
        try {
            $newExportDate = Carbon::createFromFormat("Y-m-d", $request->get("export_date"));
            $newExportDate->setTime(0, 0, 0);
            $newExportDate = $newExportDate->toDateTimeString();
            $newOrder->export_date = $newExportDate;
        } catch (\Exception $e) {
            Log::error("Fehler beim Erstellen eines Datumsobjekts: " . $e->getMessage());
            $orderExportDateError = true;
        }


        # Auftrag zwischenspeichern, um ID zu erhalten
        try {
            $newOrder->save();
        } catch (\Exception $e) {
            Log::error("Fehler beim Anlegen eines neuen Auftrags: " . $e->getMessage());
            return redirect()->back()->withErrors("Es ist ein Fehler aufgetreten");
        }


        # Diverse Komponenten zum Auftrag hinzufügen

        # Verantwortliche Bearbeiter
        $responsibleOperators = $request->get("responsibleOperators");

        $responsibleOperatorsError = false;
        $operatorsToAdd = array();
        if ($responsibleOperators <> NULL && count($responsibleOperators) > 0) {
            foreach ($responsibleOperators as $operator => $status) {
                if ($status != "on") continue;

                $addOperator = Operator::find($operator);
                if ($addOperator == null) continue;

                $operatorsToAdd[] = $addOperator->id;
            }

            try {
                $newOrder->responsibleOperators()->attach($operatorsToAdd);
            } catch (\Exception $e) {
                Log::error("Fehler beim Hinzufügen von verantwortlichen Bearbeitern: " . $e->getMessage());
                $responsibleOperatorsError = true;
            }
        }

        # Auftragskennzeichen mit Werten
        $orderTags = $request->get("ordertags");
        $orderTagsValues = $request->get("ordertagsValues");

        $orderTagsError = false;

        if ($orderTags <> NULL && count($orderTags) > 0) {
            # Gesetzte Auftragskennzeichen erkennen und in Relationstabelle order_order_tag mit Wert eintragen
            foreach ($orderTags as $orderTag => $status) {
                if ($status != "on") continue;

                $addOrderTag = OrderTags::find($orderTag);
                if ($addOrderTag == null) continue;

                $orderTagValue = null;

                # Prüfen, ob für dieses Auftragskennzeichen ein Wert übergeben wurde - wenn nicht, auch kein Problem
                if (array_key_exists($orderTag, $orderTagsValues)) {
                    $orderTagValue = $orderTagsValues[$orderTag];
                }

                try {
                    $newOrder->order_tags()->save($addOrderTag, ["tag_value" => $orderTagValue]);
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen eines Auftragskennzeichens: " . $e->getMessage());
                    $orderTagsError = true;
                    continue;
                }
            }
        }

        # Auftragsadressen aus JSON-Objekten lesen, konvertieren und schreiben
        $orderAddresses = $request->get("address");
        $orderAddressError = false;

        if ($orderAddresses <> NULL && count($orderAddresses) > 0) {
            foreach ($orderAddresses as $jsonOrderAddress) {
                $orderAddressData = json_decode($jsonOrderAddress);

                if ($orderAddressData == NULL) {
                    Log::error("Fehler beim Dekodieren einer Auftragsadresse: " . json_last_error_msg());
                    $orderAddressError = true;
                    continue;
                }

                $newOrderAddress = new OrderAddresses();

                $newOrderAddress->order_id = $newOrder->id;
                $newOrderAddress->name = $orderAddressData->name;
                $newOrderAddress->firstname = $orderAddressData->firstname;
                $newOrderAddress->company = $orderAddressData->company;
                $newOrderAddress->street = $orderAddressData->street;
                $newOrderAddress->house_number = $orderAddressData->house_number;
                $newOrderAddress->address_addendum = $orderAddressData->address_addendum;
                $newOrderAddress->zipcode = $orderAddressData->zipcode;
                $newOrderAddress->place = $orderAddressData->place;
                $newOrderAddress->country = $orderAddressData->country;
                $newOrderAddress->telephone_number = $orderAddressData->telephone_number;
                $newOrderAddress->note = $orderAddressData->note;
                $newOrderAddress->email_final_customer = $orderAddressData->email_final_customer;

                // Bei Neuanlage sofort die Geokoordinaten bestimmen und mit speichern
                $addressString = $newOrderAddress->street . " " . $newOrderAddress->house_number;
                $addressString.= ", " . $newOrderAddress->zipcode . " " . $newOrderAddress->place;
                $result = OrdersController::geocode($addressString);
                if($result !== false)
                {
                    // Geocoordinaten konnten korrekt bestimmt werden
                    $newOrderAddress->latitude = $result[0];
                    $newOrderAddress->longitude = $result[1];
                }

                try {
                    $newOrderAddress->save();
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen einer Auftragsadresse: " . $e->getMessage());
                    $orderAddressError = true;
                    continue;
                }
            }
        }


        # Auftragsdokumente zuordnen

        # Liste der temporären Dateinamen auslesen
        $jsonOrderFiles = json_decode($request->get("newFiles"));
        $orderFilesError = false;
        if ($jsonOrderFiles == NULL) {
            Log::error("Fehler beim Hinzufügen von Auftragsdokumenten; JSON-Fehler: " . json_last_error_msg());
            $orderFilesError = true;
        }
        else {
            # Dateien lesen, Pfade bestimmen
            $orderFiles = $jsonOrderFiles->files;

            $draftPath = DropzoneController::DROPZONE_TMP_PATH;
            $path = OrdersController::ORDER_FILES_PATH . "order_" . $newOrder->id . "/";

            foreach ($orderFiles as $orderFile) {
                # Entwurfskennzeichen von Datei entfernen
                $filename = substr($orderFile, 4, strlen($orderFile) - 4);

                # Für angezeigten Dateinamen GUID von Datei entfernen
                $prettyFilename = substr($filename, 9, strlen($filename) - 9);

                # Bild in Storage umbenennen
                UploadController::renameFile($orderFile, $filename, $draftPath, $path);

                # Pfad in Tabelle speichern
                $newOrderDocument = new OrderDocuments();
                $newOrderDocument->order_id = $newOrder->id;
                $newOrderDocument->user_id = Auth::user()->id;
                $newOrderDocument->path = $path . $filename;
                $newOrderDocument->prettyName = $prettyFilename;
                $newOrderDocument->locked = 1;

                # In Tabelle Pfad speichern
                try {
                    $newOrderDocument->save();
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen von Auftragsdokumenten in Datenbank: " . $e->getMessage());
                    $orderFilesError = true;
                }
            }
        }


        # Auftragstermine und -bearbeiter
        $orderAppointmentError = false;
        $orderAppointments = $request->get("newAppointments");

        if ($orderAppointments <> NULL && count($orderAppointments) > 0) {

            $orderAppointmentsToAdd = array();

            foreach ($orderAppointments as $jsonOrderAppointment) {

                # Adressdaten dekodieren
                $orderAppointment = json_decode($jsonOrderAppointment);

                if ($orderAppointment == NULL) {
                    Log::error("Fehler beim Dekodieren eines Auftragstermins: " . json_last_error_msg());
                    $orderAppointmentError = true;
                    continue;
                }

                # Neue Bearbeiter-Termin-Auftrags-Relation erstellen
                # Bearbeiter suchen
                $operator = Operator::find($orderAppointment->operatorId);
                if ($operator == null) {
                    $orderAppointmentError = true;
                    continue;
                }

                # Zeitstempel konvertieren
                $carbonTime = Carbon::createFromTimestamp($orderAppointment->appointmentDate);
                $appointmentDate = $carbonTime->toDateTimeString();

                $orderAppointmentsToAdd[$operator->id] = ["appointment_from" => $appointmentDate,
                    "appointment_to" => $appointmentDate];
            }

            try {
                $newOrder->operators()->attach($orderAppointmentsToAdd);
            } catch (\Exception $e) {
                Log::error("Fehler beim Hinzufügen der Auftragstermine: " . $e->getMessage());
                $orderAppointmentError = true;
            }
        }

        # Auftragswert als Auftragsposition anlegen
        $orderValueError = false;
        if (!OrderPositionsController::storeNewPosition($newOrder->id, "Auftragswert",
            OrderPositionsController::convertToDouble($request->get("order_value")))) {
            $orderValueError = true;
        }

        # Wiedervorlage setzen
        $nextUp = (new Carbon())->addWeek(1);
        OrderFollowUpController::createNewFollowUp($newOrder->id, $loggedUser->operator->id, "", $nextUp->toDateString());

        # Log schreiben
        OrderLogsController::writeLog(1, $newOrder->id, $loggedUser->operator->id);

        # Erfolgsmeldung zurückgeben, ggf. mit Hinweisen
        $message = "Der Auftrag " . $newOrder->order_number . " wurde erfolgreich angelegt. ";
        if ($orderTagsError) $message .= "Es gab einen Fehler beim Anlegen der Auftragskennzeichen.";
        if ($orderAddressError) $message .= "Es gab einen Fehler beim Anlegen der Auftragsadressen.";
        if ($orderFilesError) $message .= "Es gab einen Fehler beim Zuordnen der Auftragsdokumente.";
        if ($orderAppointmentError) $message .= "Es gab einen Fehler beim Zuweisen von einem Auftragstermin.";
        if ($orderValueError) $message .= "Es gab einen Fehler beim  Hinterlegen des Auftragswerts.";
        if ($orderEndDateError) $message .= "Es gab einen Fehler beim  Hinterlegen des Endtermins.";
        if ($orderExportDateError) $message .= "Es gab einen Fehler beim  Hinterlegen des Exporttermins.";
        if ($responsibleOperatorsError) $message .= "Es gab einen Fehler beim  Hinterlegen der verantwortlichen Bearbeiter.";

        if ($orderTagsError || $orderAddressError || $orderFilesError || $orderAppointmentError || $orderEndDateError
            || $responsibleOperatorsError || $orderExportDateError)
            return redirect()->route('orders.list')->with('okayWithErrors', $message);
        else
            return redirect()->route('orders.list')->with('message', $message);
    }


    /**
     * Show the modal to confirm the destroy of a resource.
     *
     */
    public function showDeleteConfirm() {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("deleteOrders", $loggedUser))
            return response()->json(['response' => 'error', 'message' => 'Keine Berechtigung.']);

        $orderId = request()->input('orderId');

        # Auftragsdaten lesen
        $order = Orders::find($orderId);
        if ($order == null)
            return response()->json(['response' => 'error', 'message' => 'Auftrag nicht gefunden.']);

        # Darf nur auf Aufträge zugreifen, bei denen verantwortlich -> Prüfen, ob verantwortlich
        if (UserController::checkRight("seeOnlyOwnOrders", $loggedUser) && !(UserController::isResponsibleOperatorForOrder($loggedUser->operator, $order)))
            return response()->json(['response' => 'error', 'message' => 'Keine Berechtigung.']);


        # Auftraggeber
        if ($order->customer <> null)
            $order->customerName = $order->customer->company . " " . $order->customer->place;
        else
            $order->customerName = "Unbekannt";

        # Auftragstyp
        if ($order->order_type <> null)
            $order->order_type_label = $order->order_type->label;
        else
            $order->order_type_label = "Unbekannt";

        # View rendern
        $arguments = ["order" => $order];

        $editorHTML = View::make('order.deleteOrder')->with($arguments)->render();

        return response()->json(['response' => 'success', 'editorHTML' => $editorHTML]);
    }

    /**
     * Remove the specified resource from storage.
     *
     */
    public function destroy() {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("deleteOrders", $loggedUser))
            return redirect()->route('index');

        $orderId = request()->input('orderToDelete');
        $confirmDelete = request()->input('confirmDelete');

        # Auftragsdaten lesen
        $order = Orders::find($orderId);
        if ($order == null)
            return redirect()->back()->withErrors("Auftrag nicht gefunden.");

        # Darf nur auf Aufträge zugreifen, bei denen verantwortlich -> Prüfen, ob verantwortlich
        if (UserController::checkRight("seeOnlyOwnOrders", $loggedUser) && !(UserController::isResponsibleOperatorForOrder($loggedUser->operator, $order)))
            return redirect()->back()->withErrors("Keine Berechtigung.");

        # Prüfen, ob Checkbox bestätigt ist
        if (!is_null($confirmDelete)) {
            # Auftrag löschen - wenn möglich!
            try {
                # Versuchen, zu löschen
                $order->delete();
            } catch (\Exception $e) {
                # Es kann sein, dass noch Relations zu Aufträgen (orders) bestehen
                Log::error("Fehler beim Löschen des Auftrags " . $order->id . ", Mandant " .
                    $loggedUser->client->id . ": " . $e->getMessage() . $e->getFile() . $e->getLine());

                return redirect()->back()->withErrors("Auftrag konnte nicht gelöscht werden.");
            }
        }
        else {
            return redirect()->back()->with("message", "Der Auftrag wurde nicht gelöscht, da das Löschen bestätigt werden muss!");
        }

        Log::info("Auftrag " . $order->id . ", Mandant " . $loggedUser->client->id . " wurde gelöscht.
                            (User " . $loggedUser->id . ")");
        return redirect()->route('orders.list')->with("message", "Auftrag wurde erfolgreich gelöscht.");
    }

    /**
     * Show the form for editing the specified resource.
     *
     */
    public function edit($orderId) {
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("editOrders", $loggedUser))
            return redirect()->route('index');


        # Auftragsdaten lesen
        $order = Orders::find($orderId);
        if ($order == null)
            return redirect()->route("orders.list")->withErrors("Auftrag nicht gefunden.");

        # Darf nur Aufträge sehen, bei denen verantwortlich -> Prüfen, ob verantwortlich
        if (UserController::checkRight("seeOnlyOwnOrders", $loggedUser) && !(UserController::isResponsibleOperatorForOrder($loggedUser->operator, $order)))
            return redirect()->route('index');

        $client = $loggedUser->client;

        # Für eventuelle Änderungen weitere Auftragskomponenten laden
        # Auftraggeberliste laden
        $customerList = $client->customer;

        # Auftragstypen laden
        $orderTypesList = $client->order_types;

        # Auftragskennzeichen laden
        $orderTagsList = $client->order_tags;

        # Auftragskennzeichen, die schon dem Auftrag zugeordnet sind, entfernen
        if (count($order->order_tags) > 0) {
            foreach ($order->order_tags as $orderTag) {
                $orderTagsList = $orderTagsList->except($orderTag->id);
            }
        }

        # Auftragsstatus laden
        $orderStatusList = $client->order_status;

        # Bezahlstatus laden
        $orderPaymentStatusList = $client->order_payment_status;

        # Adressen laden
        $orderAddresses = array();

        # Adressen in JSON-Objekte konvertieren
        if (count($order->addresses) > 0) {
            foreach ($order->addresses as $address) {
                $address->addressExisted = true;
                $orderAddresses[$address->id] = json_encode($address);

            }
        }

        # Auftragsdokumente laden
        $orderDocuments = $this->getOrderDocumentsList($order->id);

        # Auftragshistorie lesen
        $orderLogs = array();
        if (count($order->order_logs) > 0) {

            # 14.01.2019 / DS / Wenn sensible Daten ausgeblendet werden sollen, nur Freitext-Lognachrichten anzeigen und
            # die Systemlogs, die durch den Nutzer selbst getriggert wurden
            if (UserController::checkRight("hideSensibleOrderData", $loggedUser)) {
                $customLogs = $order->order_custom_logs()->get();
                $ownLogs = $order->order_logs()->where('operator_id', '=', $loggedUser->operator->id)->get();

                $logs = $customLogs->merge($ownLogs)->sortByDesc("created_at");
            }
            # Ansonsten alle Lognachrichten anzeigen
            else $logs = $order->order_logs()->orderBy("created_at", "desc")->get();

            foreach ($logs as $order_log_id => $order_log) {

                # Zeitstempel in lesbare Form bringen
                $timeObject = \DateTime::createFromFormat("Y-m-d H:i:s", $order_log->created);
                if (!$timeObject) continue;

                $orderLogs[$order_log_id]["created"] = $timeObject->format("d.m.Y");
                $orderLogs[$order_log_id]["type"] = $order_log->type;
                $orderLogs[$order_log_id]["text"] = $order_log->text;

                # Systemlogs werden extra gekennzeichnet, Standardeinstellung
                $orderLogs[$order_log_id]["creator"] = "System";
                $orderLogs[$order_log_id]["class"] = "systemlog";

                # Wenn Logeintrag kein Systemlog, anderweitig kennzeichnen und Nutzernamen auslesen
                if ($order_log->type != OrderLogs::LOGTYPE_SYSLOG) {
                    # Nutzernamen holen
                    $operator = Operator::find($order_log->operator_id);
                    if ($operator == null) continue;
                    $orderLogs[$order_log_id]["creator"] = $operator->name . " " . $operator->firstname;
                    $orderLogs[$order_log_id]["class"] = "userlog";
                }
            }
        }

        # Bearbeiter/Termine lesen
        $orderAppointments = array();

        if (count($order->operators) > 0) {
            foreach ($order->operators()->orderBy("appointment_from", "asc")->get() as $operatorAppointment) {

                # Zeitstempel in lesbare Form bringen
                $timeObject = \DateTime::createFromFormat("Y-m-d H:i:s", $operatorAppointment->pivot->appointment_from);

                $orderAppointments[$operatorAppointment->pivot->id]["operator"] = $operatorAppointment->name . " " . $operatorAppointment->firstname;
                $orderAppointments[$operatorAppointment->pivot->id]["appointment"] = $timeObject->format("d.m.Y");
            }
        }

        # Rechnungen lesen
        $orderInvoices = array();
        if (count($order->order_invoices) > 0) {
            foreach ($order->order_invoices()->orderBy("created_at", "desc")->get() as $order_invoice_id => $order_invoice) {
                # Zeitstempel in lesbare Form bringen
                $timeObject = Carbon::createFromFormat('Y-m-d', $order_invoice->date_of_issue);
                if (!$timeObject) continue;

                $orderInvoices[$order_invoice_id]["id"] = $order_invoice->id;
                $orderInvoices[$order_invoice_id]["order_id"] = $order->id;
                $orderInvoices[$order_invoice_id]["date_of_issue"] = $timeObject->format("d.m.Y");
                $orderInvoices[$order_invoice_id]["invoice_number"] = $order_invoice->invoice_number;
                $orderInvoices[$order_invoice_id]["customer_id"] = $order->customer->id;

                // Adressdaten
                $orderInvoices[$order_invoice_id]["company"] = $order_invoice->company;
                $orderInvoices[$order_invoice_id]["street"] = $order_invoice->street;
                $orderInvoices[$order_invoice_id]["house_number"] = $order_invoice->house_number;
                $orderInvoices[$order_invoice_id]["zipcode"] = $order_invoice->zipcode;
                $orderInvoices[$order_invoice_id]["place"] = $order_invoice->place;
                $orderInvoices[$order_invoice_id]["country"] = $order_invoice->country;

                // TODO kann man bestimmt Eloquenter lösen ;)
                // Gesamtpreis aufsummieren
                $auftragssumme = 0;
                $auftragssumme =
                    (DB::table('order_invoice_positions')
                        ->select(DB::raw('sum(quantity * price) AS summary'))
                        ->where('order_invoice_id', $order_invoice->id)
                        ->first())->summary;

                $orderInvoices[$order_invoice_id]["total_price"] = $auftragssumme;
            }
        }

        # Auftragsmails lesen
        $orderMailLogs = $order->mails;

        # Wiedervorlagen
        if (UserController::checkRight("editFollowUps", $loggedUser)) {
            $orderFollowUps = $order->followUps()->orderBy("nextUp", "asc")->get();
        }
        else {
            $orderFollowUps = $order->followUpsUser($loggedUser->operator->id);
        }

        # Verantwortliche Bearbeiter des Auftrags laden
        $responsibleOperatorsIds = $order->responsibleOperators->pluck('id')->toArray();
        $responsibleOperators = $order->responsibleOperators;

        # Alle weiteren Bearbeiter laden
        $operators = $client->operatorsWithUser->whereNotIn("id", $responsibleOperatorsIds);


        # Gutschriften in JSON-Objekte konvertieren
        $orderCreditNotes = array();
        if (count($order->creditNotes) > 0) {
            foreach ($order->creditNotes as $creditNote) {
                $creditNote->creditNoteExisted = true;
                $orderCreditNotes[$creditNote->id] = json_encode($creditNote);
            }
        }

        # Validierung
        # 14.01.2019 / DS / Wenn der Nutzer die sensiblen Daten nicht sehen darf, sind andere Validierungen nötig,
        # da manche Felder im Frontend dann nicht vorhanden sind.
        if (UserController::checkRight("hideSensibleOrderData", $loggedUser))
            $validationRules = $this->orderCreateValidationRules;
        else $validationRules = $this->orderCreateSensibleValidationRules;
        $frontValidator = JsValidator::make($validationRules, [], [], "#editOrder");

        $parameters = ["orderToEdit" => $order,
            "customerList" => $customerList,
            "orderTypesList" => $orderTypesList,
            "orderTagsList" => $orderTagsList,
            "orderStatusList" => $orderStatusList,
            "orderPaymentStatusList" => $orderPaymentStatusList,
            "orderAddresses" => $orderAddresses,
            "orderDocuments" => $orderDocuments,
            "orderLogs" => $orderLogs,
            "orderAppointments" => $orderAppointments,
            "orderMailLogs" => $orderMailLogs,
            "orderFollowUps" => $orderFollowUps,
            "responsibleOperatorsList" => $responsibleOperators,
            "operatorsList" => $operators,
            "orderCreditNotes" => $orderCreditNotes,
            "validator" => $frontValidator,
            "orderInvoices" => $orderInvoices];

        return View::make('order.edit')->with($parameters);
    }

    public static function getOrderDocumentsList($orderId) {

        # Auftragsdaten ladenf
        $order = Orders::find($orderId);

        if ($order == null) return false;

        # Auftragsdokumente laden
        $orderDocuments = array();

        # Eigenschaften wie Dateityp und Größe nachladen
        if (count($order->documents) > 0) {
            foreach ($order->documents as $document) {
                # Datei laden
                $exists = Storage::disk('uploads')->exists($document->path);

                if (!$exists) continue;

                # Metadaten bestimmen
                $orderDocuments[$document->id]["mimetype"] = Storage::disk('uploads')->mimeType($document->path);
                $orderDocuments[$document->id]["mimetypeLabel"] = OrderDocumentsController::getReadableMimetype($orderDocuments[$document->id]["mimetype"]);
                $orderDocuments[$document->id]["icon"] = OrderDocumentsController::getIconPath($orderDocuments[$document->id]["mimetype"]);

                $fileSize = Storage::disk('uploads')->size($document->path);
                if ($fileSize < (1024 * 1024)) {
                    $orderDocuments[$document->id]["size"] = round(($fileSize / 1024), 2); # Dateigröße in KB
                    $orderDocuments[$document->id]["sizeUnit"] = "KB";
                }
                else {
                    $orderDocuments[$document->id]["size"] = round(($fileSize / 1024 / 1024), 2); # Dateigröße in MB
                    $orderDocuments[$document->id]["sizeUnit"] = "MB";
                }

                $orderDocuments[$document->id]["user_id"] = $document->user_id;
                $orderDocuments[$document->id]["locked"] = $document->locked;

                $orderDocuments[$document->id]["url"] = Storage::disk('uploads')->url($document->path);
                $orderDocuments[$document->id]["path"] = Storage::disk("uploads")->getDriver()->getAdapter()->getPathPrefix() . $document->path;
                $orderDocuments[$document->id]["name"] = $document->prettyName;
                $orderDocuments[$document->id]["extension"] = pathinfo($orderDocuments[$document->id]["url"], PATHINFO_EXTENSION);
            }
        }

        return $orderDocuments;
    }


    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     */
    public function update(Request $request) {

        $loggedUser = User::find(Auth::user()->id);

        $orderToEdit = Orders::find($request->get("orderToEdit"));

        # Berechtigung prüfen
        if (!UserController::checkRight("editOrders", $loggedUser))
            return redirect()->route('index');

        # Darf nur Aufträge bearbeiten, bei denen verantwortlich -> Prüfen, ob verantwortlich
        if (UserController::checkRight("seeOnlyOwnOrders", $loggedUser) && !(UserController::isResponsibleOperatorForOrder($loggedUser->operator, $orderToEdit)))
            return redirect()->route('index');


        $client = $loggedUser->client;

        # 14.01.2019 / DS / Wenn der Nutzer die sensiblen Daten nicht sehen darf, sind andere Validierungen nötig,
        # da manche Felder im Frontend dann nicht vorhanden sind.
        if (UserController::checkRight("hideSensibleOrderData", $loggedUser))
            $validationRules = $this->orderCreateValidationRules;
        else $validationRules = $this->orderCreateSensibleValidationRules;

        # Input validieren, soweit möglich
        $backValidator = Validator::make($request->all(), $validationRules);

        if ($backValidator->fails()) {
            return redirect()->back()->withErrors($backValidator->errors());
        }


        # Errorflags
        $responsibleOperatorsError = false;
        $orderTagsError = false;
        $orderAddressError = false;
        $orderCreditNoteError = false;
        $orderFilesError = false;
        $orderFilesDeleteError = false;
        $orderAppointmentError = false;
        $orderLogError = false;
        $orderValueError = false;
        $orderEndDateError = false;
        $orderExportDateError = false;


        # Bei grundlegenden Auftragsdaten überprüfen, ob etwas geändert wurde,
        # wenn ja, Auftragshistorie-Eintrag schreiben.


        # Auftragsbeschreibung
        $newDescription = $request->get("description_text");

        if ($orderToEdit->description != $newDescription) {
            # Log schreiben
            OrderLogsController::writeLog(7, $orderToEdit->id, $loggedUser->operator->id);

            $orderToEdit->description = $newDescription;
        }

        # 14.01.2019 / DS / Sensible Daten nur ändern / auf Änderung überprüfen, wenn Nutzer das entsprechende Recht
        # hat (hideSensibleOrderData == false)
        if (!UserController::checkRight("hideSensibleOrderData", $loggedUser)) {

            # Auftraggeber
            $newCustomerID = $request->get("customer_id");

            if ($orderToEdit->customer_id != $newCustomerID) {
                # Namen des alten Auftraggebers holen
                $oldCustomer = $orderToEdit->customer->company;

                # Namen des neuen Auftraggebers holen
                $newCustomer = Customer::find($newCustomerID);

                # Wenn neuer Auftraggeber nicht gefunden wird, Fehler zurückgeben: Kein Auftrag ohne gültigen Auftraggeber!
                if ($newCustomer == null) return redirect()->back()->withErrors("Neuen Auftraggeber nicht gefunden!");

                $newCustomer = $newCustomer->company;

                # Log schreiben
                OrderLogsController::writeLog(2, $orderToEdit->id, $loggedUser->operator->id,
                    ["{OLDCUSTOMER}" => $oldCustomer, "{NEWCUSTOMER}" => $newCustomer]);

                $orderToEdit->customer_id = $newCustomerID;
            }

            # Auftragstyp
            $newOrderTypeID = $request->get("order_type_id");

            if ($orderToEdit->order_type_id != $newOrderTypeID) {
                # Alten Auftragstyp lesen
                $oldOrderType = $orderToEdit->order_type->label;

                # Neuen Auftragstyp lesen
                $newOrderType = OrderTypes::find($newOrderTypeID);

                # Wenn neuer Auftragstyp nicht gefunden wird, Fehler zurückgeben: Kein Auftrag ohne gültigen Auftragstypen!
                if ($newOrderType == null) return redirect()->back()->withErrors("Neuen Auftragstyp nicht gefunden!");

                $newOrderType = $newOrderType->label;

                # Log schreiben
                OrderLogsController::writeLog(3, $orderToEdit->id, $loggedUser->operator->id,
                    ["{OLDORDERTYPE}" => $oldOrderType, "{NEWORDERTYPE}" => $newOrderType]);

                $orderToEdit->order_type_id = $newOrderTypeID;
            }


            # Auftragsstatus
            $newOrderStatusID = $request->get("order_status_id");

            if ($orderToEdit->order_status_id != $newOrderStatusID) {
                # Alten Auftragsstatus lesen
                $oldOrderStatus = $orderToEdit->order_status->label;

                # Neuen Auftragsstatus lesen
                $newOrderStatus = OrderStatus::find($newOrderStatusID);

                # Wenn neuer Auftragsstatus nicht gefunden wird, unbekannt zurückgeben. Auftrag kann auch so existieren
                if ($newOrderStatus == null) $newOrderStatus = "unbekannt";
                else $newOrderStatus = $newOrderStatus->label;

                # Log schreiben
                OrderLogsController::writeLog(4, $orderToEdit->id, $loggedUser->operator->id,
                    ["{OLDORDERSTATUS}" => $oldOrderStatus, "{NEWORDERSTATUS}" => $newOrderStatus]);

                $orderToEdit->order_status_id = $newOrderStatusID;
            }


            # Bezahlstatus
            $newOrderPaymentStatusID = $request->get("order_payment_status_id");

            if ($orderToEdit->order_payment_status_id != $newOrderPaymentStatusID) {
                # Alten Bezahlstatus lesen
                $oldOrderPaymentStatus = $orderToEdit->order_payment_status->label;

                # Neuen Bezahlstatus lesen
                $newOrderPaymentStatus = OrderPaymentStatus::find($newOrderPaymentStatusID);

                # Wenn neuer Bezahlstatus nicht gefunden wird, unbekannt zurückgeben. Auftrag kann auch so existieren
                if ($newOrderPaymentStatus == null) $newOrderPaymentStatus = "unbekannt";
                else $newOrderPaymentStatus = $newOrderPaymentStatus->label;

                # Log schreiben
                OrderLogsController::writeLog(5, $orderToEdit->id, $loggedUser->operator->id,
                    ["{OLDORDERPAYMENTSTATUS}" => $oldOrderPaymentStatus, "{NEWORDERPAYMENTSTATUS}" => $newOrderPaymentStatus]);

                $orderToEdit->order_payment_status_id = $newOrderPaymentStatusID;
            }

            # Auftragswert
            $newOrderValue = $request->get("order_value");
            if ($orderToEdit->getOrderValue() != OrderPositionsController::convertToDouble($newOrderValue)) {

                # Alte Position für Auftragswert löschen
                if (!OrderPositionsController::removeOrderValuePosition($orderToEdit->id)) $orderValueError = true;

                # Neue Position anlegen
                if (!OrderPositionsController::storeNewPosition($orderToEdit->id,
                    "Auftragswert",
                    OrderPositionsController::convertToDouble($newOrderValue)))
                    $orderValueError = true;

                # Log schreiben
                OrderLogsController::writeLog(15, $orderToEdit->id, $loggedUser->operator->id,
                    ["{ORDERVALUE}" => $newOrderValue]);
            }

            # Wohneinheiten
            $newApartmentUnits = $request->get("apartment_units");

            if ($orderToEdit->apartment_units != $newApartmentUnits) {
                # Log schreiben
                OrderLogsController::writeLog(16, $orderToEdit->id, $loggedUser->operator->id,
                    ["{APARTMENTUNITSNUMBER}" => $newApartmentUnits]);

                $orderToEdit->apartment_units = $newApartmentUnits;
            }

            # Endtermin
            try {
                $newEndDate = Carbon::createFromFormat("Y-m-d", $request->get("end_date"));
                $newEndDate->setTime(0, 0, 0);
                $newEndDateLabel = $newEndDate->format("d.m.Y");
                $newEndDate = $newEndDate->toDateTimeString();
            } catch (\Exception $e) {
                Log::error("Fehler beim Erstellen eines Datumsobjekts: " . $e->getMessage());
                Log::error($e->getFile());
                $orderEndDateError = true;
                $newEndDate = $orderToEdit->end_date;
            }

            if ($orderToEdit->end_date != $newEndDate) {
                # Log schreiben
                OrderLogsController::writeLog(24, $orderToEdit->id, $loggedUser->operator->id,
                    ["{NEWENDDATE}" => $newEndDateLabel]);

                $orderToEdit->end_date = $newEndDate;
            }

            # Exporttermin
            try {
                $newExportDate = Carbon::createFromFormat("Y-m-d", $request->get("export_date"));
                $newExportDate->setTime(0, 0, 0);
                $newExportDateLabel = $newExportDate->format("d.m.Y");
                $newExportDate = $newExportDate->toDateTimeString();
            } catch (\Exception $e) {
                Log::error("Fehler beim Erstellen eines Datumsobjekts: " . $e->getMessage());
                Log::error($e->getFile());
                $orderExportDateError = true;
                $newExportDate = $orderToEdit->export_date;
            }

            if ($orderToEdit->export_date != $newExportDate) {
                # Log schreiben
                OrderLogsController::writeLog(29, $orderToEdit->id, $loggedUser->operator->id,
                    ["{NEWEXPORTDATE}" => $newExportDateLabel]);

                $orderToEdit->export_date = $newExportDate;
            }
        }


        # Zwischenspeichern
        try {
            $orderToEdit->save();
        } catch (\Exception $e) {
            return redirect()->back()->withErrors("Speichern fehlgeschlagen.");
        }


        # 14.01.2019 / DS
        if (!UserController::checkRight("hideSensibleOrderData", $loggedUser)) {

            # Verantwortlicher Bearbeiter
            # Bisherige verantwortliche Bearbeiter holen
            $responsibleOperatorsIds = $orderToEdit->responsibleOperators->pluck('id')->toArray();

            $newResponsibleOperators = $request->get("responsibleOperators");

            if ($newResponsibleOperators <> null)
                $newResponsibleOperators = array_keys($newResponsibleOperators);
            else $newResponsibleOperators = array();

            $operatorsToAdd = array();

            foreach ($newResponsibleOperators as $operatorId) {

                $addOperator = Operator::find($operatorId);
                if ($addOperator == null) continue;

                $operatorsToAdd[] = $addOperator->id;
            }

            try {
                $orderToEdit->responsibleOperators()->sync($operatorsToAdd);
            } catch (\Exception $e) {
                Log::error("Fehler beim Hinzufügen von verantwortlichen Bearbeitern: " . $e->getMessage());
                $responsibleOperatorsError = true;
            }

            # Für Log prüfen, ob etwas geändert wurde
            #
            $newOperatorCount = count($newResponsibleOperators);
            $oldOperatorCount = count($responsibleOperatorsIds);

            $logOperatorChange = false;
            if ($oldOperatorCount == $newOperatorCount) {
                # Differenz der Arrays
                if (count(array_diff($responsibleOperatorsIds, $newResponsibleOperators)) > 0)
                    $logOperatorChange = true;
            }
            else $logOperatorChange = true;

            if ($logOperatorChange)
                OrderLogsController::writeLog(26, $orderToEdit->id, $loggedUser->operator->id);


            /*
            # Andere Variante mit Checksum
            $newOperatorChecksum = array_sum($newResponsibleOperators);
            $oldOperatorChecksum = array_sum($responsibleOperatorsIds);

            if($newOperatorChecksum <> $oldOperatorChecksum) {

            }*/

            # Auftragskennzeichen mit Werten aus Formular lesen und neu setzen
            $orderTagsToAdd = $this->getOrderTagsToAdd($request);

            try {
                # Alte Auftragskennzeichen löschen
                $orderToEdit->order_tags()->detach();

                # Auftragskennzeichen hinzufügen
                $orderToEdit->order_tags()->attach($orderTagsToAdd);
            } catch (\Exception $e) {
                Log::error("Fehler beim Aktualisieren der Auftragskennzeichen: " . $e->getMessage());
                $orderTagsError = true;
            }

            # Log schreiben, wenn etwas geändert wurde
            if ($request->get("orderTagsChanged") == true) {
                OrderLogsController::writeLog(6, $orderToEdit->id, $loggedUser->operator->id);
            }


            # Gutschriften
            # Gutschriften aus JSON-Objekten lesen, konvertieren und schreiben
            $orderCreditNotes = $request->get("creditNote");

            if ($orderCreditNotes <> NULL && count($orderCreditNotes) > 0) {
                foreach ($orderCreditNotes as $orderCreditNoteId => $jsonOrderCreditNote) {

                    # Gutschriftdaten dekodieren
                    $orderCreditNoteData = json_decode($jsonOrderCreditNote);

                    if ($orderCreditNoteData == NULL) {
                        Log::error("Fehler beim Dekodieren einer Auftragsgutschrift: " . json_last_error_msg());
                        $orderCreditNoteError = true;
                        continue;
                    }

                    // Existiert die Gutschrift bereits?
                    if (isset($orderCreditNoteData->creditNoteExisted)) {
                        if ($orderCreditNoteData->creditNoteExisted == true) {

                            // Gutschrift holen
                            $oldCreditNote = OrderCreditNote::find($orderCreditNoteId);

                            // Gutschrift nicht gefunden, überspringen
                            if ($oldCreditNote == null) {
                                continue;
                            }

                            // Gutschrift löschen - entweder bleibt sie das oder sie wird neu geschrieben
                            try {
                                $oldCreditNote->forceDelete();
                            } catch (\Exception $e) {
                                Log::info($e->getMessage());
                            }
                            // Prüfen, ob Gutschrift gelöscht werden soll
                            if (isset($orderCreditNoteData->deleteCreditNote)) {
                                if ($orderCreditNoteData->deleteCreditNote == true) {
                                    // Gutschrift bleibt gelöscht
                                    # Log schreiben
                                    OrderLogsController::writeLog(28, $orderToEdit->id, $loggedUser->operator->id);
                                    continue;
                                }
                            }

                            // Wenn nicht, Gutschrift komplett neu schreiben - es könnten Änderungen gemacht worden sein,
                            // daher wird die Gutschrift neu gespeichert
                            //
                        }
                        else {
                            # Log schreiben: Gutschrift ist neu
                            OrderLogsController::writeLog(27, $orderToEdit->id, $loggedUser->operator->id);
                        }
                    }

                    $newOrderCreditNote = new OrderCreditNote();

                    # Datum formatieren
                    if (strlen($orderCreditNoteData->date) <= 10) {
                        $date = Carbon::createFromFormat("Y-m-d", $orderCreditNoteData->date);
                        $date = $date->toDateTimeString();
                    }
                    else $date = $orderCreditNoteData->date;

                    # Gutschriftdaten
                    $newOrderCreditNote->order_id = $orderToEdit->id;
                    $newOrderCreditNote->date = $date;
                    $newOrderCreditNote->credit_note_number = $orderCreditNoteData->credit_note_number;
                    $newOrderCreditNote->credit_note_value = OrderPositionsController::convertToDouble($orderCreditNoteData->credit_note_value);
                    $newOrderCreditNote->reference_number = $orderCreditNoteData->reference_number;
                    $newOrderCreditNote->fetch_number = $orderCreditNoteData->fetch_number;

                    # Enddatum formatieren und nur setzen, wenn vorhanden
                    if ($orderCreditNoteData->end_date <> "") {
                        if (strlen($orderCreditNoteData->date) <= 10) {
                            $endDate = Carbon::createFromFormat("Y-m-d", $orderCreditNoteData->end_date);
                            $endDate = $endDate->toDateTimeString();
                        }
                        else
                            $endDate = $orderCreditNoteData->end_date;

                        $newOrderCreditNote->end_date = $endDate;
                    }

                    # Gutschrift schreiben
                    try {
                        $newOrderCreditNote->save();
                    } catch (\Exception $e) {
                        Log::error("Fehler beim Hinzufügen einer Gutschrift: " . $e->getMessage());
                        $orderCreditNoteError = true;
                        continue;
                    }
                }
            }


            # Auftragstermine und -bearbeiter
            $orderAppointments = $request->get("newAppointments");

            if ($orderAppointments <> NULL && count($orderAppointments) > 0) {

                $orderAppointmentsToAdd = array();

                foreach ($orderAppointments as $jsonOrderAppointment) {

                    # Adressdaten dekodieren
                    $orderAppointment = json_decode($jsonOrderAppointment);

                    if ($orderAppointment == NULL) {
                        Log::error("Fehler beim Dekodieren eines Auftragstermins: " . json_last_error_msg());
                        $orderAppointmentError = true;
                        continue;
                    }

                    # Neue Bearbeiter-Termin-Auftrags-Relation erstellen
                    # Bearbeiter suchen
                    $operator = Operator::find($orderAppointment->operatorId);
                    if ($operator == null) {
                        $orderAppointmentError = true;
                        continue;
                    }

                    # Zeitstempel konvertieren
                    $carbonTime = Carbon::createFromTimestamp($orderAppointment->appointmentDate);
                    $appointmentDate = $carbonTime->toDateTimeString();

                    $orderAppointmentsToAdd[$operator->id] = ["appointment_from" => $appointmentDate,
                        "appointment_to" => $appointmentDate];
                }

                try {
                    $orderToEdit->operators()->attach($orderAppointmentsToAdd);
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen eines Auftragstermins: " . $e->getMessage());
                    $orderAppointmentError = true;
                }

                # Log schreiben
                OrderLogsController::writeLog(12, $orderToEdit->id, $loggedUser->operator->id);
            }


            $jsonOrderAppointmentsToDelete = json_decode($request->get("appointmentsToDelete"));
            $appointmentsToDelete = $jsonOrderAppointmentsToDelete->appointmentsToDelete;

            if ($jsonOrderAppointmentsToDelete == NULL) {
                Log::error("Fehler beim Löschen von Auftragsterminen; JSON-Fehler: " . json_last_error_msg());
                $orderAppointmentError = true;
            }
            else {
                if (count($appointmentsToDelete) > 0) {
                    foreach ($appointmentsToDelete as $appointmentToDelete) {

                        $deleteAppointment = DB::table('order_appointment_operator')
                            ->where('id', $appointmentToDelete)
                            ->delete();

                        if (!$deleteAppointment) {
                            Log::error("Fehler beim Löschen von Auftragstermin: " . $appointmentToDelete);
                            $orderAppointmentError = true;
                            continue;
                        }

                        # Log schreiben
                        OrderLogsController::writeLog(13, $orderToEdit->id, $loggedUser->operator->id);

                    }
                }
            }

        }

        # Auftragsadressen
        # Auftragsadressen aus JSON-Objekten lesen, konvertieren und schreiben
        $orderAddresses = $request->get("address");

        if ($orderAddresses <> NULL && count($orderAddresses) > 0) {
            foreach ($orderAddresses as $orderAddressKey => $jsonOrderAddress) {

                # Adressdaten dekodieren
                $orderAddressData = json_decode($jsonOrderAddress);

                if ($orderAddressData == NULL) {
                    Log::error("Fehler beim Dekodieren einer Auftragsadresse: " . json_last_error_msg());
                    $orderAddressError = true;
                    continue;
                }

                // Existiert die Adresse bereits?
                if (isset($orderAddressData->addressExisted)) {
                    if ($orderAddressData->addressExisted == true) {

                        // Adresse holen
                        $oldOrderAddress = OrderAddresses::find($orderAddressKey);

                        // Adresse nicht gefunden, überspringen
                        if ($oldOrderAddress == null) {
                            continue;
                        }

                        // Adresse löschen - entweder bleibt sie das oder sie wird neu geschrieben
                        try {
                            OrderAddresses::destroy($oldOrderAddress->id);
                        } catch (\Exception $e) {
                            Log::info($e->getMessage());
                        }
                        // Prüfen, ob Adresse gelöscht werden soll
                        if (isset($orderAddressData->deleteAddress)) {
                            if ($orderAddressData->deleteAddress == true) {
                                // Adresse bleibt gelöscht
                                # Log schreiben
                                OrderLogsController::writeLog(9, $orderToEdit->id, $loggedUser->operator->id);
                                continue;
                            }
                        }

                        // Wenn nicht, Adresse komplett neu schreiben - es könnten Änderungen gemacht worden sein,
                        // daher wird die Adresse neu gespeichert
                        //
                    }
                    else {
                        # Log schreiben: Adresse ist neu
                        OrderLogsController::writeLog(8, $orderToEdit->id, $loggedUser->operator->id);
                    }
                }

                $newOrderAddress = new OrderAddresses();

                $newOrderAddress->order_id = $orderToEdit->id;
                $newOrderAddress->name = $orderAddressData->name;
                $newOrderAddress->firstname = $orderAddressData->firstname;
                $newOrderAddress->company = $orderAddressData->company;
                $newOrderAddress->street = $orderAddressData->street;
                $newOrderAddress->house_number = $orderAddressData->house_number;
                $newOrderAddress->address_addendum = $orderAddressData->address_addendum;
                $newOrderAddress->zipcode = $orderAddressData->zipcode;
                $newOrderAddress->place = $orderAddressData->place;
                $newOrderAddress->country = $orderAddressData->country;
                $newOrderAddress->telephone_number = $orderAddressData->telephone_number;
                $newOrderAddress->note = $orderAddressData->note;
                $newOrderAddress->email_final_customer = $orderAddressData->email_final_customer;

                // FIXME Eigentlich sollte beim regulären Speichern nicht immer eine Neue Adresse (ID) angelegt werden. Die bestehenden Adressen sollten im Regelfall nur aktualisiert werden
                $addressString = $newOrderAddress->street . " " . $newOrderAddress->house_number;
                $addressString.= ", " . $newOrderAddress->zipcode . " " . $newOrderAddress->place;
                $result = OrdersController::geocode($addressString);
                if($result !== false)
                {
                    // Geocoordinaten konnten korrekt bestimmt werden
                    $newOrderAddress->latitude = $result[0];
                    $newOrderAddress->longitude = $result[1];
                }

                try {
                    $newOrderAddress->save();
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen einer Auftragsadresse: " . $e->getMessage());
                    $orderAddressError = true;
                    continue;
                }
            }
        }

        # Neue Auftragsdokumente hinzufügen

        # Auftragsdokumente zuordnen

        # Liste der temporären Dateinamen auslesen
        $jsonOrderFiles = json_decode($request->get("newFiles"));

        if ($jsonOrderFiles == NULL) {
            Log::error("Fehler beim Hinzufügen von Auftragsdokumenten; JSON-Fehler: " . json_last_error_msg());
            $orderFilesError = true;
        }
        else {
            # Dateien lesen, Pfade bestimmen
            $orderFiles = $jsonOrderFiles->files;

            $draftPath = DropzoneController::DROPZONE_TMP_PATH;
            $path = OrdersController::ORDER_FILES_PATH . "order_" . $orderToEdit->id . "/";

            foreach ($orderFiles as $orderFile) {
                # Entwurfskennzeichen von Datei entfernen
                $filename = substr($orderFile, 4, strlen($orderFile) - 4);

                # Für angezeigten Dateinamen GUID von Datei entfernen
                $prettyFilename = substr($filename, 9, strlen($filename) - 9);

                # Bild in Storage umbenennen
                UploadController::renameFile($orderFile, $filename, $draftPath, $path);

                # Pfad in Tabelle speichern
                $newOrderDocument = new OrderDocuments();
                $newOrderDocument->order_id = $orderToEdit->id;
                $newOrderDocument->user_id = Auth::user()->id;
                $newOrderDocument->path = $path . $filename;
                $newOrderDocument->prettyName = $prettyFilename;
                $newOrderDocument->locked = 1;

                # In Tabelle Pfad speichern
                try {
                    $newOrderDocument->save();
                } catch (\Exception $e) {
                    Log::error("Fehler beim Hinzufügen von Auftragsdokumenten in Datenbank: " . $e->getMessage());
                    $orderFilesError = true;
                }

                # Log schreiben
                OrderLogsController::writeLog(10, $orderToEdit->id, $loggedUser->operator->id, ["{FILENAME}" => $prettyFilename]);

            }
        }


        # Zum Löschen vorgesehene Dateien "löschen"
        $jsonOrderFilesToDelete = json_decode($request->get("documentsToDelete"));

        if ($jsonOrderFilesToDelete == NULL) {
            Log::error("Fehler beim Löschen von Auftragsdokumenten; JSON-Fehler: " . json_last_error_msg());
            $orderFilesDeleteError = true;
        }
        else {
            if (count($jsonOrderFilesToDelete->documents) > 0) {
                foreach ($jsonOrderFilesToDelete->documents as $fileToDeleteID) {

                    # Auftragsdokument suchen
                    $orderDocument = OrderDocuments::find($fileToDeleteID);

                    if ($orderDocument == null) continue;

                    # Datensatz löschen, Pfad und Dateinamen merken
                    $path = $orderDocument->path;
                    $prettyFilename = $orderDocument->prettyName;

                    try {
                        $orderDocument->delete();
                    } catch (\Exception $e) {
                        $orderFilesDeleteError = true;
                        Log::error("Fehler beim Löschen einer Datei: " . $path . ", Fehler: " . $e->getMessage());
                        continue;
                    }

                    # Datei von Server löschen
                    /*$deleteFile = UploadController::deleteFile($path);

                    if (!$deleteFile) {
                        $orderFilesDeleteError = true;
                        continue;
                    }*/

                    # Log schreiben
                    OrderLogsController::writeLog(11, $orderToEdit->id, $loggedUser->operator->id, ["{FILENAME}" => $prettyFilename]);
                }
            }
        }


        # Vom Bearbeiter verfasste Historieneinträge schreiben
        $customOrderLogs = json_decode($request->get("newLogEntries"));

        if ($customOrderLogs == NULL) {
            Log::error("Fehler beim Hinzufügen von Auftragshistorie-Einträgen; JSON-Fehler: " . json_last_error_msg());
            $orderLogError = true;
        }
        else {
            if (count($customOrderLogs->logs) > 0) {
                foreach ($customOrderLogs->logs as $customOrderLog) {
                    if (!OrderLogsController::writeCustomLog($customOrderLog->text, $customOrderLog->order_id, $customOrderLog->operator_id)) {
                        $orderLogError = true;
                    }
                }
            }
        }

        # Erfolgsmeldung zurückgeben, ggf. mit Hinweisen
        $message = "Der Auftrag " . $orderToEdit->order_number . " wurde erfolgreich gespeichert. ";
        if ($orderTagsError) $message .= "Es gab einen Fehler beim Aktualisieren der Auftragskennzeichen.";
        if ($orderAddressError) $message .= "Es gab einen Fehler beim Aktualisieren der Auftragsadressen.";
        if ($orderFilesError) $message .= "Es gab einen Fehler beim Hinzufügen der Auftragsdokumente.";
        if ($orderFilesDeleteError) $message .= "Es gab einen Fehler beim Löschen von Auftragsdokumente.";
        if ($orderLogError) $message .= "Es gab einen Fehler beim Hinzufügen von Einträgen in der Auftragshistorie.";
        if ($orderAppointmentError) $message .= "Es gab einen Fehler beim Aktualisieren eines Auftragstermins.";
        if ($orderValueError) $message .= "Es gab einen Fehler beim Aktualisieren des Auftragswerts.";
        if ($orderEndDateError) $message .= "Es gab einen Fehler beim Setzen des Endtermins.";
        if ($orderExportDateError) $message .= "Es gab einen Fehler beim Setzen des Exporttermins.";
        if ($responsibleOperatorsError) $message .= "Es gab einen Fehler beim Aktualisieren der verantwortlichen Bearbeiter.";
        if ($orderCreditNoteError) $message .= "Es gab einen Fehler beim Aktualisieren der Gutschriften.";

        if ($orderTagsError || $orderAddressError || $orderFilesError || $orderFilesDeleteError || $orderLogError ||
            $orderAppointmentError || $orderEndDateError || $responsibleOperatorsError || $orderCreditNoteError || $orderExportDateError)
            return redirect()->route('orders.list')->with('okayWithErrors', $message);
        else
            return redirect()->back()->with('message', $message);

    }

    public function getOrderTagsToAdd($request)
    {
        $orderTags = $request->get("ordertags");
        $orderTagsValues = $request->get("ordertagsValues");

        $orderTagsToAdd = array();

        if ($orderTags <> NULL && count($orderTags) > 0) {
            # Gesetzte Auftragskennzeichen erkennen und in Relationstabelle order_order_tag mit Wert eintragen
            foreach ($orderTags as $orderTag => $status) {
                if ($status != "on") continue;

                $addOrderTag = OrderTags::find($orderTag);
                if ($addOrderTag == null) continue;

                $orderTagValue = null;

                # Prüfen, ob für dieses Auftragskennzeichen ein Wert übergeben wurde - wenn nicht, auch kein Problem
                if (array_key_exists($orderTag, $orderTagsValues)) {
                    $orderTagValue = $orderTagsValues[$orderTag];
                }

                # Sammeln
                $orderTagsToAdd[$addOrderTag->id] = ["tag_value" => $orderTagValue];
            }
        }

        return $orderTagsToAdd;
    }

    public function checkDuplicate(Request $request)
    {
        $orderTagsToAdd = $this->getOrderTagsToAdd($request);
        $orderToEdit = $request->get("orderToEdit");
        $orderAddresses = $request->get("address");

        #Duplikatsprüfung: Auftragskennzeichen KLS schon vorhanden
        $duplicateOrders = array();
        $orderTagKls = OrderTags::where("label", "=", "KLS")->get();
        if (array_key_exists($orderTagKls[0]->getAttributes()["id"], $orderTagsToAdd)) {
            $ordersSameKls = Orders::join('order_order_tag', 'order_order_tag.orders_id', '=', 'orders.id')
                ->where("order_order_tag.order_tags_id", "=", $orderTagKls[0]->getAttributes()["id"])
                ->where("orders_id", "!=", $orderToEdit)
                ->where("order_order_tag.tag_value", "=", $orderTagsToAdd[$orderTagKls[0]->getAttributes()["id"]]["tag_value"])
                ->get();
            $duplicateOrders = $ordersSameKls;
        }

        if ($orderAddresses <> NULL && count($orderAddresses) > 0) {
            foreach ($orderAddresses as $orderAddressKey => $jsonOrderAddress) {
                # Adressdaten dekodieren
                $orderAddressData = json_decode($jsonOrderAddress);

                // Zu vergleichende Auftragsnummer
                $orderSearch = 0;
                if (isset($orderAddressData->id)){
                    $orderSearch = $orderAddressData->id;
                }

                #Duplikatsprüfung: Adresse schon vorhanden
                $duplicateAddresses = OrderAddresses::where("name", "=", $orderAddressData->name)
                    ->where("firstname", "=", $orderAddressData->firstname)
                    ->where("street", "=", $orderAddressData->street)
                    ->where("house_number", "=", $orderAddressData->house_number)
                    ->where("zipcode", "=", $orderAddressData->zipcode)
                    ->where("place", "=", $orderAddressData->place)
                    ->where("id", "!=", $orderSearch)->get();
                foreach ($duplicateAddresses as $duplicateAddress) {
                    $duplicate = Orders::find($duplicateAddress->getAttribute("order_id"));
                    $duplicateOrders[] = $duplicate;
                }
            }
        }

        #Duplikatsprüfung
        if (1==2 && count($duplicateOrders) != 0){
            return $this->showDuplicate($duplicateOrders);
        }
        else{
            return response()->json(['response' => 'success']);
        }
    }

    /* Auftrags-Duplikate anzeigen */
    public function showDuplicate($duplicateOrders)
    {
        $arguments = ["activeNavItem" => "userList",
                      "duplicateOrders" => $duplicateOrders];

        # View rendern
        $editorHTML = View::make('order.duplicate')->with($arguments)->render();

        return response()->json(['response' => 'duplicateExists', 'editorHTML' => $editorHTML]);
    }

    /* Auftragssuche via AJAX */
    public function asyncSearch() {

        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("seeOrdersList", $loggedUser))
            return response()->json(['response' => 'error', 'message' => 'Keine Berechtigung.']);

        $noResult = false;
        $now = microtime(true);


        $limit = request()->get("limit");
        $offset = request()->get("offset");
        $jsonSearch = request()->get("searchParameters");
        $jsonSort = request()->get("sortParameters");

        $result = array();

        $decodedSearchJson = json_decode($jsonSearch);
        $decodedSortJson = json_decode($jsonSort);

        if ($decodedSearchJson == null)
            return response()->json(['response' => 'error']);

        if ($decodedSortJson == null)
            return response()->json(['response' => 'error']);


        # Für leichtere Verarbeitung in Array casten
        $searchParameters = (array)$decodedSearchJson;
        $sortParameters = (array)$decodedSortJson;

        $query = DB::table('orders')->distinct()->select("orders.*")
            ->where("orders.deleted_at", "=", null);
        /*
         * FILTERUNG
         */
        $mode = "ordersList";
        $orderCreditNotesJoin = false;

        # Controlling: Monat
        if (array_key_exists("month", $searchParameters) && array_key_exists("year", $searchParameters)) {
            $firstDay = Carbon::createMidnightDate($searchParameters["year"], $searchParameters["month"], 1);
            $searchParameters["dateFrom"] = $firstDay->toDateString();

            $lastDay = $firstDay->endOfMonth();
            $searchParameters["dateTo"] = $lastDay->toDateString();

            $mode = "controllingList";
        }

        # Auftragsnummer - wenn diese eingegeben wird, kommt genau ein Auftrag zurück, also den Rest ignorieren!
        if ($mode == "ordersList" &&
            (array_key_exists("ordernumber", $searchParameters) && $searchParameters["ordernumber"] <> "")) {
            $query = DB::table('orders')->where("order_number", $searchParameters["ordernumber"]);
            $result = $query->get();
        } # Ansonsten aus der restlichen Suchanfrage Query bauen
        else {

            # Aufträge nach Umkreissuche filtern
            # Muss als erstes stehen aufgrund der addBindings!
            if (array_key_exists("zipcodeSearch", $searchParameters) && $searchParameters["zipcodeSearch"] <> ""
                && array_key_exists("circuit", $searchParameters) && $searchParameters["circuit"] <> "") {

                $query->join('order_addresses', "order_addresses.order_id", "=", "orders.id");

                $query->join(DB::raw("(Select * from (SELECT
                  dest.zip,
                  ACOS(
                       SIN(RADIANS(src.lat)) * SIN(RADIANS(dest.lat))
                       + COS(RADIANS(src.lat)) * COS(RADIANS(dest.lat))
                       * COS(RADIANS(src.lon) - RADIANS(dest.lon))
                  ) * 6380 AS distance
                  FROM zip_coordinates dest
                  INNER JOIN (Select * from zip_coordinates
                  WHERE id =
                  (
                    SELECT id
                    FROM zip_coordinates
                    WHERE zip = ?
                    limit 1
                  )) src) as zipresults
                  where zipresults.distance <= ?) as zipcodes"),
                    'zipcodes.zip',
                    '=',
                    'order_addresses.zipcode')
                    ->addBinding($searchParameters["zipcodeSearch"])
                    ->addBinding($searchParameters["circuit"]);
            }

            # Aufträge nach Auftraggeber filtern
            if (array_key_exists("customer", $searchParameters) && $searchParameters["customer"] <> "") {
                $query->where('customer_id', $searchParameters["customer"]);
            }

            // TODO Prüfen, ob diese Vordefinierung wirklich Sinn macht
            if (!isset($searchParameters["noResponsible"]))
                $searchParameters["noResponsible"] = false;

            # Aufträge nach Verantwortlichem Filtern - aber nur, wenn "ohne" nicht angekreuzt!
            if (array_key_exists("responsible", $searchParameters) && $searchParameters["noResponsible"] == false) {
                $query->join('order_operators', 'orders.id', '=', 'order_operators.order_id')
                    ->where('operator_id', '=', $searchParameters["responsible"]);
            }

            # Nur Aufträge, die keinen Verantwortlichen haben
            if ($searchParameters["noResponsible"] == true) {
                $query->leftJoin('order_operators', 'orders.id', '=', 'order_operators.order_id', 'left outer')
                    ->whereNull('order_operators.order_id');
            }

            # Aufträge nach Auftragstyp filtern
            if (array_key_exists("ordertype", $searchParameters) && count($searchParameters["ordertype"]) > 0) {
                $query->whereIn('order_type_id', $searchParameters["ordertype"]);
            }

            # Aufträge nach Auftragsstatus filtern
            if (array_key_exists("orderstatus", $searchParameters) && count($searchParameters["orderstatus"]) > 0) {
                $query->whereIn('order_status_id', $searchParameters["orderstatus"]);
            }

            # Aufträge nach Bezahlstatus filtern
            if (array_key_exists("orderpaymentstatus", $searchParameters) && count($searchParameters["orderpaymentstatus"]) > 0) {
                $query->whereIn('order_payment_status_id', $searchParameters["orderpaymentstatus"]);
            }

            # Aufträge nach Erstelldatum Zeitspanne oder Von/Bis filtern
            if (array_key_exists("dateFrom", $searchParameters) || array_key_exists("dateTo", $searchParameters)) {
                $dateFrom = $dateTo = "";

                # Aufträge nach Datum filtern
                if (array_key_exists("dateFrom", $searchParameters)) {
                    $dateFrom = $searchParameters["dateFrom"];
                }

                if (array_key_exists("dateTo", $searchParameters)) {
                    $dateTo = $searchParameters["dateTo"];
                }

                # Zeitspanne gegeben
                if ($dateFrom <> "" && $dateTo <> "") {
                    # Prüfen, ob Datumsangaben "falsch rum" sind
                    $from = Carbon::createFromFormat("Y-m-d", $dateFrom);
                    $to = Carbon::createFromFormat("Y-m-d", $dateTo);

                    if ($from->gt($to)) {
                        # Tauschen
                        $save = $dateTo;
                        $dateTo = $dateFrom;
                        $dateFrom = $save;
                    }

                    $query->whereBetween(DB::raw('DATE(created)'), array($dateFrom, $dateTo));
                } # Nur ein Datum gegeben
                else {
                    # Von-Suche
                    if ($dateFrom <> "") {
                        $query->where(DB::raw('DATE(created)'), '>=', $dateFrom);
                    }

                    # Bis-Suche
                    if ($dateTo <> "") {
                        $query->where(DB::raw('DATE(created)'), '<=', $dateTo);
                    }
                }

            }


            # Aufträge nach Endtermin Zeitspanne oder Von/Bis filtern
            if (array_key_exists("end_dateFrom", $searchParameters) || array_key_exists("end_dateTo", $searchParameters)) {
                $endDateFrom = $endDateTo = "";

                # Aufträge nach Datum filtern
                if (array_key_exists("end_dateFrom", $searchParameters)) {
                    $endDateFrom = $searchParameters["end_dateFrom"];
                }

                if (array_key_exists("end_dateTo", $searchParameters)) {
                    $endDateTo = $searchParameters["end_dateTo"];
                }

                # Zeitspanne gegeben
                if ($endDateFrom <> "" && $endDateTo <> "") {
                    # Prüfen, ob Datumsangaben "falsch rum" sind
                    $tmpFrom = Carbon::createFromFormat("Y-m-d", $endDateFrom);
                    $tmpTo = Carbon::createFromFormat("Y-m-d", $endDateTo);

                    if ($tmpFrom->gt($tmpTo)) {
                        # Tauschen
                        $tmpSave = $endDateTo;
                        $endDateTo = $endDateFrom;
                        $endDateFrom = $tmpSave;
                    }

                    $query->whereBetween(DB::raw('DATE(end_date)'), array($endDateFrom, $endDateTo));
                } # Nur ein Datum gegeben
                else {
                    # Von-Suche
                    if ($endDateFrom <> "") {
                        $query->where(DB::raw('DATE(end_date)'), '>=', $endDateFrom);
                    }

                    # Bis-Suche
                    if ($endDateTo <> "") {
                        $query->where(DB::raw('DATE(end_date)'), '<=', $endDateTo);
                    }
                }

            }

            # E-Vergabe
            if (array_key_exists("fetchnumber", $searchParameters) && $searchParameters["fetchnumber"] <> "") {
                /*
                $query->join('order_credit_notes', 'orders.id', '=', 'order_credit_notes.order_id')
                    ->where('order_credit_notes.fetch_number', '=', $searchParameters["fetchnumber"]);
                */
                // Auch in den Order Tags prüfen
                $query->join('order_order_tag', 'orders.id', '=', 'order_order_tag.orders_id')
                    ->where('order_order_tag.tag_value', '=', $searchParameters["fetchnumber"])
                    ->where('order_order_tag.order_tags_id', '=', 5);

                $orderCreditNotesJoin = true;
            }

            # Gutschriftnummer
            if (array_key_exists("creditnotenumber", $searchParameters) && $searchParameters["creditnotenumber"] <> "") {

                # Wenn bereits nach E-Vergabe gesucht wurde, ist der Join schon da; dann nur where-Bedingung hinzufügen
                if ($orderCreditNotesJoin)
                    $query->where('order_credit_notes.credit_note_number', '=', $searchParameters["creditnotenumber"]);
                else {
                    $query->join('order_credit_notes', 'orders.id', '=', 'order_credit_notes.order_id')
                        ->where('order_credit_notes.credit_note_number', '=', $searchParameters["creditnotenumber"]);
                    $orderCreditNotesJoin = true;
                }
            }

            # Avis
            if (array_key_exists("referencenumber", $searchParameters) && $searchParameters["referencenumber"] <> "") {

                # Wenn bereits nach E-Vergabe oder Gutschriftnummer gesucht wurde, ist der Join schon da; dann nur where-Bedingung hinzufügen
                if ($orderCreditNotesJoin)
                    $query->where('order_credit_notes.reference_number', '=', $searchParameters["referencenumber"]);
                else
                    $query->join('order_credit_notes', 'orders.id', '=', 'order_credit_notes.order_id')
                        ->where('order_credit_notes.reference_number', '=', $searchParameters["referencenumber"]);
            }

            # Aufträge nach PLZ filtern
            if (array_key_exists("zipcode", $searchParameters) && $searchParameters["zipcode"] <> "") {
                $query->join('order_addresses as oas_zipcode', 'orders.id', '=', 'oas_zipcode.order_id')
                    ->where('oas_zipcode.zipcode', 'like', '%'.$searchParameters["zipcode"].'%');
            }

            # Aufträge nach Ort filtern
            if (array_key_exists("city", $searchParameters) && $searchParameters["city"] <> "") {
                $query->join('order_addresses as oap', 'orders.id', '=', 'oap.order_id')
                    ->where('oap.place', 'like', '%'.$searchParameters["city"].'%');
            }

            # Aufträge nach Straße filtern
            if (array_key_exists("street", $searchParameters) && $searchParameters["street"] <> "") {
                $query->join('order_addresses as oas', 'orders.id', '=', 'oas.order_id')
                    ->where('oas.street', 'like', '%'.$searchParameters["street"].'%');
            }

            # Wenn in Controlling-Liste, Auftragsnummern nur innerhalb des gegebenen Zeitraums berücksichtigen
            if ($mode == "controllingList" &&
                (array_key_exists("ordernumber", $searchParameters) && $searchParameters["ordernumber"] <> "")) {
                $query->where('order_number', '=', $searchParameters["ordernumber"]);
            }

            /*
             * SORTIERUNG
             */
            # Bisher übrig gebliebene Aufträge sortieren, wenn nötig, und aus Datenbank holen

            if (count($sortParameters) > 0) {

                foreach ($sortParameters as $sortParameter => $direction) {

                    # Sortierung nach Auftraggeber (bzw. Firmenname)
                    if ($sortParameter == "customer_id") {
                        $query->join('customers', 'orders.customer_id', '=', 'customers.id')
                            ->addSelect('customers.company')
                            ->orderBy('customers.company', $direction);
                    }

                    # Auftragstyp alphabetisch
                    if ($sortParameter == "order_types_id") {
                        $query->join('order_types', 'orders.order_type_id', '=', 'order_types.id')
                            ->addSelect('order_types.label')
                            ->orderBy('order_types.label', $direction);
                    }

                    # Verantwortlicher nach Nachname alphabetisch
                    /*  if ($sortParameter == "responsible_operator_id") {
                          $query->join('order_operators', 'orders.id', '=', 'order_operators.order_id')
                              ->join('operators', 'order_operators.operator_id', '=', 'operators.id')
                              ->addSelect('operators.name', 'operators.firstname')
                              ->orderBy('operators.name', $direction);
                      }*/

                    # Wohneinheiten
                    if ($sortParameter == "apartment_units") {
                        $query->orderBy('apartment_units', $direction);
                    }

                    # Auftragswert
                    if ($sortParameter == "order_value") {
                        $query->join('order_positions', 'orders.id', 'order_positions.order_id')
                            ->where('order_positions.position_text', '=', 'Auftragswert')
                            ->addSelect('order_positions.position_value')
                            ->orderBy('order_positions.position_value', $direction);
                    }

                    # Auftragsstatus alphabetisch
                    if ($sortParameter == "order_status_id") {
                        $query->join('order_statuses', 'orders.order_status_id', '=', 'order_statuses.id')
                            ->addSelect('order_statuses.label')
                            ->orderBy('order_statuses.label', $direction);
                    }

                    # Bezahlstatus alphabetisch
                    if ($sortParameter == "order_payment_status_id") {
                        $query->join('order_payment_statuses', 'orders.order_payment_status_id', '=', 'order_payment_statuses.id')
                            ->addSelect('order_payment_statuses.label')
                            ->orderBy('order_payment_statuses.label', $direction);
                    }

                    # Erstelldatum
                    if ($sortParameter == "created") {
                        $query->orderBy('created', $direction);
                    }

                    # Endtermin
                    if ($sortParameter == "end_date") {
                        $query->orderBy('end_date', $direction);
                    }

                    # Nächster Termin
                    if ($sortParameter == "next_appointment") {
                        $query->leftJoin(DB::raw("(Select orders_id, min(appointment_from) as appointment_from from order_appointment_operator
                                                   where appointment_from >= CURRENT_DATE()
                                                   group by orders_id) as order_appointment_operator"),
                            'orders.id',
                            '=',
                            'order_appointment_operator.orders_id')
                            ->addSelect('order_appointment_operator.appointment_from')
                            ->orderBy(DB::raw('order_appointment_operator.appointment_from IS NULL, order_appointment_operator.appointment_from'), $direction);
                    }
                }

                // Sortierung nach Auftragsnummer zum Schluss, sonst machen die anderen Kriterien keinen Sinn
                if (array_key_exists('order_number', $sortParameters)) {
                    $query->orderBy('order_number', $sortParameters['order_number']);
                }

            }


            # Mandantenbezug
            $query->where('orders.client_id', '=', $loggedUser->operator->client->id);

            # Ergebnisse holen
            $result = $query->get();

            # Zuletzt Aufträge nach Auftragskennzeichen filtern
            $orderTagsToSearch = (array)$searchParameters["tags"];

            if (count($orderTagsToSearch) > 0) {
                $tempResult = array();

                foreach ($result as $item) {

                    $order = Orders::find($item->id);
                    if ($order == null) continue;

                    if (count($order->order_tags) <= 0) continue;

                    # Für jedes vorgegebene Kennzeichen prüfen, ob dies, ggf. mit Wert, auch im Auftrag vorhanden ist
                    $foundAll = true; # Es müssen alle vorgegebenen Kennzeichen gefunden werden
                    foreach ($orderTagsToSearch as $orderTagToSearch => $orderTagValueToSearch) {

                        $found = false;
                        foreach ($order->order_tags as $order_tag) {

                            # Auf Kennzeichen prüfen
                            if ($orderTagToSearch != $order_tag->id) {
                                $found = false;
                                continue;
                            }

                            # Prüfen, ob Wert vorgegeben
                            if ($orderTagValueToSearch <> "") {
                                if ($orderTagValueToSearch != $order_tag->pivot->tag_value) {
                                    $found = false;
                                    continue;
                                }
                            }

                            $found = true;
                            break;
                        }

                        if (!$found) {
                            $foundAll = false;
                            break;
                        }
                    }

                    if ($foundAll) {
                        $tempResult[] = $order;
                    }
                }

                unset($result);
                $result = $tempResult;
            }
        }

        # Wichtigste Daten für Listenvorblendung zusammensuchen
        $returnOrders = array();

        $offsetCounter = 0;
        $limitCounter = 0;

        $totalResult = 0;
        $totalSum = 0;
        $totalApartmentUnits = 0;

        foreach ($result as $item) {
            $totalResult++;

            # Relevante Auftragsdaten lesen
            $order = Orders::find($item->id);
            if ($order == null) continue;

            # Auftragssumme kumulieren
            $totalSum = $totalSum + $order->getOrderValue();
            $totalApartmentUnits = $totalApartmentUnits + $order->apartment_units;


            if ($limit != -1 && $offsetCounter < $offset) {
                $offsetCounter++;
                continue;
            }

            if ($limit != -1 && $limitCounter >= $limit) continue;

            $orderItem = new \stdClass();
            $orderItem->id = $order->id;
            $orderItem->order_number = $order->order_number;
            $orderItem->customer = $order->customer->company;
            $orderItem->customerId = $order->customer->id;

            if ($order->customer->place <> NULL)
                $orderItem->customerLocation = $order->customer->place;
            else
                $orderItem->customerLocation = "";

            $orderItem->type = $order->order_type->label;
            $orderItem->status = $order->order_status->label;
            $orderItem->statusid = $order->order_status->id;
            $orderItem->payment_status = $order->order_payment_status->label;
            $orderItem->payment_status_id = $order->order_payment_status->id;
            $orderItem->apartmentUnits = $order->apartment_units;
            $orderItem->orderValue = $order->getOrderValueDisplay();
            $orderItem->responsibleOperatorId = $order->responsible_operator_id;
            $orderItem->created = $order->getReadableCreated();
            $orderItem->end_date = $order->getReadableEndDate();

            # Verantwortliche Bearbeiter
            $responsibleOperators = array();
            if (count($order->responsibleOperators) > 0) {
                foreach ($order->responsibleOperators as $responsibleOperator) {
                    $responsibleOperators[$responsibleOperator->id] = $responsibleOperator->getName();
                }
                $orderItem->responsibleOperators = json_encode($responsibleOperators);
            }
            else
                $orderItem->responsibleOperators = "{}";

            # Nächster Termin
            $nextAppointment = OrderAppointmentOperatorController::getNextAppointment($order);

            if ($nextAppointment <> null) {
                $appointmentFrom = \DateTime::createFromFormat("Y-m-d H:i:s", $nextAppointment->appointment_from);
                $orderItem->appointment = $appointmentFrom->format("d.m.Y");
            }
            else {
                $orderItem->appointment = "-";
            }

            # Adressdaten, wenn vorhanden
            $firstOrderAddress = $order->addresses->first();

            $orderItem->location = "-";

            if ($firstOrderAddress <> null) {
                $orderItem->location = "";
                if ($firstOrderAddress->street <> null)
                    $orderItem->location .= $firstOrderAddress->street;
                if ($firstOrderAddress->house_number <> null)
                    $orderItem->location .= " " . $firstOrderAddress->house_number;
                if ($firstOrderAddress->place <> null)
                    $orderItem->location .= ",<br>" . $firstOrderAddress->place;

                // Geolocation
                if ($firstOrderAddress->latitude <> null)
                    $orderItem->latitude = $firstOrderAddress->latitude;
                if ($firstOrderAddress->longitude <> null)
                    $orderItem->longitude = $firstOrderAddress->longitude;
            }

            $returnOrders[] = $orderItem;
            $limitCounter++;
        }

        # Auftragssumme ggf. runden und Dezimalstellen kürzen
        $totalSum = round($totalSum, 2);

        if (count($returnOrders) <= 0) $noResult = true;

        return response()->json(['response' => 'success', 'noResult' => $noResult, 'returnOrders' => json_encode($returnOrders),
            'totalResult' => $totalResult, 'totalSum' => $totalSum, 'totalApartmentUnits' => $totalApartmentUnits]);
    }

    /* Auftrag kopieren */
    public function clone($orderId) {

        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("createOrders", $loggedUser))
            return redirect()->route('index');

        # Errorflag
        $cloneError = false;

        $now = Carbon::now();

        # Grunddaten
        $order = Orders::find($orderId);

        if ($order == null)
            return redirect()->back()->withErrors('Auftrag nicht gefunden.');


        $cloneOrder = $order->replicate();
        $cloneOrder->created = $now->toDateTimeString();

        # Neue Auftragsnummer zuweisen
        $cloneOrder->order_number = $loggedUser->client->numberRangeOrders->getNextNumber();

        try {
            $cloneOrder->save();
        } catch (\Exception $e) {
            Log::error("Fehler beim Auftrag kopieren: " . $e->getMessage());
            return redirect()->back()->withErrors(["Es ist ein Fehler aufgetreten."]);
        }

        # Relations

        # Termine werden nicht geklont - ergibt in diesem Anwendungsfall wenig Sinn


        # Adressen
        foreach ($order->addresses as $address) {
            $newAddress = $address->replicate();
            $newAddress->order_id = $cloneOrder->id;

            try {
                $newAddress->save();
            } catch (\Exception $e) {
                Log::error("Fehler beim Auftrag kopieren (Auftragsadressen): " . $e->getMessage());
                $cloneError = true;
            }
        }

        # Dokumente
        foreach ($order->documents as $document) {
            $newDocument = $document->replicate();
            $newDocument->order_id = $cloneOrder->id;

            # Dokument auch auf Festplatte kopieren
            $newPath = OrdersController::ORDER_FILES_PATH . "order_" . $cloneOrder->id . "/";

            $pathinfo = pathinfo($newDocument->path);

            $oldPath = $pathinfo["dirname"] . "/";
            $filename = $pathinfo["basename"];

            if (!UploadController::copyFile($filename, $filename, $oldPath, $newPath)) {
                Log::error("Fehler beim Auftrag kopieren (Auftragsdokumente auf Festplatte kopieren)");
                $cloneError = true;
            }

            try {
                $newDocument->save();
            } catch (\Exception $e) {
                Log::error("Fehler beim Auftrag kopieren (Auftragsdokumente): " . $e->getMessage());
                $cloneError = true;
            }
        }

        # Wiedervorlagen werden nicht kopiert
        # Hier wird lediglich eine neue Wiedervorlage für den kopierenden Bearbeiter gesetzt
        $nextUp = (new Carbon())->addWeek(1);
        OrderFollowUpController::createNewFollowUp($cloneOrder->id, $loggedUser->operator->id, "", $nextUp->toDateString());

        # Auftragshistorie und -emails werden ebenfalls nicht kopiert, ergibt keinen Sinn
        # Lediglich neuer Logeintrag, von wo der Auftrag kopiert wurde
        OrderLogsController::writeLog(18, $cloneOrder->id, $loggedUser->operator->id,
            ["{OLDORDERNUMBER}" => $order->order_number, "{NEWORDERNUMBER}" => $cloneOrder->order_number]);

        # Auftragskennzeichen
        foreach ($order->order_tags as $order_tag) {
            try {
                $cloneOrder->order_tags()->save($order_tag, ["tag_value" => $order_tag->pivot->tag_value]);
            } catch (\Exception $e) {
                Log::error("Fehler beim Auftrag kopieren (Auftragskennzeichen): " . $e->getMessage());
                $cloneError = true;
            }
        }

        # Auftragspositionen
        foreach ($order->positions as $position) {
            $newPosition = $position->replicate();
            $newPosition->order_id = $cloneOrder->id;

            try {
                $newPosition->save();
            } catch (\Exception $e) {
                Log::error("Fehler beim Auftrag kopieren (Auftragspositionen): " . $e->getMessage());
                $cloneError = true;
            }
        }


        $message = "Der Auftrag " . $order->order_number . " wurde erfolgreich kopiert. Die Auftragsnummer des neuen Auftrags lautet " . $cloneOrder->order_number . ".";

        if ($cloneError) {
            $message .= " Es konnten nicht alle Daten kopiert werden.";
            return redirect()->route('orders.list')->with('okayWithErrors', $message);
        }

        return redirect()->back()->with("message", $message);
    }

    /* Datenblatt ausgeben */
    public function makeDatasheetPdf($orderId) {

        # Berechtigung prüfen
        $loggedUser = User::find(Auth::user()->id);

        # Berechtigung prüfen
        if (!UserController::checkRight("seeOrdersList", $loggedUser))
            return redirect()->route('index');

        # Daten holen
        $order = Orders::find($orderId);
        if ($order == null)
            return redirect()->back()->withErrors("Auftragsdaten nicht gefunden.");

        $parameters = array();

        $parameters["ordernumber"] = $order->order_number;
        $parameters["customer"] = $order->customer->company;
        $parameters["ordertype"] = $order->order_type->label;
        $parameters["end_date"] = $order->getReadableEndDate();

        # Nur erste Adresse des Auftrags
        $parameters["address"] = "-";
        if (count($order->addresses) > 0) {
            $firstOrderAddress = $order->addresses->first();


            if ($firstOrderAddress <> null) {
                $parameters["address"] = "";

                if ($firstOrderAddress->place <> null)
                    $parameters["address"] .= $firstOrderAddress->place;
                if ($firstOrderAddress->street <> null)
                    $parameters["address"] .= ", " . $firstOrderAddress->street;
                if ($firstOrderAddress->house_number <> null)
                    $parameters["address"] .= " " . $firstOrderAddress->house_number;
            }
        }

        # KLS
        $kls = $order->order_tags()->where("order_tags_id", 2)->first();
        if ($kls <> null) {
            $parameters["kls"] = $kls->pivot->tag_value;
        }
        else $parameters["kls"] = "-";


        # Bearbeiter
        $parameters["operators"] = "-";
        if (count($order->operators) > 0) {
            $parameters["operators"] = "";
            foreach ($order->operators as $operator) {
                $parameters["operators"] .= " " . $operator->getName() . ",";
            }

            # Letztes Komma raus
            $parameters["operators"] = substr($parameters["operators"], 0, strlen($parameters["operators"]) - 1);
        }

        # eVergabe-Nummer
        $parameters["fetchnumbers"] = "-";

        # Sammeln
        if (count($order->creditNotes) > 0) {

            $fetchnumbers = array();

            foreach ($order->creditNotes as $creditNote) {
                $fetchnumbers[] = $creditNote->fetch_number;
            }

            $fetchnumbers = array_unique($fetchnumbers);

            $parameters["fetchnumbers"] = implode(", ", $fetchnumbers);
        }


        # PDF erstellen
        # https://github.com/barryvdh/laravel-dompdf
        PDF::setOptions(['dpi' => 300, 'defaultFont' => 'sans-serif', 'defaultPaperSize' => 'A4']);

        $pdfName = "datenblatt_" . $order->id . ".pdf";

        # PDF generieren
        $pdf = PDF::loadView('order.datasheetPdf', $parameters);

        # PDF ausgeben
        return $pdf->download($pdfName);
    }

    // function to geocode address, it will return false if unable to geocode address
    public static function geocode($address){

        ini_set("allow_url_fopen", 1);

        // url encode the address
        $address = urlencode($address);

        // google map geocode api url
		try
		{
			$url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&key=AIzaSyBeGVz5s7oPWcEQ6flHVPOD2duKebRd3UY";

			// get the json response
			$resp_json = file_get_contents($url);
            // decode the json
            $resp = json_decode($resp_json, true);
		}
		catch (Exception $e)
		{
			Log::error($e);
			$resp = array("status" => "NOK");
		}

        // response status will be 'OK', if able to geocode given address
        if($resp['status']=='OK'){

            // get the important data
            $lati = isset($resp['results'][0]['geometry']['location']['lat']) ? $resp['results'][0]['geometry']['location']['lat'] : "";
            $longi = isset($resp['results'][0]['geometry']['location']['lng']) ? $resp['results'][0]['geometry']['location']['lng'] : "";
            $formatted_address = isset($resp['results'][0]['formatted_address']) ? $resp['results'][0]['formatted_address'] : "";

            // verify if data is complete
            if($lati && $longi && $formatted_address){

                // put the data in the array
                $data_arr = array();

                array_push(
                    $data_arr,
                    $lati,
                    $longi,
                    $formatted_address
                );

                return $data_arr;

            }else{
                return false;
            }

        }

        else{
            return false;
        }
    }


}
