/**
     * Delete an order by marking it as deleted in the database.
     *
     * @param  Order     $order The order to be deleted
     *
     * @return Order     The order that was deleted, with the "delete" authorship data set
     */
    public function delete(Order $order)
    {
        $order->authorship->delete(new DateTimeImmutable(), $this->_currentUser->id);
        $event = new Event\TransactionalEvent($order);
        $event->setTransaction($this->_trans);
        $order = $this->_eventDispatcher->dispatch(Events::DELETE_START, $event)->getOrder();
        $this->_trans->run('
			UPDATE
				order_summary
			SET
				deleted_at = :at?d,
				deleted_by = :by?in
			WHERE
				order_id = :id?i
		', array('at' => $order->authorship->deletedAt(), 'by' => $order->authorship->deletedBy(), 'id' => $order->id));
        $event = new Event\TransactionalEvent($order);
        $event->setTransaction($this->_trans);
        $order = $this->_eventDispatcher->dispatch(Events::DELETE_END, $event)->getOrder();
        return $order;
    }
    public function updateStatus(Order $order, $statusCode)
    {
        if (!$this->_statuses->exists($statusCode)) {
            throw new \InvalidArgumentException(sprintf('Order status `%s` does not exist', $statusCode));
        }
        $status = $this->_statuses->get($statusCode);
        // Skip if the item is already at this status
        if ($status->code === $order->status->code) {
            return false;
        }
        $order->authorship->update(new DateTimeImmutable(), $this->_currentUser->id);
        $this->_query->run('
			UPDATE
				order_summary
			SET
				status_code = :status?i,
				updated_at  = :updatedAt?i,
				updated_by  = :updatedBy?in
			WHERE
				order_id = :id?i
		', array('id' => $order->id, 'status' => $status->code, 'updatedAt' => $order->authorship->updatedAt(), 'updatedBy' => $order->authorship->updatedBy()));
        if ($status->code === Statuses::CANCELLED) {
            $this->_query->run('UPDATE
					`order_shipping`
				SET
					`status_code` = :status?i
				WHERE
					`order_id` = :id?i', ['id' => $order->id, 'status' => $status->code]);
        }
        $order->status = clone $status;
        $event = new Event\TransactionalEvent($order);
        $event->setTransaction($this->_query);
        $order = $this->_eventDispatcher->dispatch(Events::STATUS_CHANGE, $event)->getOrder();
        if (!$this->_transOverridden) {
            $this->_query->commit();
        }
        return $this->_eventDispatcher->dispatch(Events::EDIT, new Event\Event($order))->getOrder();
    }
    public function create(Order $order)
    {
        $event = new Event\TransactionalEvent($order);
        $event->setTransaction($this->_trans);
        $order = $this->_eventDispatcher->dispatch(Events::CREATE_START, $event)->getOrder();
        $validation = $this->_eventDispatcher->dispatch(Events::CREATE_VALIDATE, new Event\ValidateEvent($order));
        if ($validation->hasErrors()) {
            throw new \InvalidArgumentException(sprintf('Cannot create order: %s', implode(', ', $validation->getErrors())));
        }
        if (!$order->authorship->createdAt()) {
            $order->authorship->create(new DateTimeImmutable(), $this->_currentUser->id);
        }
        $this->_trans->add('
			INSERT INTO
				order_summary
			SET
				created_at       = :createdAt?d,
				created_by       = :createdBy?in,
				status_code      = :status?i,
				user_id          = :userID?in,
				user_email		 = :userEmail?sn,
				type             = :type?sn,
				locale           = :locale?s,
				taxable          = :taxable?b,
				currency_id      = :currencyID?s,
				conversion_rate  = :conversionRate?f,
				product_net      = :productNet?f,
				product_discount = :productDiscount?f,
				product_tax      = :productTax?f,
				product_gross    = :productGross?f,
				total_net        = :totalNet?f,
				total_discount   = :totalDiscount?f,
				total_tax        = :totalTax?f,
				total_gross      = :totalGross?f
		', array('createdAt' => $order->authorship->createdAt(), 'createdBy' => $order->authorship->createdBy(), 'userID' => $order->user ? $order->user->id : null, 'userEmail' => $order->user ? $order->user->email : null, 'status' => $order->status->code, 'type' => $order->type, 'locale' => $order->locale, 'taxable' => $order->taxable, 'currencyID' => $order->currencyID, 'conversionRate' => $order->conversionRate, 'productNet' => $order->productNet, 'productDiscount' => $order->productDiscount, 'productTax' => $order->productTax, 'productGross' => $order->productGross, 'totalNet' => $order->totalNet, 'totalDiscount' => $order->totalDiscount, 'totalTax' => $order->totalTax, 'totalGross' => $order->totalGross));
        $this->_trans->setIDVariable('ORDER_ID');
        $order->id = '@ORDER_ID';
        $this->_trans->add('
			INSERT INTO
				order_shipping
			SET
				order_id     = :orderID?i,
				list_price   = :listPrice?f,
				net          = :net?f,
				discount     = :discount?f,
				tax          = :tax?f,
				tax_rate     = :taxRate?f,
				gross        = :gross?f,
				name         = :name?sn,
				display_name = :display_name?sn
		', array('orderID' => $order->id, 'listPrice' => $order->shippingListPrice, 'net' => $order->shippingNet, 'discount' => $order->shippingDiscount, 'tax' => $order->shippingTax, 'taxRate' => $order->shippingTaxRate, 'gross' => $order->shippingGross, 'name' => $order->shippingName, 'display_name' => $order->shippingDisplayName));
        // Insert metadata
        foreach ($order->metadata as $key => $value) {
            $this->_trans->add('
				INSERT INTO
					order_metadata
				SET
					`order_id` = :orderID?i,
					`key`      = :key?s,
					`value`    = :value?sn
			', array('orderID' => $order->id, 'key' => $key, 'value' => $value));
        }
        foreach ($order->getEntities() as $name => $collection) {
            if (count($collection) > 0 && !array_key_exists($name, $this->_entityCreators)) {
                throw new \LogicException(sprintf('Creator for `%s` order entity not set on order creator', $name));
            }
            foreach ($collection as $entity) {
                $entity->order = $order;
                // Create the entities with the same authorship data as the order
                if (isset($entity->authorship) && $entity->authorship instanceof Authorship && !$entity->authorship->createdAt()) {
                    $entity->authorship->create($order->authorship->createdAt(), $order->authorship->createdBy());
                }
                $this->_entityCreators[$name]->create($entity);
            }
        }
        // Insert item tax rates
        $tokens = [];
        $inserts = [];
        foreach ($order->getShippingTaxes() as $type => $rate) {
            $tokens[] = '(?i, ?s, ?f, ?f)';
            $inserts[] = $order->id;
            $inserts[] = $type;
            $inserts[] = $rate;
            $inserts[] = $order->shippingNet * $rate / 100;
        }
        if ($inserts) {
            $this->_trans->add("INSERT INTO\n\t\t\t\t\t`order_shipping_tax` (`order_id`, `tax_type`, `tax_rate`, `tax_amount`)\n\t\t\t\tVALUES " . implode(',', $tokens), $inserts);
        }
        // Fire the "create end" event before committing the transaction
        $event = new Event\TransactionalEvent($order);
        $event->setTransaction($this->_trans);
        $this->_eventDispatcher->dispatch(Events::CREATE_END, $event);
        $order = $event->getOrder();
        // add CREATE_COMPLETE event to when transaction is committed
        $loader = $this->_loader;
        $this->_trans->attachEvent(Events::CREATE_COMPLETE, function ($trans) use($loader) {
            return new Event\Event($loader->getByID($trans->getIDVariable('ORDER_ID')));
        });
        // @todo ideally we want to listen to CREATE_COMPLETE
        // check if $event->getOrder()->id == $sqlVariableThing
        // then set that as the return value
        if (!$this->_transOverridden) {
            $this->_trans->commit();
            $order = $this->_loader->getByID($this->_trans->getIDVariable('ORDER_ID'));
        }
        return $order;
    }
    /**
     * Update the status of an item or items.
     *
     * @param  Item|Collection|array $items Item, array of items or collection
     *                                      of items
     * @param  int              $statusCode Status code to set
     *
     * @return Edit                         Returns $this for chainability
     *
     * @throws \InvalidArgumentException If the item status supplied is not
     *                                   set on the status collection
     * @throws \InvalidArgumentException If no valid Item instances are passed
     * @throws \InvalidArgumentException If a non-falsey value that is not an
     *                                   instance of Item is passed as an item
     */
    public function updateStatus($items, $statusCode)
    {
        if (!$this->_statuses->exists($statusCode)) {
            throw new \InvalidArgumentException(sprintf('Order item status `%s` does not exist', $statusCode));
        }
        $status = $this->_statuses->get($statusCode);
        if (!is_array($items) && !$items instanceof Collection) {
            $items = array($items);
        }
        // Filter out any falsey values
        $items = $items instanceof Collection ? $items->all() : $items;
        $items = array_filter($items);
        $orders = [];
        // Throw exception if we don't have any items
        if (empty($items)) {
            throw new \InvalidArgumentException('No items passed to `updateStatus()`');
        }
        foreach ($items as $key => $item) {
            if (!$item instanceof Item) {
                $type = gettype($item);
                if ($type == 'object') {
                    $type = get_class($item);
                }
                throw new \InvalidArgumentException(sprintf('Unexpected value: expected order item instance - "' . $type . '"'));
            }
            // Skip if the item is already at this status
            if ($status->code === $item->status->code) {
                continue;
            }
            // Get instance of item status (so we have authorship info)
            $status = new Status\Status($status->code, $status->name);
            $status->authorship->create(new DateTimeImmutable(), $this->_currentUser->id);
            $this->_query->add('
				INSERT INTO
					order_item_status
				SET
					order_id    = :orderID?i,
					item_id     = :itemID?i,
					status_code = :status?i,
					created_at  = :createdAt?i,
					created_by  = :createdBy?in
			', array('orderID' => $item->order->id, 'itemID' => $item->id, 'status' => $status->code, 'createdAt' => $status->authorship->createdAt(), 'createdBy' => $status->authorship->createdBy()));
            $item->status = $status;
            // Collect the order if it hasn't been collected yet
            if (!array_key_exists($item->order->id, $orders)) {
                $orders[$item->order->id] = $item->order;
            }
        }
        // Dispatch an event for each individual order
        foreach ($orders as $order) {
            $event = new Event\TransactionalEvent($order);
            $event->setTransaction($this->_query);
            $this->_eventDispatcher->dispatch(OrderEvents::ITEM_STATUS_CHANGE, $event);
        }
        if (!$this->_transOverridden) {
            $this->_query->commit();
        }
        return $this;
    }