/**
  * Create a new invoice
  *
  * @param void
  * @return null
  */
 function add()
 {
     $this->wireframe->print_button = false;
     if (!Invoice::canAdd($this->logged_user)) {
         $this->httpError(HTTP_ERR_FORBIDDEN);
     }
     // if
     $default_currency = Currencies::findDefault();
     if (!instance_of($default_currency, 'Currency')) {
         $this->httpError(HTTP_ERR_NOT_FOUND, 'Default currency not set');
     }
     // if
     $time_report = null;
     $project = null;
     $timerecord_ids = null;
     $invoice_data = $this->request->post('invoice');
     if (!is_array($invoice_data)) {
         $duplicate_invoice_id = $this->request->getId('duplicate_invoice_id');
         $time_report_id = $this->request->getId('time_report_id');
         $ticket_id = $this->request->getId('ticket_id');
         // ---------------------------------------------------
         //  Duplicate existing invoice
         // ---------------------------------------------------
         if ($duplicate_invoice_id) {
             $duplicate_invoice = Invoices::findById($duplicate_invoice_id);
             if (instance_of($duplicate_invoice, '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());
                 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
             // ---------------------------------------------------
             //  Create invoice from time report
             // ---------------------------------------------------
         } else {
             if ($time_report_id) {
                 $time_report = TimeReports::findById($time_report_id);
                 if (instance_of($time_report, 'TimeReport')) {
                     $project_id = $this->request->getId('project_id');
                     $client_company_id = null;
                     if ($project_id) {
                         $project = Projects::findById($project_id);
                     }
                     // if
                     $time_report->setBillableFilter(BILLABLE_FILTER_BILLABLE);
                     $conditions = $time_report->prepareConditions($this->logged_user, $project);
                     if ($conditions === false) {
                         $this->httpError(HTTP_ERR_OPERATION_FAILED, 'Failed to prepare time report conditions');
                     }
                     // if
                     $timerecord_ids = array();
                     $total_time = 0;
                     $project_objects_table = TABLE_PREFIX . 'project_objects';
                     $invoice_time_records_table = TABLE_PREFIX . 'invoice_time_records';
                     $rows = db_execute_all("SELECT DISTINCT id,float_field_1 FROM {$project_objects_table} WHERE {$conditions}");
                     if (is_foreachable($rows)) {
                         // find time records ids that needs to be attached to this invoice due to time record
                         foreach ($rows as $row) {
                             $timerecord_ids[] = (int) $row['id'];
                         }
                         // foreach
                         $timerecords_filtered = Invoices::filterAvailableTimerecords($timerecord_ids);
                         // calculate total time, but only if time record is not yet attached to the existing invoice
                         if (is_foreachable($rows) && is_foreachable($timerecord_ids)) {
                             foreach ($rows as $row) {
                                 if (!in_array($row['id'], $timerecord_ids)) {
                                     $total_time += (double) $row['float_field_1'];
                                 }
                                 // if
                             }
                             // foreach
                         }
                         // if
                     }
                     // if
                     if ($total_time == 0) {
                         $this->wireframe->addPageMessage(lang('You don\'t have any billable time records in this time report, or all bilable time records are already attached to existing invoice'), PAGE_MESSAGE_INFO);
                     } else {
                         if ($timerecords_filtered) {
                             $this->wireframe->addPageMessage(lang('One or more billable timerecords in this time report are already attached to the existing invoice. They won\'t be counted in this one'), PAGE_MESSAGE_INFO);
                         }
                     }
                     // if
                     if (count($timerecord_ids) && $total_time) {
                         if (instance_of($project, 'Project')) {
                             $description = lang('Total of :total hours in :project project', array('total' => $total_time, 'project' => $project->getName()));
                         } else {
                             $description = lang('Total of :total hours', array('total' => $total_time));
                         }
                         // if
                         $invoice_data = array('due_on' => new DateValue(), 'currency_id' => $default_currency->getId(), 'project_id' => instance_of($project, 'Project') ? $project->getId() : null, 'company_id' => instance_of($project, 'Project') ? $project->getCompanyId() : null, 'items' => array(array('description' => $description, 'unit_cost' => $default_currency->getDefaultRate(), 'quantity' => $total_time, 'subtotal' => $default_currency->getDefaultRate() * $total_time, 'total' => $default_currency->getDefaultRate() * $total_time, 'tax_rate_id' => null, 'time_record_ids' => $timerecord_ids)));
                     }
                     // if
                 }
                 // if
                 // ---------------------------------------------------
                 //  Create invoice from ticket
                 // ---------------------------------------------------
             } else {
                 if ($ticket_id) {
                     $ticket = Tickets::findById($ticket_id);
                     if (instance_of($ticket, 'Ticket')) {
                         $timerecords_filtered = false;
                         $items = array();
                         if ($ticket->getHasTime()) {
                             $timerecords = TimeRecords::findByParent($ticket, array(BILLABLE_STATUS_BILLABLE), STATE_VISIBLE, $this->logged_user->getVisibility());
                             $timerecord_ids = array();
                             $ticket_total_time = 0;
                             if (is_foreachable($timerecords)) {
                                 foreach ($timerecords as $timerecord) {
                                     if ($timerecord->getValue() > 0) {
                                         $timerecord_ids[] = $timerecord->getId();
                                     }
                                     // if
                                 }
                                 // foreach
                                 $timerecords_filtered = Invoices::filterAvailableTimerecords($timerecord_ids);
                                 foreach ($timerecords as $timerecord) {
                                     if (in_array($timerecord->getId(), $timerecord_ids)) {
                                         $ticket_total_time += $timerecord->getValue();
                                     }
                                     // if
                                 }
                                 // foreach
                                 $items[] = array('description' => lang('Ticket: :ticket_name', array('ticket_name' => $ticket->getName())), 'unit_cost' => $default_currency->getDefaultRate(), 'quantity' => $ticket_total_time, 'subtotal' => $default_currency->getDefaultRate() * $ticket_total_time, 'total' => $default_currency->getDefaultRate() * $ticket_total_time, 'tax_rate_id' => null, 'time_record_ids' => $timerecord_ids);
                             }
                             // if
                         }
                         // if
                         $tasks = $ticket->getTasks();
                         if (is_foreachable($tasks)) {
                             foreach ($tasks as $task) {
                                 if ($task->getHasTime()) {
                                     $timerecords = TimeRecords::findByParent($task, array(BILLABLE_STATUS_BILLABLE), STATE_VISIBLE, $this->logged_user->getVisibility());
                                     $task_total_time = 0;
                                     $timerecord_ids = array();
                                     if (is_foreachable($timerecords)) {
                                         foreach ($timerecords as $timerecord) {
                                             if ($timerecord->getValue() > 0) {
                                                 $timerecord_ids[] = $timerecord->getId();
                                             }
                                             // if
                                         }
                                         // foreach
                                         $timerecords_filtered = $timerecords_filtered || Invoices::filterAvailableTimerecords($timerecord_ids);
                                         foreach ($timerecords as $timerecord) {
                                             if (in_array($timerecord->getId(), $timerecord_ids)) {
                                                 $task_total_time += $timerecord->getValue();
                                             }
                                             // if
                                         }
                                         // foreach
                                         if (is_foreachable($timerecord_ids) && $task_total_time > 0) {
                                             $items[] = array('description' => lang('Task: :task_name', array('task_name' => $task->getName())), 'unit_cost' => $default_currency->getDefaultRate(), 'quantity' => $task_total_time, 'subtotal' => $default_currency->getDefaultRate() * $task_total_time, 'total' => $default_currency->getDefaultRate() * $task_total_time, 'tax_rate_id' => null, 'time_record_ids' => $timerecord_ids);
                                         }
                                         // if
                                     }
                                     // if
                                 }
                                 // if
                             }
                             // foreach
                         }
                         // if
                         $project = $ticket->getProject();
                         $invoice_data = array('due_on' => new DateValue(), 'currency_id' => $default_currency->getId(), 'project_id' => $ticket->getProjectId(), 'company_id' => instance_of($project, 'Project') ? $project->getCompanyId() : null, 'time_record_ids' => $timerecord_ids, 'items' => $items);
                         if ($timerecords_filtered) {
                             $this->wireframe->addPageMessage(lang('One or more billable timerecords in this time report are already attached to the existing invoice. They won\'t be counted in this one'), PAGE_MESSAGE_INFO);
                         }
                         // if
                     }
                     // if
                 }
             }
         }
         // if
         // ---------------------------------------------------
         //  Start blank
         // ---------------------------------------------------
         if (!is_array($invoice_data)) {
             $invoice_data = array('due_on' => new DateValue(), 'currency_id' => $default_currency->getId(), 'project_id' => instance_of($project, 'Project') ? $project->getId() : null, 'time_record_ids' => null);
         }
         // if
     }
     // if
     $invoice_notes = InvoiceNoteTemplates::findAll();
     $invoice_item_templates = InvoiceItemTemplates::findAll();
     $this->smarty->assign(array('invoice_data' => $invoice_data, 'tax_rates' => TaxRates::findAll(), 'invoice_notes' => $invoice_notes, 'invoice_item_templates' => $invoice_item_templates, 'original_note' => $this->active_invoice->getNote()));
     $cleaned_notes = array();
     if (is_foreachable($invoice_notes)) {
         foreach ($invoice_notes as $invoice_note) {
             $cleaned_notes[$invoice_note->getId()] = $invoice_note->getContent();
         }
         // foreach
     }
     // if
     js_assign('invoice_notes', $cleaned_notes);
     js_assign('original_note', $this->active_invoice->getNote());
     $cleaned_item_templates = array();
     if (is_foreachable($invoice_item_templates)) {
         foreach ($invoice_item_templates as $invoice_item_template) {
             $cleaned_item_templates[$invoice_item_template->getId()] = array('description' => $invoice_item_template->getDescription(), 'unit_cost' => $invoice_item_template->getUnitCost(), 'quantity' => $invoice_item_template->getQuantity(), 'tax_rate_id' => $invoice_item_template->getTaxRateId());
         }
         // foreach
     }
     // if
     js_assign('invoice_item_templates', $cleaned_item_templates);
     js_assign('company_details_url', assemble_url('invoice_company_details'));
     js_assign('move_icon_url', get_image_url('move.gif'));
     if ($this->request->isSubmitted()) {
         db_begin_work();
         $this->active_invoice->setAttributes($invoice_data);
         $this->active_invoice->setCreatedBy($this->logged_user);
         $invoice_company = Companies::findById(array_var($invoice_data, 'company_id', null));
         $this->active_invoice->setCompanyName($invoice_company->getName());
         $save = $this->active_invoice->save();
         if ($save && !is_error($save)) {
             $counter = 0;
             if (is_foreachable($invoice_data['items'])) {
                 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($counter + 1);
                     $item_save = $invoice_item->save();
                     if ($item_save && !is_error($item_save)) {
                         $invoice_item->setTimeRecordIds(array_var($invoice_item_data, 'time_record_ids'));
                         $counter++;
                     } else {
                         // error in invoice_item_data
                     }
                     // if
                 }
                 // foreach
             }
             // if
             if ($counter > 0) {
                 db_commit();
                 flash_success('Invoice ":number" has been created', array('number' => $this->active_invoice->getName()));
                 $this->redirectToUrl($this->active_invoice->getViewUrl());
             } else {
                 db_rollback();
                 $this->smarty->assign('errors', new ValidationErrors(array('items' => 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
         } else {
             db_rollback();
             $this->smarty->assign('errors', $save);
         }
         // if
     }
     // if
 }
 /**
  * Execute report and prepare it for export
  *
  * @param User $user
  * @param TimeReport $report
  * @param Project $project
  * @return array
  */
 function executeReportForExport($user, $report, $project = null)
 {
     $report_records = TimeReports::executeReport($user, $report, $project);
     $csv_content = array();
     if ($report->getSumByUser()) {
         $csv_content[] = array('Person', 'Hours');
         if (is_foreachable($report_records)) {
             foreach ($report_records as $report_record) {
                 $csv_content[] = array($report_record['user']->getDisplayName(), (string) $report_record['total_time']);
             }
             // foreach
         }
         // if
     } else {
         $csv_content[] = array('Date', 'Person', 'Project ID', 'Project Name', 'Hours', 'Summary', 'Parent Summary', 'Billable', 'Billed', 'Billed Status');
         if (is_foreachable($report_records)) {
             foreach ($report_records as $time_record) {
                 if ($time_record->isBillable()) {
                     $billable = 'Yes';
                     $billed = $time_record->isBilled() ? 'Yes' : 'No';
                 } else {
                     $billable = 'No';
                     $billed = 'No';
                 }
                 // if
                 $date = $time_record->getRecordDate();
                 $person = $time_record->getUser();
                 $project = $time_record->getProject();
                 $parent = $time_record->getParent();
                 $csv_content[] = array($date->toMySql(), $person->getDisplayName(), $time_record->getProjectId(), $project->getName(), (string) $time_record->getValue(), $time_record->getBody(), instance_of($parent, 'ProjectObject') ? $parent->getName() : '', $billable, $billed, (int) $time_record->getBillableStatus());
             }
             // foreach
         }
         // if
     }
     // if
     return $csv_content;
 }
 /**
  * Export report as CSV
  *
  * @param void
  * @return null
  */
 function report_export()
 {
     if ($this->active_report->isNew()) {
         $this->httpError(HTTP_ERR_NOT_FOUND);
     }
     // if
     if (!$this->active_report->canView($this->logged_user)) {
         $this->httpError(HTTP_ERR_FORBIDDEN);
     }
     // if
     download_contents(array_to_csv(TimeReports::executeReportForExport($this->logged_user, $this->active_report)), 'text/csv', "time-report.csv", true);
 }