/**
  * creates invoice positions by the billables per each month
  * 
  * @param array $billables
  * @param Sales_Model_Accountable_Interface $accountable
  * @return Tinebase_Record_RecordSet
  */
 protected function _getInvoicePositionsFromBillables(array $billables, Sales_Model_Accountable_Interface $accountable)
 {
     $invoicePositions = new Tinebase_Record_RecordSet('Sales_Model_InvoicePosition');
     foreach ($billables as $month => $billablesPerMonth) {
         if ($accountable->sumBillables()) {
             $sumQuantity = 0.0;
             foreach ($billablesPerMonth as $billable) {
                 $qty = $billable->getQuantity();
                 $sumQuantity = $sumQuantity + $qty;
             }
             $pos = array('month' => $month, 'model' => get_class($accountable), 'accountable_id' => $accountable->getId(), 'title' => $accountable->getTitle(), 'quantity' => $sumQuantity, 'unit' => $billable->getUnit());
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->log(__METHOD__ . '::' . __LINE__ . ' Create invoice position ' . print_r($pos, 1) . ' for contract: ' . $this->_currentBillingContract->getId(), Zend_Log::DEBUG);
             }
             $invoicePositions->addRecord(new Sales_Model_InvoicePosition($pos));
         } else {
             foreach ($billablesPerMonth as $billable) {
                 $pos = array('month' => $month, 'model' => get_class($accountable), 'accountable_id' => $accountable->getId(), 'title' => $accountable->getTitle(), 'quantity' => $billable->getQuantity(), 'unit' => $billable->getUnit());
                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                     Tinebase_Core::getLogger()->log(__METHOD__ . '::' . __LINE__ . ' Create invoice position ' . print_r($pos, 1), Zend_Log::DEBUG);
                 }
                 $invoicePositions->addRecord(new Sales_Model_InvoicePosition($pos));
             }
         }
     }
     return $invoicePositions;
 }
 /**
  * create contracts, auto add timeaccounts if there are any
  * 
  * @param array $contractData
  * @return Tinebase_Record_RecordSet
  */
 protected function _createContracts($contractData = NULL)
 {
     // 1.1.20xx
     $startDate = clone $this->_referenceDate;
     $endDate = clone $startDate;
     // 1.8.20xx
     $endDate->addMonth(7);
     $this->_contractController = Sales_Controller_Contract::getInstance();
     $container = $this->_contractController->getSharedContractsContainer();
     $this->_sharedContractsContainerId = $container->getId();
     if (!$contractData) {
         if (!$this->_costcenterRecords) {
             $this->_createCostCenters();
         }
         if (!$this->_productRecords) {
             $this->_createProducts();
         }
         if (!$this->_customerRecords) {
             $this->_createCustomers();
         }
         if (!$this->_timesheetRecords) {
             $this->_createTimesheets();
         }
         $contractData = array(array('number' => 1, 'title' => Tinebase_Record_Abstract::generateUID(), 'description' => '1 unittest begin', 'container_id' => $this->_sharedContractsContainerId, 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer1')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(), 'start_date' => clone $startDate, 'end_date' => NULL, 'products' => array(array('start_date' => $startDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 1, 'billing_point' => 'begin', 'product_id' => $this->_productRecords->filter('name', 'Hours')->getFirstRecord()->getId()))), array('number' => 2, 'title' => Tinebase_Record_Abstract::generateUID(), 'description' => '2 unittest end', 'container_id' => $this->_sharedContractsContainerId, 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer2')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(), 'start_date' => clone $startDate, 'end_date' => clone $endDate, 'products' => array(array('start_date' => clone $startDate, 'end_date' => clone $endDate, 'quantity' => 1, 'interval' => 4, 'billing_point' => 'end', 'product_id' => $this->_productRecords->filter('name', 'Hours')->getFirstRecord()->getId()))), array('number' => 3, 'title' => Tinebase_Record_Abstract::generateUID(), 'description' => '3 unittest end', 'container_id' => $this->_sharedContractsContainerId, 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer3')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(), 'start_date' => clone $startDate, 'end_date' => NULL, 'products' => array(array('start_date' => clone $startDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 3, 'billing_point' => 'end', 'product_id' => $this->_productRecords->filter('name', 'Hours')->getFirstRecord()->getId()))), array('number' => 4, 'title' => Tinebase_Record_Abstract::generateUID(), 'description' => '4 unittest products', 'container_id' => $this->_sharedContractsContainerId, 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer4')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(), 'start_date' => clone $startDate, 'end_date' => NULL, 'products' => array(array('start_date' => clone $startDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 6, 'billing_point' => 'begin', 'product_id' => $this->_productRecords->filter('name', 'billhalfyearly')->getFirstRecord()->getId()), array('start_date' => clone $startDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 3, 'billing_point' => 'begin', 'product_id' => $this->_productRecords->filter('name', 'billeachquarter')->getFirstRecord()->getId()))));
     }
     $this->_contractRecords = new Tinebase_Record_RecordSet('Sales_Model_Contract');
     $i = 0;
     foreach ($contractData as $cd) {
         $costcenter = $this->_costcenterRecords->getByIndex($i);
         $customer = $this->_customerRecords->getByIndex($i);
         if ($this->_timeaccountRecords) {
             $timeaccount = $this->_timeaccountRecords->getByIndex($i);
         }
         $i++;
         $contract = new Sales_Model_Contract($cd);
         $contract->setTimezone('UTC');
         $contract->relations = array(array('own_model' => 'Sales_Model_Contract', 'own_backend' => Tasks_Backend_Factory::SQL, 'own_id' => NULL, 'related_degree' => Tinebase_Model_Relation::DEGREE_SIBLING, 'related_model' => 'Sales_Model_CostCenter', 'related_backend' => Tasks_Backend_Factory::SQL, 'related_id' => $costcenter->getId(), 'type' => 'LEAD_COST_CENTER'), array('own_model' => 'Sales_Model_Contract', 'own_backend' => Tasks_Backend_Factory::SQL, 'own_id' => NULL, 'related_degree' => Tinebase_Model_Relation::DEGREE_SIBLING, 'related_model' => 'Sales_Model_Customer', 'related_backend' => Tasks_Backend_Factory::SQL, 'related_id' => $customer->getId(), 'type' => 'CUSTOMER'));
         if ($this->_timeaccountRecords) {
             $contract->relations = array_merge($contract->relations, array(array('own_model' => 'Sales_Model_Contract', 'own_backend' => Tasks_Backend_Factory::SQL, 'own_id' => NULL, 'related_degree' => Tinebase_Model_Relation::DEGREE_SIBLING, 'related_model' => 'Timetracker_Model_Timeaccount', 'related_backend' => Tasks_Backend_Factory::SQL, 'related_id' => $timeaccount->getId(), 'type' => 'TIME_ACCOUNT')));
         }
         $this->_contractRecords->addRecord($this->_contractController->create($contract));
     }
     return $this->_contractRecords;
 }
 /**
  * returns timeaccount-contract relation
  * @param Sales_Model_Contract $contract
  * @param Timetracker_Model_Timeaccount $timeaccount
  */
 protected function _getRelation($contract, $timeaccount)
 {
     $r = new Tinebase_Model_Relation();
     $ra = array('own_model' => 'Timetracker_Model_Timeaccount', 'own_backend' => 'Sql', 'own_id' => $timeaccount->getId(), 'related_degree' => 'sibling', 'remark' => 'phpunit test', 'related_model' => 'Sales_Model_Contract', 'related_backend' => 'Sql', 'related_id' => $contract->getId(), 'type' => 'CONTRACT');
     $r->setFromArray($ra);
     return $r;
 }
 /**
  * merges source contracts into the target contract (relations and products)
  * 
  * @param Sales_Model_Contract $targetContract
  * @param Tinebase_Record_RecordSet $sourceContracts
  */
 public function mergeContracts(Sales_Model_Contract $targetContract, Tinebase_Record_RecordSet $sourceContracts)
 {
     // handle relations (duplicates get skipped)
     foreach ($sourceContracts as $sourceContract) {
         Tinebase_Relations::getInstance()->transferRelations($sourceContract->getId(), $targetContract->getId(), 'Sales_Model_Contract');
     }
     // handle products
     $filter = new Sales_Model_ProductAggregateFilter(array());
     $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'in', 'value' => $sourceContracts->getId())));
     $products = Sales_Controller_ProductAggregate::getInstance()->search($filter);
     foreach ($products as $product) {
         $product->contract_id = $targetContract->getId();
         Sales_Controller_ProductAggregate::getInstance()->update($product);
     }
     return true;
 }
 /**
  * removes unbilled auto invoices
  * 
  * @param Sales_Model_Contract $contract
  */
 public function removeUnbilledAutoInvoices(Sales_Model_Contract $contract = NULL)
 {
     if (!Sales_Config::getInstance()->featureEnabled(Sales_Config::FEATURE_INVOICES_MODULE)) {
         Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' removeUnbilledAutoInvoices ran allthoug feature ' . Sales_Config::FEATURE_INVOICES_MODULE . ' is disabled');
         return false;
     }
     $c = Sales_Controller_Invoice::getInstance();
     $f = new Sales_Model_InvoiceFilter(array(array('field' => 'is_auto', 'operator' => 'equals', 'value' => TRUE), array('field' => 'cleared', 'operator' => 'not', 'value' => 'CLEARED')), 'AND');
     if ($contract) {
         $subf = new Tinebase_Model_Filter_ExplicitRelatedRecord(array('field' => 'contract', 'operator' => 'AND', 'value' => array(array('field' => ':id', 'operator' => 'equals', 'value' => $contract->getId())), 'options' => array('controller' => 'Sales_Controller_Contract', 'filtergroup' => 'Sales_Model_ContractFilter', 'own_filtergroup' => 'Sales_Model_InvoiceFilter', 'own_controller' => 'Sales_Controller_Invoice', 'related_model' => 'Sales_Model_Contract')));
         $f->addFilter($subf);
     }
     $p = new Tinebase_Model_Pagination(array('sort' => 'start_date', 'dir' => 'DESC'));
     $invoiceIds = $c->search($f, $p, false, true);
     if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' About to delete ' . count($invoiceIds) . ' uncleared invoices ...');
     }
     foreach ($invoiceIds as $invoiceId) {
         try {
             $c->delete(array($invoiceId));
         } catch (Sales_Exception_DeletePreviousInvoice $sedpi) {
             Tinebase_Exception::log($sedpi);
         }
     }
 }
 /**
  * set relations for contract
  * 
  * @param array|Sales_Model_Contract $contract
  * @param array $contacts
  * @param string $type
  */
 protected function _setContractRelations($contract, $contacts, $type = 'PARTNER')
 {
     $relationData = array();
     foreach ($contacts as $contact) {
         $relationData[] = array('own_degree' => 'sibling', 'related_degree' => 'sibling', 'related_model' => 'Addressbook_Model_Contact', 'related_backend' => 'Sql', 'related_id' => $contact->getId(), 'type' => $type);
     }
     $contractId = $contract instanceof Sales_Model_Contract ? $contract->getId() : $contract['id'];
     Tinebase_Relations::getInstance()->setRelations('Sales_Model_Contract', 'Sql', $contractId, $relationData);
 }
 /**
  * returns a temporarily productaggregate which contains the
  * default billing information of this accountable
  * 
  * @param Sales_Model_Contract $contract
  * @return Sales_Model_ProductAggregate
  */
 public function getDefaultProductAggregate(Sales_Model_Contract $contract)
 {
     $startDate = clone $contract->start_date;
     if ($contract->start_date->format('d') !== 1) {
         $startDate->setDate($startDate->format('Y'), $startDate->format('m'), 1);
     }
     $accountable = get_class($this);
     $filter = new Sales_Model_ProductFilter(array(array('field' => 'accountable', 'operator' => 'equals', 'value' => $accountable)));
     $product = Sales_Controller_Product::getInstance()->search($filter)->getFirstRecord();
     // create product, if no product is found
     if (!$product) {
         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' ' . ' Create Product for ' . $accountable);
         }
         $product = Sales_Controller_Product::getInstance()->create(new Sales_Model_Product(array('name' => $accountable, 'accountable' => $accountable, 'description' => 'auto generated on invoicing')));
     }
     if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' ' . ' Create ProductAggregate for ' . $accountable . ' contract: ' . $contract->getId());
     }
     $endDate = clone $startDate;
     $endDate->addMonth($this->_defaultInterval);
     $pa = new Sales_Model_ProductAggregate(array('interval' => $this->_defaultInterval, 'billing_point' => $this->_defaultBillingPoint, 'contract_id' => $contract->getId(), 'start_date' => $startDate, 'end_date' => NULL, 'last_autobill' => NULL, 'product_id' => $product->getId(), 'quantity' => $product->accountable ? NULL : 1));
     return $pa;
 }
 /**
  * creates the auto invoices, gets called by cli
  * 
  * @param Tinebase_DateTime $currentDate
  * @param Sales_Model_Contract $contract
  */
 public function createAutoInvoices(Tinebase_DateTime $currentDate, Sales_Model_Contract $contract = NULL)
 {
     $this->_autoInvoiceIterationResults = array();
     $this->_autoInvoiceIterationFailures = array();
     $contractBackend = new Sales_Backend_Contract();
     $ids = $contract ? array($contract->getId()) : $contractBackend->getBillableContractIds($currentDate);
     $filter = new Sales_Model_ContractFilter(array());
     $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'id', 'operator' => 'in', 'value' => $ids)));
     $iterator = new Tinebase_Record_Iterator(array('iteratable' => $this, 'controller' => Sales_Controller_Contract::getInstance(), 'filter' => $filter, 'options' => array('getRelations' => TRUE, 'limit' => $this->_autoInvoiceIterationLimit), 'function' => 'processAutoInvoiceIteration'));
     $iterator->iterate($currentDate);
     $result = array('failures' => $this->_autoInvoiceIterationFailures, 'failures_count' => count($this->_autoInvoiceIterationFailures), 'created' => $this->_autoInvoiceIterationResults, 'created_count' => count($this->_autoInvoiceIterationResults));
     return $result;
 }