public function ajaxProcessSaveOrder()
    {
        $products = Tools::getValue('products');
        $delivery_date = Tools::getValue('delivery_date');
        $delivery_time_from = Tools::getValue('delivery_time_from');
        $delivery_time_to = Tools::getValue('delivery_time_to');
        $id_employee = Tools::getValue('employees');
        if (!empty($id_employee) && is_array($id_employee)) {
            $id_employee = $id_employee[0];
        }
        $other = Tools::getValue('other');
        if (empty($products)) {
            PrestaShopLogger::addLog('AphCalendar::saveOrder - Products to be select', 1, null, 'AphCalendar', 0, true);
            die(Tools::jsonEncode(array('result' => false, 'error' => 'Non e\' stato selezionato nessun prodotto. Prego selezionarle almeno uno.')));
        }
        $id_customer = (int) Tools::getValue('id_customer');
        if ($id_customer < 1) {
            $customer = new Customer();
            $customer->firstname = Tools::getValue('firstname');
            $customer->lastname = Tools::getValue('lastname');
            $customer->email = Tools::getValue('email');
            $customer->phone = Tools::getValue('phone');
            $customer->id_gender = Tools::getValue('id_gender');
            $customer->id_shop = (int) Context::getContext()->shop->id;
            $customer->passwd = strtoupper(Tools::passwdGen(10));
            $customer->newsletter = 1;
            if ($customer->validateFields(false, true) !== true) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Fields of customer not valid', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione del cliente. Prego riprovare.')));
            }
            $result = $customer->add();
            if (!$result) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Address of customer is to be added', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione del cliente. Prego riprovare.')));
            }
            $stores = Db::getInstance()->executeS('
			SELECT st.id_country,st.id_state,st.city
			FROM ' . _DB_PREFIX_ . 'store_shop ss
			LEFT JOIN `' . _DB_PREFIX_ . 'store` st 
				ON (ss.`id_store` = st.`id_store`)
			WHERE ss.`id_shop` = ' . (int) Context::getContext()->shop->id);
            $address = new Address();
            $address->id_customer = $customer->id;
            $address->alias = 'indirizzo';
            $address->firstname = $customer->firstname;
            $address->lastname = $customer->lastname;
            $address->address1 = '-';
            $address->postcode = '00000';
            $address->phone = $customer->phone;
            $address->phone_mobile = $customer->phone;
            $address->id_country = $stores[0]['id_country'];
            $address->id_state = $stores[0]['id_state'];
            $address->city = $stores[0]['city'];
            if ($address->validateFields(false, true) !== true) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Fields of address of customer not valid', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione del cliente. Prego riprovare.')));
            }
            $address->add();
            $customer->id_address_delivery = $address->id;
            $customer->id_address_invoice = $address->id;
            if (!$result) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Customer is to be added', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione del cliente. Prego riprovare.')));
            }
            $id_customer = $customer->id;
        } else {
            $customer = new Customer($id_customer);
            $customer->firstname = Tools::getValue('firstname');
            $customer->lastname = Tools::getValue('lastname');
            $customer->email = Tools::getValue('email');
            $customer->phone = Tools::getValue('phone');
            $customer->id_gender = Tools::getValue('id_gender');
            $customer->id_shop = (int) Context::getContext()->shop->id;
            $customer->update();
            $addresses = $customer->getAddresses((int) Context::getContext()->language->id);
            if (empty($addresses)) {
                $customer->id_address_delivery = $customer->id_address_invoice = 0;
            } else {
                $customer->id_address_delivery = $addresses[0]['id_address'];
                $customer->id_address_invoice = $addresses[0]['id_address'];
                $address = new Address($addresses[0]['id_address'], (int) Context::getContext()->language->id);
                $address->firstname = $customer->firstname;
                $address->lastname = $customer->lastname;
                $address->phone = $customer->phone;
                $address->phone_mobile = $customer->phone;
                $address->update();
            }
        }
        $id_order = (int) Tools::getValue('id_order');
        $feature_duration = Configuration::get('APH_FEATURE_DURATION');
        $services_duration = json_decode(Configuration::get('APH_SERVICES_DURATION'), true);
        $reservation_offline_status = Configuration::get('APH_RESERVATION_OFFLINE_STATUS');
        // always add taxes even if there are not displayed to the customer
        $use_taxes = true;
        // Total method
        $total_method = Cart::BOTH_WITHOUT_SHIPPING;
        //TODO ajaxProcessAddProductOnOrder() in AdminOrdersController
        if ($id_order < 1) {
            do {
                $reference = Order::generateReference();
            } while (Order::getByReference($reference)->count());
            $order = new Order();
            $order->id_customer = (int) $customer->id;
            $order->secure_key = $customer->secure_key;
            $order->id_address_invoice = $customer->id_address_delivery;
            $order->id_address_delivery = $customer->id_address_invoice;
            $order->id_currency = (int) Context::getContext()->currency->id;
            $order->id_lang = (int) Context::getContext()->language->id;
            $order->reference = $reference;
            $order->id_shop = (int) Context::getContext()->shop->id;
            $order->id_shop_group = (int) Context::getContext()->shop->id_shop_group;
            $order->id_cart = 0;
            $order->id_carrier = 0;
            $order->payment = 'Pagamento alla consegna';
            $order->module = 'cashondelivery';
            $order->total_paid = 0;
            $order->total_paid_real = 0;
            $order->total_products = 0;
            $order->total_products_wt = 0;
            $order->conversion_rate = 1;
            $order->delivery_number = 1;
            $order->delivery_date = $delivery_date . ' ' . $delivery_time_from;
            $order->current_state = $reservation_offline_status;
            if ($order->validateFields(false, true) !== true) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Fields of order not valid', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione dell\'appuntamento. Prego riprovare.')));
            }
            $result = $order->add();
            if (!$result) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Order is about to be added', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante la creazione dell\'appuntamento. Prego riprovare.')));
            }
            // Create new cart
            $cart = new Cart();
            $cart->id_shop_group = $order->id_shop_group;
            $cart->id_shop = $order->id_shop;
            $cart->id_customer = $order->id_customer;
            $cart->id_carrier = $order->id_carrier;
            $cart->id_address_delivery = $order->id_address_delivery;
            $cart->id_address_invoice = $order->id_address_invoice;
            $cart->id_currency = $order->id_currency;
            $cart->id_lang = $order->id_lang;
            $cart->secure_key = $order->secure_key;
            // Save new cart
            $cart->add();
            // Save context (in order to apply cart rule)
            $this->context->cart = $cart;
            $this->context->customer = new Customer($order->id_customer);
            // calculate prices of products
            $products_detail = array();
            foreach ($products as &$product_id) {
                $product = new Product($product_id, false, $order->id_lang, $order->id_shop);
                $products_detail[$product_id] = array();
                $products_detail[$product_id]['id'] = $products_detail[$product_id]['id_product'] = $product_id;
                $products_detail[$product_id]['name'] = $product->name;
                $products_detail[$product_id]['ean13'] = $product->ean13;
                $products_detail[$product_id]['upc'] = $product->upc;
                $products_detail[$product_id]['reference'] = $product->reference;
                $products_detail[$product_id]['cart_quantity'] = 1;
                $products_detail[$product_id]['id_product_attribute'] = 0;
                $products_detail[$product_id]['id_shop'] = $order->id_shop;
                $products_detail[$product_id]['id_supplier'] = 0;
                $products_detail[$product_id]['weight'] = $product->weight;
                $products_detail[$product_id]['height'] = $product->height;
                $products_detail[$product_id]['depth'] = $product->depth;
                $products_detail[$product_id]['ecotax'] = $product->ecotax;
                $products_detail[$product_id]['price_without_reduction'] = Product::getPriceStatic((int) $product_id, true, isset($row['id_product_attribute']) ? (int) $row['id_product_attribute'] : null, 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                $products_detail[$product_id]['price_with_reduction'] = Product::getPriceStatic((int) $product_id, true, isset($row['id_product_attribute']) ? (int) $row['id_product_attribute'] : null, 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                $products_detail[$product_id]['price'] = $products_detail[$product_id]['price_with_reduction_without_tax'] = Product::getPriceStatic((int) $product_id, false, $products_detail[$product_id]['id_product_attribute'], 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                switch (Configuration::get('PS_ROUND_TYPE')) {
                    case Order::ROUND_TOTAL:
                        $products_detail[$product_id]['total'] = $products_detail[$product_id]['price_with_reduction_without_tax'] * (int) $products_detail[$product_id]['cart_quantity'];
                        $products_detail[$product_id]['total_wt'] = $products_detail[$product_id]['price_with_reduction'] * (int) $products_detail[$product_id]['cart_quantity'];
                        break;
                    case Order::ROUND_LINE:
                        $products_detail[$product_id]['total'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction_without_tax'] * (int) $products_detail[$product_id]['cart_quantity'], _PS_PRICE_COMPUTE_PRECISION_);
                        $products_detail[$product_id]['total_wt'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction'] * (int) $products_detail[$product_id]['cart_quantity'], _PS_PRICE_COMPUTE_PRECISION_);
                        break;
                    case Order::ROUND_ITEM:
                    default:
                        $products_detail[$product_id]['total'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction_without_tax'], _PS_PRICE_COMPUTE_PRECISION_) * (int) $products_detail[$product_id]['cart_quantity'];
                        $products_detail[$product_id]['total_wt'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction'], _PS_PRICE_COMPUTE_PRECISION_) * (int) $products_detail[$product_id]['cart_quantity'];
                        break;
                }
                $products_detail[$product_id]['price_wt'] = $products_detail[$product_id]['price_with_reduction'];
                $products_detail[$product_id]['reduction_applies'] = $specific_price_output && (double) $specific_price_output['reduction'];
                $products_detail[$product_id]['wholesale_price'] = $product->wholesale_price;
                $products_detail[$product_id]['additional_shipping_cost'] = $product->additional_shipping_cost;
                // Add product to cart
                $update_quantity = $cart->updateQty($products_detail[$product_id]['cart_quantity'], $product->id, $products_detail[$product_id]['id_product_attribute'], null, 'up', 0, new Shop($cart->id_shop));
                $order_detail = new AphOrderDetail();
                $order_detail->createList($order, $cart, $order->current_state, array($products_detail[$product_id]), 0, $use_taxes, 0);
                // update totals amount of order
                $order->total_products += (double) $cart->getOrderTotal(false, Cart::ONLY_PRODUCTS);
                $order->total_products_wt += (double) $cart->getOrderTotal($use_taxes, Cart::ONLY_PRODUCTS);
                $order->total_paid += Tools::ps_round((double) $cart->getOrderTotal(true, $total_method), 2);
                $order->total_paid_tax_excl += Tools::ps_round((double) $cart->getOrderTotal(false, $total_method), 2);
                $order->total_paid_tax_incl += Tools::ps_round((double) $cart->getOrderTotal($use_taxes, $total_method), 2);
                // discount
                $order->total_discounts += (double) abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS));
                $order->total_discounts_tax_excl += (double) abs($cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS));
                $order->total_discounts_tax_incl += (double) abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS));
                // Save changes of order
                $order->id_cart = $cart->id;
                $order->update();
                // Update Tax lines
                $order_detail->updateTaxAmount($order);
                // duration event
                $features = $product->getFeatures();
                foreach ($features as &$feature) {
                    if ($feature_duration == $feature['id_feature']) {
                        $products_detail[$product_id]['duration'] = (int) $services_duration[$feature['id_feature_value']];
                    }
                }
                $order_detail->id_employee = $id_employee;
                $order_detail->delivery_date = $delivery_date;
                $order_detail->delivery_time_from = $delivery_time_from;
                if (!empty($products_detail[$product_id]['duration'])) {
                    $time = new DateTime($delivery_date . ' ' . $delivery_time_from);
                    $time->add(new DateInterval('PT' . $products_detail[$product_id]['duration'] . 'M'));
                    $time_to = $time->format('H:i');
                    if ($time_to > $delivery_time_to) {
                        $delivery_time_to = $time_to;
                    }
                }
                $order_detail->delivery_time_to = $delivery_time_to;
                $order_detail->note = $other;
                $order_detail->update();
            }
        } else {
            $order = new Order($id_order);
            $order->id_customer = (int) $customer->id;
            $order->secure_key = $customer->secure_key;
            $order->id_address_invoice = $customer->id_address_delivery;
            $order->id_address_delivery = $customer->id_address_invoice;
            $order->id_currency = (int) Context::getContext()->currency->id;
            $order->id_lang = (int) Context::getContext()->language->id;
            $order->total_paid = 0;
            $order->total_paid_real = 0;
            $order->total_products = 0;
            $order->total_products_wt = 0;
            $order->conversion_rate = 1;
            $order->delivery_number = 1;
            $order->delivery_date = $delivery_date . ' ' . $delivery_time_from;
            if ($order->validateFields(false, true) !== true) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Fields of order not valid', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante l\'aggiornamento dell\'appuntamento. Prego riprovare.')));
            }
            $result = $order->update();
            if (!$result) {
                PrestaShopLogger::addLog('AphCalendar::saveOrder - Order is about to be added', 1, null, 'AphCalendar', 0, true);
                die(Tools::jsonEncode(array('result' => false, 'error' => 'Si e\' verificato un problema durante l\'aggiornamento dell\'appuntamento. Prego riprovare.')));
            }
            // Create new cart
            $cart = new Cart($order->id_cart);
            // Save context (in order to apply cart rule)
            $this->context->cart = $cart;
            $this->context->customer = new Customer($order->id_customer);
            $id_order_detail = Tools::getValue('id_order_detail');
            $order_detail = new AphOrderDetail($id_order_detail);
            if (empty($products_detail[$order_detail->product_id])) {
                $order_detail->delete();
            }
            // calculate prices of products
            $products_detail = array();
            $is_to_update = false;
            foreach ($products as &$product_id) {
                $is_to_update = $product_id == $order_detail->product_id;
                $product = new Product($product_id, false, $order->id_lang, $order->id_shop);
                $products_detail[$product_id] = array();
                $products_detail[$product_id]['id'] = $products_detail[$product_id]['id_product'] = $product_id;
                $products_detail[$product_id]['name'] = $product->name;
                $products_detail[$product_id]['ean13'] = $product->ean13;
                $products_detail[$product_id]['upc'] = $product->upc;
                $products_detail[$product_id]['reference'] = $product->reference;
                $products_detail[$product_id]['cart_quantity'] = 1;
                $products_detail[$product_id]['id_product_attribute'] = 0;
                $products_detail[$product_id]['id_shop'] = $order->id_shop;
                $products_detail[$product_id]['id_supplier'] = 0;
                $products_detail[$product_id]['weight'] = $product->weight;
                $products_detail[$product_id]['height'] = $product->height;
                $products_detail[$product_id]['depth'] = $product->depth;
                $products_detail[$product_id]['ecotax'] = $product->ecotax;
                $products_detail[$product_id]['price_without_reduction'] = Product::getPriceStatic((int) $product_id, true, isset($row['id_product_attribute']) ? (int) $row['id_product_attribute'] : null, 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                $products_detail[$product_id]['price_with_reduction'] = Product::getPriceStatic((int) $product_id, true, isset($row['id_product_attribute']) ? (int) $row['id_product_attribute'] : null, 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                $products_detail[$product_id]['price'] = $products_detail[$product_id]['price_with_reduction_without_tax'] = Product::getPriceStatic((int) $product_id, false, $products_detail[$product_id]['id_product_attribute'], 6, null, false, true, $products_detail[$product_id]['cart_quantity'], false, $order->id_customer, (int) $cart->id, $order->id_address_invoice, $specific_price_output, true, true, $this->context);
                switch (Configuration::get('PS_ROUND_TYPE')) {
                    case Order::ROUND_TOTAL:
                        $products_detail[$product_id]['total'] = $products_detail[$product_id]['price_with_reduction_without_tax'] * (int) $products_detail[$product_id]['cart_quantity'];
                        $products_detail[$product_id]['total_wt'] = $products_detail[$product_id]['price_with_reduction'] * (int) $products_detail[$product_id]['cart_quantity'];
                        break;
                    case Order::ROUND_LINE:
                        $products_detail[$product_id]['total'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction_without_tax'] * (int) $products_detail[$product_id]['cart_quantity'], _PS_PRICE_COMPUTE_PRECISION_);
                        $products_detail[$product_id]['total_wt'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction'] * (int) $products_detail[$product_id]['cart_quantity'], _PS_PRICE_COMPUTE_PRECISION_);
                        break;
                    case Order::ROUND_ITEM:
                    default:
                        $products_detail[$product_id]['total'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction_without_tax'], _PS_PRICE_COMPUTE_PRECISION_) * (int) $products_detail[$product_id]['cart_quantity'];
                        $products_detail[$product_id]['total_wt'] = Tools::ps_round($products_detail[$product_id]['price_with_reduction'], _PS_PRICE_COMPUTE_PRECISION_) * (int) $products_detail[$product_id]['cart_quantity'];
                        break;
                }
                $products_detail[$product_id]['price_wt'] = $products_detail[$product_id]['price_with_reduction'];
                $products_detail[$product_id]['reduction_applies'] = $specific_price_output && (double) $specific_price_output['reduction'];
                $products_detail[$product_id]['wholesale_price'] = $product->wholesale_price;
                $products_detail[$product_id]['additional_shipping_cost'] = $product->additional_shipping_cost;
                // Add product to cart
                $update_quantity = $cart->updateQty($products_detail[$product_id]['cart_quantity'], $product->id, $products_detail[$product_id]['id_product_attribute'], null, 'up', 0, new Shop($cart->id_shop));
                $order_detail = new AphOrderDetail();
                $order_detail->createList($order, $cart, $order->current_state, array($products_detail[$product_id]), 0, $use_taxes, 0);
                // update totals amount of order
                $order->total_products += (double) $cart->getOrderTotal(false, Cart::ONLY_PRODUCTS);
                $order->total_products_wt += (double) $cart->getOrderTotal($use_taxes, Cart::ONLY_PRODUCTS);
                $order->total_paid += Tools::ps_round((double) $cart->getOrderTotal(true, $total_method), 2);
                $order->total_paid_tax_excl += Tools::ps_round((double) $cart->getOrderTotal(false, $total_method), 2);
                $order->total_paid_tax_incl += Tools::ps_round((double) $cart->getOrderTotal($use_taxes, $total_method), 2);
                // discount
                $order->total_discounts += (double) abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS));
                $order->total_discounts_tax_excl += (double) abs($cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS));
                $order->total_discounts_tax_incl += (double) abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS));
                // Save changes of order
                $order->id_cart = $cart->id;
                $order->update();
                // Update Tax lines
                $order_detail->updateTaxAmount($order);
                // duration event
                $features = $product->getFeatures();
                foreach ($features as &$feature) {
                    if ($feature_duration == $feature['id_feature']) {
                        $products_detail[$product_id]['duration'] = (int) $services_duration[$feature['id_feature_value']];
                    }
                }
                $order_detail->id_employee = $id_employee;
                $order_detail->delivery_date = $delivery_date;
                $order_detail->delivery_time_from = $delivery_time_from;
                if (!empty($products_detail[$product_id]['duration'])) {
                    $time = new DateTime($delivery_date . ' ' . $delivery_time_from);
                    $time->add(new DateInterval('PT' . $products_detail[$product_id]['duration'] . 'M'));
                    $time_to = $time->format('H:i');
                    if ($time_to > $delivery_time_to) {
                        $delivery_time_to = $time_to;
                    }
                }
                $order_detail->delivery_time_to = $delivery_time_to;
                $order_detail->note = $other;
                if ($is_to_update) {
                    $order_detail->update();
                } else {
                    $order_detail->add();
                }
            }
        }
        $send_memo = (bool) Tools::getValue('send_memo');
        if (!empty($send_memo)) {
            $shop = new AphStore($products_detail[$product_id]['id_shop']);
            $topic = 'Promemoria appuntamento';
            $data = array('{lastname}' => $customer->lastname, '{firstname}' => $customer->firstname, '{id_order}' => (int) $order->id, '{order_name}' => $order->getUniqReference(), '{product_name}' => $products_detail[$product_id]['name'], '{delivery_date}' => $order_detail->delivery_date, '{delivery_time_from}' => $order_detail->delivery_time_from, '{shop_name}' => $shop->name, '{shop_address}' => $shop->shop_address1 . (!empty($shop->shop_address2) ? ' ' . $shop->shop_address2 : ''), '{shop_city}' => $shop->shop_city, '{shop_link_rewrite}' => $shop->shop_link_rewrite, '{shop_phone}' => $shop->phone);
            if (Validate::isLoadedObject($order)) {
                !Mail::Send((int) $order->id_lang, 'order_memo', $topic, $data, $customer->email, $customer->firstname . ' ' . $customer->lastname, null, null, false, null, _PS_MAIL_DIR_, false, (int) $order->id_shop);
            }
        }
        die(Tools::jsonEncode(array('result' => true)));
    }