/** * 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; }