<?php 

  // Build on top of backend controller
  AngieApplication::useController('backend', ENVIRONMENT_FRAMEWORK_INJECT_INTO);

  /**
   * Main invoices controller
   *
   * @package activeCollab.modules.invoicing
   * @subpackage controllers
   */
  class InvoicesController extends BackendController {

    /**
     * Selected invoice
     *
     * @var Invoice
     */
    protected $active_invoice;
    
    /**
     * Payments controller
     * 
     * @var PaymentsController
     */
    protected $payments_delegate;

    /**
     * Cached array of statuses
     *
     * @var array
     */
    protected $status_map;
    
    /**
     * Construct invoices controller
     * 
     * @param Request $request
     * @param string $context
     */
    function __construct(Request $request, $context = null) {
      parent::__construct($request, $context);
      
      if($this->getControllerName() == 'invoices') {
        $this->payments_delegate = $this->__delegate('payments', PAYMENTS_FRAMEWORK_INJECT_INTO, 'invoice');
      } // if
     
      $this->response->assign(array(
        'allow_payment' => boolval(ConfigOptions::getValue('allow_payments')),
        'allow_payments_for_invoice' => boolval(ConfigOptions::getValue('allow_payments_for_invoice')), 
      ));
    } // __construct

    /**
     * Prepare controller
     */
    function __before() {
      parent::__before();
      
      $this->wireframe->tabs->clear();
      $this->wireframe->tabs->add('invoices', lang('Invoices'), Router::assemble('invoices'), null, true);
      $this->wireframe->setCurrentMenuItem('invoicing');
      
      EventsManager::trigger('on_invoices_tabs', array(&$this->wireframe->tabs, &$this->logged_user));
      
      $invoice_id = $this->request->getId('invoice_id');
      if($invoice_id) {
        $this->active_invoice = Invoices::findById($invoice_id);
      } // if

      $this->wireframe->breadcrumbs->add('invoices', lang('Invoices'), Router::assemble('invoices'));
      if($this->active_invoice instanceof Invoice) {
        $this->wireframe->breadcrumbs->add('invoice', $this->active_invoice->getName(), $this->active_invoice->getViewUrl());
      } else {
        $this->active_invoice = new Invoice();
      } // if

      if ($this->request->isWebBrowser() && (in_array($this->request->getAction(), array('index', 'view'))) && Invoices::canAdd($this->logged_user)) {
        $this->wireframe->actions->add('new_invoice', lang('New Invoice'), Router::assemble('invoices_add'), array(
        'onclick' => new FlyoutFormCallback('invoice_created'),
          'icon' => AngieApplication::getImageUrl('layout/button-add.png', ENVIRONMENT_FRAMEWORK, AngieApplication::getPreferedInterface()),
        ));
      } // if

      $this->status_map = Invoices::getStatusMap();

      if($this->logged_user->isFinancialManager() || $this->active_invoice->payments()->canMake($this->logged_user)) {
        $this->response->assign(array( 
          'active_invoice' => $this->active_invoice, 
          'drafts_count' => Invoices::countDrafts(),
        ));
        
        if($this->payments_delegate instanceof PaymentsController) { 
          $this->payments_delegate->__setProperties(array(
            'active_object' => &$this->active_invoice,
         ));
        } // if
      } else {
        $this->response->forbidden();
      } // if
    } // __construct

    /**
     * Show invoicing dashboard
     */
    function index() {
    	
    	// Regular request made by web browser
      if($this->request->isWebBrowser()) {
      	$this->wireframe->list_mode->enable();

        $invoices = Invoices::findForObjectsList($this->logged_user);

        $invoice_dates_map = Invoices::getIssuedAndDueDatesMap($invoices);

        $this->response->assign(array(
          'invoices' => $invoices,
          'companies_map' => Companies::getIdNameMap($this->logged_user->visibleCompanyIds()),
          'invoice_states_map' => $this->status_map,
          'invoice_dates_map' => $invoice_dates_map
        ));
        
      // Phone request
      } elseif($this->request->isPhone()) {
      	$this->wireframe->actions->add('quotes', lang('Quotes'), Router::assemble('quotes'), array(
          'icon' => AngieApplication::getImageUrl('icons/navbar/quotes.png', INVOICING_MODULE, AngieApplication::getPreferedInterface())
        ));
        $this->wireframe->actions->add('recurring_profiles', lang('Recurring Profiles'), Router::assemble('recurring_profiles'), array(
          'icon' => AngieApplication::getImageUrl('icons/navbar/recurring.png', INVOICING_MODULE, AngieApplication::getPreferedInterface())
        ));
        
        $this->response->assign('formatted_invoices', Invoices::findForPhoneList($this->logged_user));
      	
      // Tablet device
    	} elseif($this->request->isTablet()) {
    		throw new NotImplementedError(__METHOD__);
        
      } elseif($this->request->isPrintCall()) {
        $group_by = strtolower($this->request->get('group_by', null));
        $filter_by = $this->request->get('filter_by', null);
        
        // page title
        $filter_by_completion = array_var($filter_by, 'status', null); 
        if ($filter_by_completion === '0') {
        	$page_title = lang('Drafts Invoices');
        } else if ($filter_by_completion === '1') {
					$page_title = lang('Issued Invoices');
        } else if ($filter_by_completion === '2') {
					$page_title = lang('Paid Invoices');
        } else if ($filter_by_completion === '3') {
					$page_title = lang('Canceled Invoices');
        } else {
        	$page_title = lang('All Invoices');
        } // if

        // maps
        $map = array();
        
        switch ($group_by) {
          case 'client_id':
            $map = Companies::getIdNameMap();
            $map[0] = lang('Unknown Client');
            
          	$getter = 'getCompanyId';
          	$page_title.= ' ' . lang('Grouped by Client'); 
            break;
          case 'status':
            $map = $this->status_map;
            $map[0] = lang('Draft');
            
          	$getter = 'getStatus';
          	$page_title.= ' ' . lang('Grouped by Status');
            break;
         case 'issued_on_month':
            $map = Invoices::mapIssuedOnMonth();
            $map[0] = lang('Draft');
            
            $getter = 'getIssuedOnMonth';
          	$page_title.= ' ' . lang('Grouped by Issued On Month');
            break;
         case 'due_on_month':
            $map = Invoices::mapDueOnMonth();
            $map[0] = lang('Draft');
            
            $getter = 'getDueOnMonth';
          	$page_title.= ' ' . lang('Grouped by Due On Month');
            break;
        } //switch
        
        // find invoices
        $invoices = Invoices::findForPrint($this->logged_user, null, $group_by, $filter_by);

        //use thisa to sort objects by map array
        $print_list = group_by_mapped($map,$invoices,$getter);
                  
        $this->smarty->assignByRef('invoices', $print_list);
        $this->smarty->assignByRef('map', $map);
        $this->response->assign(array(
          'page_title' => $page_title,
        ));
      }//if
    } // index
    
    /**
     * Show invoicing archive
     */
    function archive() {
      $this->response->assign('invoiced_companies', Invoices::findInvoicedCompaniesInfo($this->logged_user, array(INVOICE_STATUS_PAID, INVOICE_STATUS_CANCELED)));
    } // archive

    /**
     * Show invoice details
     */
    function view() {
      if (!$this->active_invoice->isLoaded()) {
				$this->response->notFound();
      } // if
      
      if (!$this->active_invoice->canView($this->logged_user)) {
				$this->response->forbidden();
      } // if
      
      if ($this->request->isMobileDevice()) {
        $payment_options = array();
          
        if ($this->active_invoice->payments()->hasDefinedGateways() && $this->active_invoice->payments()->canMake($this->logged_user) || ($this->logged_user->isFinancialManager())) {
        	$payment_options[] = "<a href='" . $this->active_invoice->payments()->getAddUrl()."' class='make_a_payment_btn'>" . lang('Make a Payment') . "</a>";
        } // if
          
        $this->response->assign(array(
        	'payment_options' => $payment_options,
        ));
      } else if ($this->request->isWebBrowser()) {
      	$this->wireframe->setPageObject($this->active_invoice, $this->logged_user);
      	
        if ($this->request->isSingleCall() || $this->request->isQuickViewCall()) {
          
        	$this->render();
        } else {
        	$this->__forward('index', 'index');
        } // if
      } // if
    } // view
    
    /**
     * Show PDF file
     */
    function pdf() {
      if($this->active_invoice->isLoaded()) {
        if($this->active_invoice->canView($this->logged_user)) {
        	InvoicePDFGenerator::download($this->active_invoice, lang(':invoice_id.pdf', array('invoice_id' => $this->active_invoice->getName())));
        	die();
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // pdf
    
    /**
     * Create a new invoice
     */
    function add() {
      if($this->request->isAsyncCall() || ($this->request->isApiCall() && $this->request->isSubmitted())) {
        if(Invoices::canAdd($this->logged_user)) {
          $default_currency = Currencies::getDefault();
          
          if(!($default_currency instanceof Currency)) {
            $this->response->notFound();
          } // if
    
          $invoice_data = $this->request->post('invoice');
          
          if(!is_array($invoice_data)) {
            $duplicate_invoice_id = $this->request->getId('duplicate_invoice_id');
            
            // Duplicate an existing invoice
            if($duplicate_invoice_id) {
              $duplicate_invoice = Invoices::findById($duplicate_invoice_id);
              if($duplicate_invoice instanceof Invoice) {
                $invoice_data = array(
                  'company_id' => $duplicate_invoice->getCompanyId(),
                  'company_address' => $duplicate_invoice->getCompanyAddress(),
                  'comment' => $duplicate_invoice->getComment(),
                  'status' => INVOICE_STATUS_DRAFT,
                  'project_id' => $duplicate_invoice->getProjectId(),
                  'note' => $duplicate_invoice->getNote(),
                  'currency_id' => $duplicate_invoice->getCurrencyId(),
                  'payment_type' => $duplicate_invoice->getAllowPayments()
                );
                
                if(is_foreachable($duplicate_invoice->getItems())) {
                  $invoice_data['items'] = array();
                  foreach($duplicate_invoice->getItems() as $item) {
                    $invoice_data['items'][] = array(
                      'description' => $item->getDescription(),
                      'unit_cost' => $item->getUnitCost(),
                      'quantity' => $item->getQuantity(),
                      'tax_rate_id' => $item->getTaxRateId(),
                      'total' => $item->getTotal(),
                      'subtotal' => $item->getSubtotal(),
                    );
                  } // foreach
                } // if
              } // if
            } // if
            
            // Blank invoice
            if(!is_array($invoice_data)) {
              $invoice_data = array(
                'due_on' => null,
                'currency_id' => $default_currency->getId(),
                'time_record_ids' => null,
                'payment_type' => -1
              );
            } // if
          } // if
          
          $this->response->assign(array(
            'invoice_data'              => $invoice_data,
            'add_invoice_url'           => Router::assemble('invoices_add'),
            'tax_rates'                 => TaxRates::find(),
            'invoice_notes'             => InvoiceNoteTemplates::find(),
            'invoice_item_templates'    => InvoiceItemTemplates::find(),
            'original_note'             => $this->active_invoice->getNote(),
            'invoice_item_template'     => get_view_path('_invoice_item_row', 'invoices', INVOICING_MODULE),
            'default_tax_rate'          => TaxRates::getDefault(),
            'js_invoice_notes'          => InvoiceNoteTemplates::findForSelect(),
            'js_invoice_item_templates' => InvoiceItemTemplates::findForSelect(),
            'js_original_note'          => $this->active_invoice->getNote(),
            'js_company_details_url'    => Router::assemble('people_company_details'),
            'js_company_projects_url'   => Router::assemble('people_company_projects', array('company_id' => '--COMPANY_ID--')),
            'js_move_icon_url'          => AngieApplication::getImageUrl('layout/bits/handle-move.png', ENVIRONMENT_FRAMEWORK),
          ));
    
          
          if($this->request->isSubmitted()) {
          	try {
            	DB::beginWork('Creating a new invoice @ ' . __CLASS__);
            	
            	$this->active_invoice->setAttributes($invoice_data);
    	        $this->active_invoice->setCreatedBy($this->logged_user);
    	        
    	        $this->active_invoice->save();
    	        
              if (!is_foreachable($invoice_data['items'])) {
              	throw new Error(lang('Invoice items data is not valid. All descriptions are required and there need to be at least one unit with cost set per item!'));
              } // if
              
              $position = 1;
              foreach($invoice_data['items'] as $invoice_item_data) {
                $invoice_item = new InvoiceItem();
                $invoice_item->setAttributes($invoice_item_data);
                $invoice_item->setInvoiceId($this->active_invoice->getId());
                $invoice_item->setPosition($position);
    
                $position++;
                $invoice_item->save();
                
                $invoice_item->setTimeRecordIds(array_var($invoice_item_data, 'time_record_ids'));
              } // foreach
    	        
    	        DB::commit('Invoice created @ ' . __CLASS__);
    	        
              $this->response->respondWithData($this->active_invoice, array(
              	'as' => 'invoice', 
                'detailed' => true,
              ));
    	        
          	} catch (Exception $e) {
          	  DB::rollback('Failed to create invoice @ ' . __CLASS__);
          		$this->response->exception($e);
          	} // try
          } // if
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->badRequest();
      } // if
    } // add

    /**
     * Update existing invoice
     */
    function edit() {
      $this->wireframe->hidePrintButton();
      
      if($this->active_invoice->isNew()) {
        $this->response->notFound();
      } // if

      if(!$this->active_invoice->canEdit($this->logged_user)) {
        $this->response->forbidden();
      } // if

      $invoice_data = $this->request->post('invoice');
      if(!is_array($invoice_data)) {
        $invoice_data = array(
          'number'          => $this->active_invoice->getNumber(),
          'due_on'          => $this->active_invoice->getDueOn(),
          'issued_on'       => $this->active_invoice->getIssuedOn(),
          'currency_id'     => $this->active_invoice->getCurrencyId(),
          'comment'         => $this->active_invoice->getComment(),
          'company_id'      => $this->active_invoice->getCompanyId(),
          'company_address' => $this->active_invoice->getCompanyAddress(),
          'project_id'      => $this->active_invoice->getProjectId(),
          'note'            => $this->active_invoice->getNote(),
          'language_id'     => $this->active_invoice->getLanguageId(),
          'payment_type'    => $this->active_invoice->getAllowPayments()
        );
        if(is_foreachable($this->active_invoice->getItems())) {
          $invoice_data['items'] = array();
          foreach($this->active_invoice->getItems() as $item) {
            $invoice_data['items'][] = array(
              'id'					=> $item->getId(),
              'description' => $item->getDescription(),
              'unit_cost'   => $item->getUnitCost(),
              'quantity'    => $item->getQuantity(),
              'tax_rate_id' => $item->getTaxRateId(),
              'total'       => $item->getTotal(),
              'subtotal'    => $item->getSubtotal(),
              'time_record_ids' => $item->getTimeRecordIds(),
            );
          } // foreach
        } // if
      } // if
    
      $this->response->assign(array(
        'invoice_data' => $invoice_data,
        'tax_rates' => TaxRates::find(),
        'default_tax_rate' => TaxRates::getDefault(),
      
      	'invoice_item_templates' => InvoiceItemTemplates::find(),
        'js_invoice_item_templates' => InvoiceItemTemplates::findForSelect(),
      
      	'invoice_notes' => InvoiceNoteTemplates::find(),
        'js_invoice_notes' => InvoiceNoteTemplates::findforSelect(),
      
        'js_original_note' => $this->active_invoice->getNote(),
      	'js_move_icon_url' => AngieApplication::getImageUrl('layout/bits/handle-move.png', ENVIRONMENT_FRAMEWORK),
      	'js_company_details_url' => Router::assemble('people_company_details'),
        'js_company_projects_url' => Router::assemble('people_company_projects', array('company_id' => '--COMPANY_ID--'))
      ));
      
      if ($this->request->isSubmitted()) {
      	if (!$this->request->isAsyncCall()) {
      		$this->response->badRequest();
      	} // if
      	
      	try {
      		DB::beginWork('Editing Invoice');
      		
          if (!is_foreachable($invoice_data['items'])) {
          	throw new ValidationErrors(lang('At least one invoice item is required'));
          } // if
          
      		$this->active_invoice->setAttributes($invoice_data);
      		
      		if($this->active_invoice->isIssued()) {
      		  $issued_on = isset($invoice_data['issued_on']) ? new DateValue($invoice_data['issued_on']) : new DateValue();
      		  $this->active_invoice->setIssuedOn($issued_on);
      		}//if
      		
      		$this->active_invoice->save();
   
      		$old_items = $this->active_invoice->getItems();
      		$cached_items = array();
      		if(is_foreachable($old_items)) {
      		  foreach ($old_items as $old_item) {
      		    $cached_items[] = $old_item->getId();
      		  }//foreach
      		}//if
      		
      		//delete items and related records
          //InvoiceItems::deleteByInvoice($this->active_invoice);
          
          $counter = 1;
          foreach($invoice_data['items'] as $invoice_item_data) {
            
            if($invoice_item_data['old_item_id'] == 0) {
              //add new Item
              $invoice_item = new InvoiceItem();
            } elseif(in_array($invoice_item_data['old_item_id'],$cached_items)) {
              //update item
              $invoice_item = InvoiceItems::findById($invoice_item_data['old_item_id']);
            }//if
            
            if($invoice_item instanceof InvoiceItem) {
              $invoice_item->setAttributes($invoice_item_data);
              $invoice_item->setInvoiceId($this->active_invoice->getId());
              $invoice_item->setPosition($counter);
              $invoice_item->save();
              
              $edited_items[] = $invoice_item->getId();   
              $counter++;
            }//if
          } // foreach
          
          //delete items
          $items_to_delete = array_diff($cached_items, $edited_items);
          if(is_foreachable($items_to_delete)) {
            InvoiceItems::deleteByInvoiceAndIds($this->active_invoice, $items_to_delete);
          }//if
          
          
          
          
// TODO: resolve the issue when dialog has to be redirected to the notification page
//            $this->flash->success('":number" has been updated', array('number' => $this->active_invoice->getName()));
//            if ($this->active_invoice->isIssued()) {
//              $this->response->redirectTo('invoice_notify', array('invoice_id' => $this->active_invoice->getId()));
//            } else {
//              $this->response->redirectToUrl($this->active_invoice->getViewUrl());  
//            } // if

      		DB::commit('Invoice Edited');
					$this->response->respondWithData($this->active_invoice, array(
          	'as' => 'invoice', 
            'detailed' => true,
          ));
          
      	} catch (Exception $e) {
      		DB::rollback('Invoice Editing Failed');
      		$this->response->exception($e);
      	} // try
      	
      } // if
    } // edit
    
    /**
     * Issue invoice
     */
    function issue() {
      if($this->active_invoice->isLoaded()) {
        if($this->active_invoice->canIssue($this->logged_user)) {
          $company = $this->active_invoice->getCompany();
          
          if(!($company instanceof Company)) {
            $this->response->operationFailed();
          } // if
          
          $issue_data = $this->request->post('issue', array(
            'issued_on' => new DateValue(),
            'due_in_days' => ConfigOptions::getValue('invoicing_default_due'), // new DateValue('+15 days'),
          ));
          
          $client_company_manager_roles = Roles::findCompanyManagerRoles();

          // in an owner company, financial management is required in addition to company management
          if ($company->isOwner() && $client_company_manager_roles) {
            foreach ($client_company_manager_roles as $key => $client_company_manager_role) {
              if (!$client_company_manager_role->getPermissionValue('can_manage_finances')) {
                unset($client_company_manager_roles[$key]);
              } // if
            } // foreach
          } // if
          
          if($client_company_manager_roles) {
            $client_company_managers = Users::getForSelectByConditions(array('company_id = ? AND role_id IN (?) AND state >= ?', $this->active_invoice->getCompanyId(), objects_array_extract($client_company_manager_roles, 'getId'), STATE_VISIBLE));
          } else {
            $client_company_managers = null;
          } // if
          
          $this->response->assign(array(
            'client_company_manager_roles' => $client_company_manager_roles, 
            'client_company_managers' => $client_company_managers,
            'issue_data' => $issue_data,
          ));
          
          if($this->request->isSubmitted()) {
          	try {
            	DB::beginWork('Issuing an invoice @ ' . __CLASS__);
            	
    	        $issued_on = isset($issue_data['issued_on']) ? new DateValue($issue_data['issued_on']) : new DateValue();
          
              $due_in_days = isset($issue_data['due_in_days']) ? $issue_data['due_in_days'] : (integer) ConfigOptions::getValue('invoicing_default_due');
              
              if(is_numeric($due_in_days)) {
                $due_in_days = (integer) $due_in_days;
              } // if
              
              if($due_in_days === 'selected') {
                if(isset($issue_data['due_in_days_selected_date']) && $issue_data['due_in_days_selected_date']) {
                  $due_on = new DateValue($issue_data['due_in_days_selected_date']);

                  if($due_on->getTimestamp() < $issued_on->getTimestamp()) {
                    $due_on = $issued_on;
                  } // if
                } else {
                  $due_on = $issued_on;
                } // if
              } elseif($due_in_days <= 0) {
                $due_on = $issued_on;
              } else {
                $due_on = $issued_on->advance($due_in_days * 86400, false);
              } // if

              
    	        $issued_to = isset($issue_data['user_id']) && $issue_data['user_id'] ? Users::findById($issue_data['user_id']) : null;
    	        
    	        
    	        $this->active_invoice->markAsIssued($this->logged_user, $issued_to, $issued_on, $due_on);
              
              DB::commit('Invoice issued @ ' . __CLASS__);
              
              if($issue_data['send_emails'] && $issued_to instanceof User) {
                $recipients = array($issued_to);
                
                if($issued_to->getId() != $this->logged_user->getId()) {
                  $recipients[] = $this->logged_user;
                } // if
                
                ApplicationMailer::notifier()->notifyUsers($recipients, $this->active_invoice, 'invoicing/issued', null, array(
                  'attachments' => array($this->active_invoice->getPdfAttachmentPath() => 'invoice.pdf'),
                  'method' => ApplicationMailer::SEND_INSTANTNLY, 
                ));
              } // if
              
              $this->response->respondWithData($this->active_invoice, array(
                'detailed' => true, 
              	'as' => 'invoice'
              ));
          	} catch (Exception $e) {
          	  DB::rollback('Failed to issue invoice @ ' . __CLASS__);
          		$this->response->exception($e);
          	} // try
          } // if
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // issue
    
    /**
     * Page is displayed when issued invoice is edited
     */
    function notify() {
      if ($this->active_invoice->isLoaded()) {
        if ($this->active_invoice->canEdit($this->logged_user)) {
          $company = $this->active_invoice->getCompany();
          if(!($company instanceof Company)) {
            $this->response->operationFailed();
          } // if
          
          
          $issue_data = $this->request->post('issue', array(
            'issued_on' => $this->active_invoice->getIssuedOn(),
            'due_on' => $this->active_invoice->getDueOn(),
            'issued_to_id' => $this->active_invoice->getIssuedToId()
          ));
          
          $client_company_manager_roles = Roles::findCompanyManagerRoles();
          
          if($client_company_manager_roles) {
            $client_company_managers = Users::getForSelectByConditions(array('company_id = ? AND role_id IN (?) AND state >= ?', $this->active_invoice->getCompanyId(), objects_array_extract($client_company_manager_roles, 'getId'), STATE_VISIBLE));
          } else {
            $client_company_managers = null;
          } // if
          
          $this->response->assign(array(
            'client_company_manager_roles' => $client_company_manager_roles, 
            'client_company_managers' => $client_company_managers,
            'issue_data' => $issue_data,
          ));
          
          if ($this->request->isSubmitted()) {
            try {
             
              $this->active_invoice->setDueOn($issue_data['due_on']);
              
              $resend_to = isset($issue_data['user_id']) && $issue_data['user_id'] ? Users::findById($issue_data['user_id']) : null;
              
              if($issue_data['send_emails'] && $resend_to instanceof IUser) {
                
                $this->active_invoice->setIssuedTo($resend_to);
                
                DB::beginWork('Resend Email @ ' . __CLASS__);
                $recipients = array($resend_to);
                
                ApplicationMailer::notifier()->notifyUsers($recipients, $this->active_invoice, 'invoicing/notify', null, array(
                  'attachments' => array($this->active_invoice->getPdfAttachmentPath() => 'invoice.pdf'),
                  'method' => ApplicationMailer::SEND_INSTANTNLY,
                ));

                DB::commit('Email resend @ ' . __CLASS__);
              } // if
              
              $this->active_invoice->save();
              
              $this->response->respondWithData($this->active_invoice, array(
                'detailed' => true, 
              	'as' => 'invoice'
              ));
          	} catch (Exception $e) {
          	  DB::rollback('Failed to resend email @ ' . __CLASS__);
          	  $this->response->exception($e);
          	} // try
          } // if
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // notify
    
    /**
     * Change invoice status to CANCELED
     */
    function cancel() {
      if($this->request->isAsyncCall()) {
        if($this->active_invoice->isLoaded()) {
          if($this->active_invoice->canCancel($this->logged_user)) {
            if($this->request->isSubmitted()) {
              try {
                $this->active_invoice->markAsCanceled($this->logged_user);
        
                $issued_to_user = $this->active_invoice->getIssuedTo();
                if ($issued_to_user instanceof User && Invoices::getNotifyClientAboutCanceledInvoice()) {
                  $notify_users = array($issued_to_user);
      	          if ($issued_to_user->getId() != $this->logged_user->getId()) {
      	            $notify_users[] = $this->logged_user;
      	          } // if
        	        
                  $this->logged_user->notifier()->notifyUsers($notify_users, $this->active_invoice, 'invoicing/canceled');
                } // if
                
    						$this->response->respondWithData($this->active_invoice, array(
    	          	'as' => 'invoice', 
    	            'detailed' => true,
    	          ));
              } catch (Error $e) {
              	$this->response->exception($e);
              } // try
            } // if
          } else {
            $this->response->forbidden();
          } // if
        } else {
          $this->response->notFound();
        } // if
      } else {
        $this->response->badRequest();
      } // if
    } // cancel

    /**
     * Drop invoice
     */
    function delete() {
      if ($this->active_invoice->isLoaded()) {
        if ($this->active_invoice->canDelete($this->logged_user)) {
          if ($this->request->isSubmitted()) {        
            try {
              $this->active_invoice->releaseTimeRecords();
              $this->active_invoice->releaseExpenses();
            	$this->active_invoice->delete();
            	$this->response->respondWithData($this->active_invoice, array(
            	  'as' => 'invoice', 
            	));
            } catch (Exception $e) {
            	$this->response->exception($e);
            } // try
          } else {
            $this->response->badRequest();
          } // if
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // delete
    
    /**
     * Show time attached to a particular invoice
     */
    function time() {
      if($this->active_invoice->isLoaded()) {
        if($this->active_invoice->canViewRelatedItems($this->logged_user)) {
          $this->wireframe->print->enable();
          
          $this->response->assign(array(
            'time_records' => $this->active_invoice->getTimeRecords(),
            'expenses' => $this->active_invoice->getExpenses(),
            'items_can_be_released' => $this->active_invoice->isDraft() || $this->active_invoice->isCanceled()
          ));
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // time
    
    /**
     * Release items related to this invoice
     */
    function items_release() {
      if($this->active_invoice->isLoaded()) {
        if($this->active_invoice->canEdit($this->logged_user) && ($this->active_invoice->isDraft() || $this->active_invoice->isCanceled())) {
          if($this->request->isSubmitted()) {
            try {
              $release_times = $this->request->post('release_times');
              $release_expenses = $this->request->post('release_expenses');
              
              if(is_foreachable($release_times)) {
                $this->active_invoice->releaseTimeRecordsByIds($release_times);
              }//if
              if(is_foreachable($release_expenses)) {
                $this->active_invoice->releaseExpensesByIds($release_expenses);
              }//if
              $this->response->respondWithData($this->active_invoice, array('as' => 'invoice'));
            } catch (Exception $e) {
              $this->response->exception($e);
            }//try
          } else {
            $this->response->badRequest();
          } // if
        } else {
          $this->response->forbidden();
        } // if
      } else {
        $this->response->notFound();
      } // if
    } // time_release
    
  }