HEX
Server: LiteSpeed
System: Linux s917.lon1.mysecurecloudhost.com 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: assibfaf (3034)
PHP: 7.4.33
Disabled: NONE
Upload Files
File: /home/assibfaf/public_html/prox-classes/Order/Order.php
<?php
/**
 * @package    Proxim
 * @author     Davison Pro <davisonpro.coder@gmail.com | https://davisonpro.dev>
 * @copyright  2019 Proxim
 * @version    1.0.0
 * @since      File available since Release 1.0.0
 */

namespace Proxim\Order;

use Db;
use Proxim\Database\DbQuery;
use Proxim\Cache\Cache;
use Proxim\ObjectModel;
use Proxim\Order\File;
use Proxim\PriceCalculator;
use Proxim\Configuration;
use Proxim\Currency;
use Proxim\Coupon;
use Proxim\Preference\PaperFormat;
use Proxim\Preference\PaperType;
use Proxim\User\Customer;
use Proxim\User\Employee;
use Proxim\Util\ArrayUtils;
use Proxim\Util\DateUtils;
use Proxim\Validate;
use Proxim\Tools;

/**
 * Order
 */
class Order extends ObjectModel
{
    const START_ORDER_ID = 11233;

    /**
	 * Dispute - is the status of the order when customer is not satisfied
	 * with the quality of the paper, or this order was delivered late and
	 * customer wants to receive a refund.
	 *
	 * We have a dispute manager who deals with all the disputed orders,
	 * however Support Team Members should know basic information about it.
	 */
	const DISPUTE = 1; 
	/**
	 * When customer tries to pay for the order, but his payment doesn't
	 * go through, we receive "Fake" order.
	 *
	 * Basically, this status means that order has 96% possibility to become
	 * a sale, as customer had filled out the whole form and clicked on "Pay
	 * for the order")
	 *
	 * When you receive such order, it is your responsibility to contact
	 * customer and to help him to complete the payment.
	 */
	const FAKE = 0;
	/**
	 * Confirmation Pending - inquiry with instructions for the writer to
	 * start working on it, we just need customer to proceed with the payment.
	 */
	const CONFIRMATION_PENDING = 2;
	
	/**
	 * Writer Assigned - is a paid order that was assigned to a specific writer
	 */
	const WRITER_ASSIGNED = 6;

	/**
	 * Writer Accepted - is a paid order that was already published, and few writers applied for it.
	 * Such orders have a list of writers who are willing to work on them.
	 * At this point supporter has to assign this order to one of the writers
	 */
	const WRITER_ACCEPTED = 7;
	
	/**
	 * Done – status of the order when we receive completed paper from the writer.
	 * At this point customer doesn't have access to this order yet.
	 * Supporter needs to check this paper for plagiarism, paper format, etc.
	 *
	 * In case if there is plagiarism or this order was written in a wrong format,
	 * it should be set on "Revision" status.
	 *
	 * If the order is completed correctly, then the preview version should be sent to customer.
	 */
	const DONE = 8;
	/**
	 * Order can move to "Canceled" status at any stage.
	 * But there are several main reasons to "cancel" the order:
	 *
	 * Customer doesn't want to pay for his inquiry
	 * Customer accidentally placed 2 or more identical inquiries or orders.
	 * Writer wasn't assigned and we issued customer a full refund
	 *  
	 */
	const CANCELLED = 9;
	/**
	 * Order moves to Finished  status when customer approves the order
	 * and receives MS Word version of the paper.
	 *
	 * By approving the order, customer confirms that product is of expected quality.
	 */
	const FINISHED = 10;
	/**
	 * This status means that the Preview version of the order is available for customer
	 * and he/she can review it in .pdf format.
	 *
	 * Preview version is protected with a password,
	 * so technically customer cannot print or copy/paste it.
	 */
	const DELIVERED = 11;
	/**
	 * New Paid - is a status of a new order that we receive from
	 * customer. This is a stage when order is paid.

	* At this point order is not visible for writers.

	* You have to check such order and make sure that there is no customer's
	* personal information and that we have all required instructions.
	*/
	const NEW_PAID = 12;
	/**
	 *  Free Inquiry -  is a transitional status. When we receive it, we
	 *  have to check instructions and call customer to turn this inquiry into
	 *  a paid order (thus status will be changed to "New Paid").
	 *
	 *  In case if we can not contact customer, or he/she refuses to
	 *  complete the payment, status will be changed as well ("Reply Pending",
	 *  "Confirmation Pending", "Canceled")
	 */
	const FREE_INQUIRY = 13;
	/**
	 * Revision – Order is in a process of editing and revising.
	 *
	 * It is necessary when customer is not satisfied with the quality of a product.
	 *
	 * Also order can move to this status when plagiarism or
	 * format mistakes were found by Support Team Member.
	 */
    const REVISION = 14;
    
	/**
	 * Reply Pending - inquiry with the lack of instructions for us to look
	 * for the writer, thus we need to ask customer to specify/add instructions
	 * for the assignment.
	 *
	 * When we will receive such information customer can proceed with
	 * the payment.
	 */
	const REPLY_PENDING = 15;
	/**
	 * This status visually looks like "Order Published," but it is NOT
	 * PAID. Orders with such status are visible for writers.
	 *
	 * This status should be applied in following cases:
	 *
	 * - big orders over $300
	 * - difficult topic: programming for ex.
	 * - specific sources are required
	 */
	const INQUERY_PUBLISHED = 16;
	/**
	 * When writers apply for orders in "Inquiry Published" status,
	 * they move to "Inquiry Writer Accepted" status.
	 *
	 * Keep in mind that it's not paid yet, so we cannot assign this order
	 * to the writer until we will receive the payment.
	 */
	const INQUERY_WRITER_ACCEPTED = 17;
	/**
	 * Order published - is a paid order, which was checked by the supporter
	 * on personal information, deadline, attachments.
	 *
	 * At this point order is visible for all writers and they can place their bids.
	 */
	const ORDER_PUBLISHED = 19;
	const CONVERT_FAILED = 20;
	const CALL_PENDING = 21;

	// aliases
	const INQUIRY = self::FREE_INQUIRY;
	const LACK_OF_INSTRUCTIONS = self::REPLY_PENDING;


	const SPACING_SINGLE = 'single';
	const SPACING_DOUBLE = 'double';
	
	/** Services **/
	const WRITING_PAGES = 1;
	const WRITING_SLIDES = 2;
	const WRITING_CHARTS = 31;
	const CHOOSE_WRITER = 3;
	const PROVIDE_ME_SAMPLES = 4;
	const PROGRESSIVE_DELIVERY = 5;
	const DISCOUNT = 6;
	const USED_SOURCES = 21;
	const COMPLEX_ASSIGNMENT = 37;
	const COUPON_WRT_PAGES = 10;
	const REVISION_MINOR_PAGES = 26;
	const REVISION_MINOR_SLIDES = 27;
	const REVISION_MINOR_CHARTS = 33;
	const REVISION_MAJOR_PAGES = 28;
	const REVISION_MAJOR_SLIDES = 29;
	const REVISION_MAJOR_CHARTS = 35;
    const EXPERT_PROOFREADING = 41;
    const VIP_SUPPORT = 44;
    const PLAGIARISM_REPORT = 45;
    const DRAFT_OUTLINE = 46;
	const SHORTEN_DEADLINE = 42;

	const REVISION_MINOR = 42;
    const REVISION_MAJOR = 43;
    
    /** @var $id Order ID */
    public $id;
    
    /** @var string tracking_id */
    public $tracking_id;
    
    /** @var int site_id */
    public $site_id = PROX_SITE_ID;

    /** @var int form_type_id */
    public $form_type_id;

    /** @var string form_type */
    public $form_type;

    /** @var string payment_method */
    public $payment_method;

    /** @var int currency_id */
    public $currency_id;

    /** @var float currency_to_usd_rate */
    public $currency_to_usd_rate = 1;

    /** @var int gmt */
    public $gmt;

    /** @var int customer_id */
    public $customer_id;

    /** @var int writer_id */
    public $writer_id;

    /** @var int requested_writer_id */
    public $requested_writer_id;

    /** @var int editor_id */
    public $editor_id;

    /** @var int status_id */
    public $status_id = self::FAKE;

    /** @var int parent_id */
    public $parent_id;

    /** @var int service_type */
    public $service_type = self::COMPLEX_ASSIGNMENT;

    /** @var int tariff_id */
    public $tariff_id;

    /** @var string title */
    public $title;

    /** @var int academic_level_id */
    public $academic_level_id;

    /** @var int paper_type_id */
    public $paper_type_id;

    /** @var string paper_type_option */
    public $paper_type_option;

    /** @var int topic_category_id */
    public $topic_category_id;

    /** @var string topic_category_option */
    public $topic_category_option;

    /** @var bool is_complex_assignment */
    public $is_complex_assignment = 0;

    /** @var int paper_format_id */
    public $paper_format_id;

    /** @var string paper_format_option */
    public $paper_format_option;

    /** @var int writer_category_id */
    public $writer_category_id;

    /** @var int writer_percent */
    public $writer_percent = 0;

    /** @var int pages */
    public $pages = 0;

    /** @var int charts */
    public $charts = 0;

    /** @var int slides */
    public $slides = 0;

    /** @var int sources */
    public $sources = 0;

    /** @var string spacing */
    public $spacing = self::SPACING_DOUBLE;

    /** @var string paper_details */
    public $paper_details;

    /** @var string comments */
    public $comments;

    /** @var bool writer_samples */
    public $writer_samples = 0;

    /** @var bool used_sources */
    public $used_sources = 0;

    /** @var bool expert_proofreading */
    public $expert_proofreading = 0;

    /** @var bool vip_support */
    public $vip_support = 0;

    /** @var bool plagiarism_report */
    public $plagiarism_report = 0;

    /** @var bool draft_outline */
    public $draft_outline = 0;

    /** @var bool progressive_delivery */
    public $progressive_delivery = 0;

    /** @var float raw_cost */
    public $raw_cost = 0;

    /** @var float total */
    public $total = 0;

    /** @var float tax */
    public $tax = 0;

    /** @var int discount */
    public $discount = 0;

     /** @var int discount_type */
    public $discount_type = 0;

    /** @var float price_per_page */
    public $price_per_page = 0;
    
    /** @var float total_paid */
    public $total_paid = 0;

    /** @var string paid_at */
    public $paid_at;

    /** @var bool is_paid */
    public $is_paid = 0;

    /** @var float writer_pay */
    public $writer_pay = 0;

    /** @var bool is_writer_paid */
    public $is_writer_paid = 0;

    /** @var string writer_paid_at */
    public $writer_paid_at;

    /** @var bool is_writer_confirmed */
    public $is_writer_confirmed = 0;

    /** @var float price_per_page */
    public $editor_pay = 0;

    /** @var bool is_editor_paid */
    public $is_editor_paid = 0;

    /** @var string editor_paid_at */
    public $editor_paid_at;

    /** @var bool is_editor_confirmed */
    public $is_editor_confirmed = 0;
    
    /** @var int hrs_customer */
    public $hrs_customer = 0;

    /** @var string customer_deadline */
    public $customer_deadline;

    /** @var int hrs_writer */
    public $hrs_writer = 0;

    /** @var string writer_deadline */
    public $writer_deadline;

    /** @var bool writer_assigned */
    public $writer_assigned = 0;

    /** @var string writer_assigned_at */
    public $writer_assigned_at;

    /** @var string form_type */
    public $editor_assigned = 0;

    /** @var string editor_assigned_at */
    public $editor_assigned_at;

    /** @var string approved_at */
    public $approved_at;

    /** @var string completed_at */
    public $completed_at;

    /** @var string cancelled_at */
    public $cancelled_at;

    /** @var string delivered_at */
    public $delivered_at;

    /** @var string finished_at */
    public $finished_at;

    /** @var string date_upd */
    public $date_upd;

    /** @var string date_add */
    public $date_add;

    public $winbackCoupons = array();

    public $presentFiles = array();

    public $currency;
    
    /**
     * @see ObjectModel::$definition
     */
    public static $definition = array(
        'table' => 'order',
        'primary' => 'order_id',
        'fields' => array(
            'tracking_id' => array('type' => self::TYPE_STRING, 'size' => 255),
            'site_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'form_type_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'form_type' => array('type' => self::TYPE_STRING),
            'payment_method' => array('type' => self::TYPE_STRING),
            'currency_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'currency_to_usd_rate' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat', 'required' => false),
            'gmt' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'customer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'writer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'requested_writer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'editor_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'status_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'parent_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'service_type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'tariff_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'title' => array('type' => self::TYPE_STRING),
            'academic_level_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_type_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_type_option' => array('type' => self::TYPE_STRING),
            'topic_category_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'topic_category_option' => array('type' => self::TYPE_STRING),
            'is_complex_assignment' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'paper_format_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_format_option' => array('type' => self::TYPE_STRING),
            'writer_category_id' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'writer_percent' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'pages' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'charts' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'slides' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'sources' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'spacing' => array('type' => self::TYPE_STRING), 
            'paper_details' => array('type' => self::TYPE_HTML, 'validate' => 'isString'),
            'comments' => array('type' => self::TYPE_STRING),
            'writer_samples' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'used_sources' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'expert_proofreading' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'vip_support' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'plagiarism_report' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'draft_outline' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'progressive_delivery' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'raw_cost' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'total' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'tax' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'discount' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'discount_type' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'price_per_page' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'total_paid' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_pay' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'is_writer_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_writer_confirmed' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_pay' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'is_editor_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_editor_confirmed' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'hrs_customer' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'customer_deadline' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'hrs_writer' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'writer_deadline' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'writer_assigned' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_assigned_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'editor_assigned' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_assigned_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'approved_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'completed_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'cancelled_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'delivered_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'finished_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
        )
    );
    
    /**
     * constructor.
     *
     * @param null $id
     */
    public function __construct($id = null, $fullInfo = false)
    {
        parent::__construct($id);

        if($this->id) {
            // currency 
            $default_currency_id = Configuration::get('CURRENCY_DEFAULT', $this->site_id, 1);

            $currency_id = $this->currency_id ? $this->currency_id : $default_currency_id;
            $currency = new Currency((int) $currency_id);
            $currency->conversion_rate = $this->currency_to_usd_rate ? (float) $this->currency_to_usd_rate : (float) $currency->conversion_rate;
            $this->currency = $currency;
        }
    }

    public function add($autodate = true, $null_values = true) {
		if ( !$this->getNextOrderId() ) {
			$this->id = self::START_ORDER_ID;
		} else {
            $order_autoincrement = Configuration::get('ORDER_AUTOINCREMENT', PROX_ADMIN_SITE_ID, 2);
			$next_order_id = Db::getInstance()->getValue(
                'SELECT MAX(`order_id`)+' . (int) $order_autoincrement . ' FROM ' . Db::prefix('order')
            );
			$this->id = $next_order_id;
		}

		$this->force_id = true;

		return parent::add($autodate, $null_values);
    }
    
    public function calculate() {
        if( $this->service_type == self::COMPLEX_ASSIGNMENT ) {
            $priceCalculator = new PriceCalculator();
            $defaultOrder = $this->defaultOrder();

            return $priceCalculator->calculate( $defaultOrder );
        } else {
            $pagesPriceMultiplicator = $this->spacing == "single" ? 2 : 1;
            $isComplexAssigment = $this->is_complex_assignment;
            $price = 0;

            $price = ($this->pages * $pagesPriceMultiplicator * $this->price_per_page) +
                     ($this->slides / 2) * $this->price_per_page +
                     ($this->charts / 2) * $this->price_per_page;

            $complexCost = 0;
            if ($isComplexAssigment) {
                $complexCost = ($price / 100) * 20;
            }

            $writerCost = 0;
            if($this->requested_writer_id) {
                $writerCost = ($price / 100) * $this->writer_percent;
            }
                
            $discountPrice = 0;
            if( $this->discount ) {
                if( $this->discount_type == Coupon::PERCENT ) {
                    $discountPrice = ($price * $this->discount) / 100;
                } else {
                    $discountPrice = $this->discount;
                }
            }

            $price += $complexCost + $writerCost;
            $totalCost = $price - $discountPrice;

            $pricesCost = array(
                'rawCost' => $price,
                'totalCost' => $totalCost
            );

            return $pricesCost;
        }
        
    }

    public function defaultOrder() {
        $discount = null;
        if($this->discount) {
            $discount = array(
                'type_id' => $this->discount_type,
                'value' => $this->discount
            );
        }

        $defaultOrder = array (
            'pages' =>                              (int) $this->pages,
            'slides' =>                             (int) $this->slides,
            'discount' =>                           $discount,
            'charts' =>                             (int) $this->charts,
            'winbackCoupons' =>                     $this->winbackCoupons,
            'writerCategoryId' =>                   $this->writer_category_id,
            'tariffPricePerPage' =>                 $this->price_per_page,
            'tariffHrs' =>                          $this->hrs_customer,
            'spacing' =>                            $this->spacing,
            'getSamplesOn' =>                       $this->writer_samples,
            'getProgressiveDeliveryOn' =>           $this->progressive_delivery,
            'getUsedSourcesOn' =>                   $this->used_sources,
            'complexAssignmentDiscipline' =>        (bool) $this->is_complex_assignment,
            'writerPercent' =>                      (int) $this->writer_percent,
            'expertProofreading' =>                 (bool) $this->expert_proofreading,
            'vipSupport' =>                         (bool) $this->vip_support,
            'plagiarismReport' =>                   (bool) $this->plagiarism_report,
            'draftOutline' =>                       (bool) $this->draft_outline
        );

        return $defaultOrder;
    }

    /**
     * used to cache additional payments.
     */
    protected $additionalServices = array();

    public function getAdditionalServices() {
        $options = array();
        $currency = $this->currency;

        if(Validate::isLoadedObject($currency)) {
            $options['currency'] = array(
                'symbol' => $currency->symbol,
                'rate' => (float) $this->currency_to_usd_rate ? (float) $this->currency_to_usd_rate : (float) $currency->conversion_rate
            );
        }

        if (empty($this->additionalServices)) {
            $calculatedCosts = $this->calculate();
            $servicesById = ArrayUtils::get($calculatedCosts, 'servicesById');

            $additionalServices = array();

            if($this->writer_samples) {
                $writerSamplesCost = ArrayUtils::get($servicesById, self::PROVIDE_ME_SAMPLES);
                $writerSamplesCost_price = (float) ArrayUtils::get($writerSamplesCost, 'price');

                $additionalServices[] = array(
                    "title" => "Order writer’s samples", 
                    "typeId" => self::PROVIDE_ME_SAMPLES, 
                    "coupon_data" => null, 
                    "description" => formatPrice($writerSamplesCost_price, $options), 
                );
            }

            if($this->used_sources) {
                $usedSourcesCost = ArrayUtils::get($servicesById, self::USED_SOURCES);
                $usedSourcesCost_price = (float) ArrayUtils::get($usedSourcesCost, 'price');

                $additionalServices[] = array(
                    "title" => "Copy of sources used", 
                    "typeId" => self::USED_SOURCES, 
                    "coupon_data" => null, 
                    "description" => formatPrice($usedSourcesCost_price, $options),
                );
            }

            if($this->expert_proofreading) {
                $expertProofreadingCost = ArrayUtils::get($servicesById, self::EXPERT_PROOFREADING);
                $expertProofreadingCost_price = (float) ArrayUtils::get($expertProofreadingCost, 'price');

                $additionalServices[] = array(
                    "title" => "Expert Proofreading", 
                    "typeId" => self::EXPERT_PROOFREADING, 
                    "coupon_data" => null, 
                    "description" =>formatPrice($expertProofreadingCost_price, $options)
                );
            }

            if($this->vip_support) {
                $vipSupportCost = ArrayUtils::get($servicesById, self::VIP_SUPPORT);
                $vipSupportCost_price = (float) ArrayUtils::get($vipSupportCost, 'price');

                $additionalServices[] = array(
                    "title" => "VIP Support", 
                    "typeId" => self::VIP_SUPPORT, 
                    "coupon_data" => null, 
                    "description" =>formatPrice($vipSupportCost_price, $options)
                );
            }

            if($this->plagiarism_report) {
                $plagiarismReportCost = ArrayUtils::get($servicesById, self::PLAGIARISM_REPORT);
                $plagiarismReportCost_price = (float) ArrayUtils::get($plagiarismReportCost, 'price');

                $additionalServices[] = array(
                    "title" => "Plagiarism Report", 
                    "typeId" => self::PLAGIARISM_REPORT, 
                    "coupon_data" => null, 
                    "description" =>formatPrice($plagiarismReportCost_price, $options)
                );
            }

            if($this->draft_outline) {
                $draftOutlineCost = ArrayUtils::get($servicesById, self::DRAFT_OUTLINE);
                $draftOutlineCost_price = (float) ArrayUtils::get($draftOutlineCost, 'price');

                $additionalServices[] = array(
                    "title" => "Draft/Outline", 
                    "typeId" => self::DRAFT_OUTLINE, 
                    "coupon_data" => null, 
                    "description" =>formatPrice($draftOutlineCost_price, $options)
                );
            }

            if($this->progressive_delivery) {
                $progressiveDeliveryCost = ArrayUtils::get($servicesById, self::PROGRESSIVE_DELIVERY);
                $progressiveDeliveryCost_price = (float) ArrayUtils::get($progressiveDeliveryCost, 'price');

                $additionalServices[] = array(
                    "title" => "Progressive delivery", 
                    "typeId" => self::PROGRESSIVE_DELIVERY, 
                    "coupon_data" => null, 
                    "description" => formatPrice($progressiveDeliveryCost_price, $options) 
                );
            }

            $this->additionalServices = $additionalServices;
        }

        return $this->additionalServices;
    }

    /**
     * used to cache additional payments.
     */
    protected $additionalPayments = array();

    public function getAdditionalPayments() {
        if (empty($this->additionalPayments)) {

            $additionalPayments = array();

            $sql = new DbQuery();
	        $sql->select('*');
	        $sql->from('additional_service');
			$sql->where('order_id = ' . (int) $this->id);
	        $sql->where('is_paid = 1');
            $result = Db::getInstance()->executeS($sql);
            
            foreach( $result as $additionalPayment ) {
                $amount =  ArrayUtils::get($additionalPayment, 'total');

                $additionalPayments[] = [
                    'id' => (int) ArrayUtils::get($additionalPayment, 'additional_service_id'),
                    'title' => "Additional Payment of $" . $amount,
                    'price' => (float) $amount,
                    'paidAt' => ArrayUtils::get($additionalPayment, 'paid_at'),
                ];
            }

            $this->additionalPayments = $additionalPayments;
        }

        return $this->additionalPayments;
    }

    /**
     * used to cache order files.
     */
    protected $orderFiles = null;

    public function getFiles() {
        if (is_null($this->orderFiles)) {
            $sql = new DbQuery();
            $sql->select('f.file_id');
            $sql->from('order_file', 'f');
            $sql->where('f.`order_id` = ' . (int) $this->id );
            $sql->orderBy('file_id');

            $result = Db::getInstance(PROX_USE_SQL_SLAVE)->executeS( $sql );

            if (!$result) {
                return array();
            }
            
            $files = array();
            foreach ($result as $file) {
                $file = new File( (int) $file['file_id']); 
                if( Validate::isLoadedObject($file) ) {
                    $files[] = $file;
                } 
            }

            $this->orderFiles = $files;
        }

		return $this->orderFiles;
    }
    
    /**
     * used to cache order messages.
     */
    protected $orderMessages = null;

    public function getMessages() {
        if (is_null($this->orderMessages)) {
            $sql = new DbQuery();
            $sql->select('m.message_id');
            $sql->from('order_message', 'm');
            $sql->where('m.`order_id` = ' . (int) $this->id );
            $sql->where(' (m.`from_department` = ' . (int) DEPARTMENT_CUSTOMER . ') OR ( m.`to_department` = ' . (int) DEPARTMENT_CUSTOMER . ' AND is_verified = 1 ) ');
            $sql->orderBy('m.message_id DESC');

            $result = Db::getInstance(PROX_USE_SQL_SLAVE)->executeS( $sql );

            if (!$result) {
                return array();
            }

            $messages = array();
            foreach( $result as $message ) {
                $message = new OrderMessage( (int) $message['message_id'] );
                if(Validate::isLoadedObject($message)) {
                    $messages[] = $message;
                }
            }

            $this->orderMessages = $messages;
        }

		return $this->orderMessages;
    }

    /**
     * used to cache order writer.
     */
    protected $cacheWriter = null;

    /**
     * Get order writer.
     *
     * @return Employee $writer
     */
    public function getWriter()
    {
        if (is_null($this->cacheWriter)) {
            $this->cacheWriter = new Employee((int) $this->writer_id);
        }

        return $this->cacheWriter;
	}

	/**
     * used to cache order customer.
     */
    protected $cacheCustomer = null;

    /**
     * Get order customer.
     *
     * @return User $customer
     */
    public function getCustomer()
    {
        if (is_null($this->cacheCustomer)) {
            $this->cacheCustomer = new Customer((int) $this->customer_id);
        }

        return $this->cacheCustomer;
    }

    public function leaveTipButtonVisible() {
        if(!Configuration::get('LEAVE_TIP_ENABLED', PROX_ADMIN_SITE_ID)) {
            return false;
        }

		$leaveTipButtonVisible = false;

		if ( $this->status_id == Order::FINISHED ) {
            $leaveTipButtonVisible = true;
        }

        $sql = new DbQuery();
        $sql->select('ot.*');
        $sql->from('order_tip', 'ot');
        $sql->where('ot.`order_id` = ' . (int) $this->id );
        $result = Db::getInstance()->executeS($sql);
        if(count($result) > 0) {
            $leaveTipButtonVisible = false;
        }

        return $leaveTipButtonVisible;
    }
    
    /**
     * This method return the ID of the previous order.
     *
     * @since 1.5.0.1
     *
     * @return int
     */
    public function getPreviousOrderId( $customerId = null )
    {
        return Db::getInstance()->getValue('
            SELECT order_id
            FROM ' . PROX_DB_PREFIX . 'order
			WHERE order_id < ' . (int) $this->id . 
			($customerId ? ' AND customer_id = '. (int) $customerId : '') . '
            ORDER BY order_id DESC');
    }
	
	/**
     * This method return the ID of the next order.
     *
     * @since 1.0.0
     *
     * @return int
     */
    public function getNextOrderId( $customerId = null )
    {
        return Db::getInstance()->getValue('
            SELECT order_id
            FROM ' . PROX_DB_PREFIX . 'order
			WHERE order_id > ' . (int) $this->id . 
			($customerId ? ' AND customer_id = '. (int) $customerId : '') . '
            ORDER BY order_id ASC');
    }
}