/** * returns the product aggregate for a given accountable * * @param Sales_Model_Accountable_Interface $record */ public function findProductAggregate(Sales_Model_Accountable_Interface $record) { $accountableClassName = get_class($record); $filter = new Sales_Model_ProductFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'accountable', 'operator' => 'equals', 'value' => $accountableClassName))); $products = Sales_Controller_Product::getInstance()->search($filter); $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'product_id', 'operator' => 'in', 'value' => $products->getId()))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $this->getId()))); $pas = Sales_Controller_ProductAggregate::getInstance()->search($filter); if ($pas->count() < 1) { throw new Tinebase_Exception_Data('A contract aggregate could not be found!'); } elseif ($pas->count() > 1) { throw new Tinebase_Exception_Data('At the moment a contract may have only one product aggregate for the same product, not more!'); } return $pas->getFirstRecord(); }
/** * add body rows * * @alternate, kind of POC or VIP, overwrites the default one * * @param Tinebase_Record_RecordSet $records */ public function processIteration($_records) { $json = new Tinebase_Convert_Json(); $productAggregateIds = $_records->accountable_id; $paFilter = new Sales_Model_ProductAggregateFilter(); $paFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'id', 'operator' => 'in', 'value' => $productAggregateIds))); $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($paFilter); $pFilter = new Sales_Model_ProductFilter(); $pFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'id', 'operator' => 'in', 'value' => array_unique($productAggregates->product_id)))); $products = Sales_Controller_Product::getInstance()->search($pFilter); $resolved = $json->fromTine20RecordSet($_records); foreach ($resolved as $record) { $record['accountable_id'] = $productAggregates->getById($record['accountable_id'])->toArray(); $record['product_id'] = $products->getById($record['accountable_id']['product_id'])->toArray(); $row = $this->_activeTable->appendRow(); $i18n = $this->_translate->getAdapter(); foreach ($this->_config->columns->column as $field) { $identifier = $field->identifier; // TODO: use ModelConfig here to get the POC // get type and value for cell $cellType = $this->_getCellType($field->type); switch ($identifier) { case 'quantity': $value = intval($record[$identifier]) * intval($record['accountable_id']['quantity']); break; case 'month': $value = $record[$identifier]; break; default: $value = $record['product_id'][$identifier]; } // create cell with type and value and add style $cell = $row->appendCell($value, $cellType); if ($field->customStyle) { $cell->setStyle((string) $field->customStyle); } } } }
/** * tests adding and removing of products to a contract */ public function testAddDeleteProducts() { $prodTest = new Sales_ProductControllerTest(); $productOne = $prodTest->testCreateProduct(); $productTwo = $prodTest->testCreateProduct(); $contractData = $this->_getContract(); $contractData->products = array(array('product_id' => $productOne->getId(), 'quantity' => 1, 'interval' => 1, 'billing_point' => 1), array('product_id' => $productTwo->getId(), 'quantity' => 1, 'interval' => 1, 'billing_point' => 1)); $this->_backend->create($contractData); $contract = $this->_backend->get($contractData->getId()); // checks $this->assertEquals($contractData->getId(), $contract->getId()); $this->assertGreaterThan(0, $contract->number); $this->assertEquals(Tinebase_Core::getUser()->getId(), $contract->created_by); // check count of product aggregates $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter); $this->assertEquals(2, count($productAggregates)); $contractData->products = array(array('product_id' => $productOne->getId(), 'quantity' => 1, 'interval' => 1, 'billing_point' => 1)); $this->_backend->update($contractData); $contract = $this->_backend->get($contractData->getId()); // check count of product aggregates $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter); $this->assertEquals(1, count($productAggregates)); // cleanup $this->_backend->delete($contract->getId()); $this->_decreaseNumber(); $prodTest->getUit()->delete(array($productOne->getId(), $productTwo->getId())); }
public function testInvoiceRecreation() { $result = $this->_createInvoiceUpdateRecreationFixtures(); $oldInvoiceId0 = $result['created'][0]; $ipc = Sales_Controller_InvoicePosition::getInstance(); $f = new Sales_Model_InvoicePositionFilter(array(array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId0))))); $positions = $ipc->search($f); $this->assertEquals(9, $positions->count()); $oldInvoiceId1 = $result['created'][1]; $ipc = Sales_Controller_InvoicePosition::getInstance(); $f = new Sales_Model_InvoicePositionFilter(array(array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId1))))); $positions = $ipc->search($f); $this->assertEquals(4, $positions->count()); $contract4 = $this->_contractRecords->getByIndex(3); $filter = new Sales_Model_ProductAggregateFilter(array(array('field' => 'interval', 'operator' => 'equals', 'value' => 3)), 'AND'); $filter->addFilter(new Tinebase_Model_Filter_ForeignId(array('field' => 'contract_id', 'operator' => 'AND', 'value' => array(array('field' => ':id', 'operator' => 'equals', 'value' => $contract4->getId())), 'options' => array('controller' => 'Sales_Controller_Contract', 'filtergroup' => 'Sales_Model_ContractFilter', 'modelName' => 'Sales_Model_Contract')))); $pA = Sales_Controller_ProductAggregate::getInstance()->search($filter); $this->assertEquals(1, $pA->count()); $pA = $pA->getFirstRecord(); $pA->interval = 4; Sales_Controller_ProductAggregate::getInstance()->update($pA); $contract4->title = $contract4->getTitle() . ' changed'; sleep(1); $this->_contractController->update($contract4); $this->sharedTimesheet->id = NULL; $this->_timesheetController->create($this->sharedTimesheet); $result = $this->_invoiceController->checkForContractOrInvoiceUpdates(); $this->assertEquals(true, count($result) === 2 || count($result) === 3); $mapping = $this->_invoiceController->getAutoInvoiceRecreationResults(); $this->assertEquals(true, isset($mapping[$oldInvoiceId0])); $this->assertEquals(true, isset($mapping[$oldInvoiceId1])); $newInvoiceId0 = $mapping[$oldInvoiceId0]; $newInvoiceId1 = $mapping[$oldInvoiceId1]; $this->assertNotEquals($oldInvoiceId0, $newInvoiceId0); $this->assertNotEquals($oldInvoiceId1, $newInvoiceId1); $this->_checkInvoiceUpdateExistingTimeaccount($newInvoiceId1); $f = new Sales_Model_InvoicePositionFilter(array(array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $newInvoiceId0))))); $positions = $ipc->search($f); $this->assertEquals(10, $positions->count()); $f = new Sales_Model_InvoicePositionFilter(array(array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $newInvoiceId1))))); $positions = $ipc->search($f); $this->assertEquals(1, $positions->count()); }
/** * finds product aggregates for $this->_currentBillingContract * * @return Tinebase_Record_RecordSet */ protected function _findProductAggregates() { $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $this->_currentBillingContract->getId()))); $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter); $productAggregates->setTimezone(Tinebase_Core::getUserTimezone()); return $productAggregates; }
/** * processUpdateBillingInformation * * @param Tinebase_Record_RecordSet $contracts */ public function processUpdateLastAutobillOfProductAggregates(Tinebase_Record_RecordSet $contracts) { $now = Tinebase_DateTime::now(); $billingPoints = array('Timetracker_Model_Timeaccount' => 'end', 'Sales_Model_Product' => 'end', 'WebAccounting_Model_BackupPath' => 'end', 'WebAccounting_Model_StoragePath' => 'end', 'WebAccounting_Model_MailAccount' => 'end', 'WebAccounting_Model_DReg' => 'begin', 'WebAccounting_Model_CertificateDomain' => 'begin', 'WebAccounting_Model_IPNet' => 'end', '' => 'end', 'Sales_Model_ProductAgregate' => 'end'); foreach ($contracts as $contract) { if ($contract->end_date && $contract->end_date < $now) { continue; } // find product aggregates for this contract $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter); foreach ($productAggregates as $pa) { // find all invoices for the contract $filter = new Sales_Model_InvoiceFilter(array(array('field' => 'contract', 'operator' => 'AND', 'value' => array(array('field' => ':id', 'operator' => 'equals', 'value' => $contract->getId()))))); $invoices = Sales_Controller_Invoice::getInstance()->search($filter); // find last invoice position for this aggregate $filter = new Sales_Model_InvoicePositionFilter(); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'in', 'value' => $invoices->getArrayOfIds()))); $pagination = new Tinebase_Model_Pagination(array('limit' => 1, 'sort' => 'month', 'dir' => 'DESC')); $lastInvoicePosition = Sales_Controller_InvoicePosition::getInstance()->search($filter, $pagination)->getFirstRecord(); // set billing_point, if none given if (!$pa->billing_point) { $pa->billing_point = $billingPoints[$lastInvoicePosition->model]; } if (!$lastInvoicePosition) { // if no invoice position has been found, this is a new contract, so set start_date to the first day of the month of the contracts start_date $date = clone $contract->start_date; $date->setTimezone(Tinebase_Core::getUserTimezone()); $date->setTime(0, 0, 0); $date->setDate($date->format('Y'), $date->format('m'), 1); $date->setTimezone('UTC'); $startDate = clone $date; $labDate = NULL; } else { $split = explode('-', $lastInvoicePosition->month); $date = Tinebase_DateTime::now(); $date->setTimezone(Tinebase_Core::getUserTimezone()); $date->setTime(0, 0, 0); $date->setDate($split[0], $split[1], 1); // set to next billing date $date->addMonth(1); // if the billing point is at the begin of the interval, set date back one interval if ($pa->billing_point == 'begin') { $date->subMonth($pa->interval); } $date->setTimezone('UTC'); $labDate = clone $date; // find first invoice position to calculate start_date $pagination = new Tinebase_Model_Pagination(array('limit' => 1, 'sort' => 'month', 'dir' => 'ASC')); $firstInvoicePosition = Sales_Controller_InvoicePosition::getInstance()->search($filter, $pagination)->getFirstRecord(); $split = explode('-', $firstInvoicePosition->month); $startDate = Tinebase_DateTime::now()->setTimezone(Tinebase_Core::getUserTimezone()); $startDate->setTime(0, 0, 0); $startDate->setDate($split[0], $split[1], 1); $startDate->setTimezone('UTC'); } $pa->start_date = $startDate; $pa->last_autobill = $labDate; Sales_Controller_ProductAggregate::getInstance()->update($pa); } } }
public function setLastAutobill() { if (!Sales_Config::getInstance()->featureEnabled(Sales_Config::FEATURE_INVOICES_MODULE)) { Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' setLastAutobill ran allthoug feature ' . Sales_Config::FEATURE_INVOICES_MODULE . ' is disabled'); return false; } $cc = Sales_Controller_Contract::getInstance(); $pc = Sales_Controller_ProductAggregate::getInstance(); $date = Tinebase_DateTime::now()->setTimezone(Tinebase_Core::getUserTimezone()); $date->setDate($date->format('Y'), 1, 1)->setTime(0, 0, 0); $date->setTimezone('UTC'); $filter = new Sales_Model_ContractFilter(array(array('field' => 'start_date', 'operator' => 'after_or_equals', 'value' => $date))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'end_date', 'operator' => 'isnull', 'value' => NULL))); $contracts = $cc->search($filter); foreach ($contracts as $contract) { $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); echo 'Updating last_autobill of ' . $contract->title . PHP_EOL; $contract->last_autobill = clone $contract->start_date; $contract->last_autobill->subMonth($contract->interval); foreach ($pc->search($filter) as $pagg) { echo 'Updating last_autobill of product assigned to ' . $contract->title . PHP_EOL; $pagg->last_autobill = clone $contract->start_date; $pagg->last_autobill->subMonth($pagg->interval); $pc->update($pagg); } $cc->update($contract); } }
public function setLastAutobill() { $cc = Sales_Controller_Contract::getInstance(); $pc = Sales_Controller_ProductAggregate::getInstance(); $date = Tinebase_DateTime::now()->setTimezone(Tinebase_Core::getUserTimezone()); $date->setDate($date->format('Y'), 1, 1)->setTime(0, 0, 0); $date->setTimezone('UTC'); $filter = new Sales_Model_ContractFilter(array(array('field' => 'start_date', 'operator' => 'after_or_equals', 'value' => $date))); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'end_date', 'operator' => 'isnull', 'value' => NULL))); $contracts = $cc->search($filter); foreach ($contracts as $contract) { $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); echo 'Updating last_autobill of ' . $contract->title . PHP_EOL; $contract->last_autobill = clone $contract->start_date; $contract->last_autobill->subMonth($contract->interval); foreach ($pc->search($filter) as $pagg) { echo 'Updating last_autobill of product assigned to ' . $contract->title . PHP_EOL; $pagg->last_autobill = clone $contract->start_date; $pagg->last_autobill->subMonth($pagg->interval); $pc->update($pagg); } $cc->update($contract); } }
/** * inspects delete action * * @param array $_ids * @return array of ids to actually delete */ protected function _inspectDelete(array $_ids) { $records = $this->_backend->getMultiple($_ids); $records->setTimezone(Tinebase_Core::getUserTimezone()); $invoicePositionController = Sales_Controller_InvoicePosition::getInstance(); $contractController = Sales_Controller_Contract::getInstance(); foreach ($records as $record) { if (!$record->is_auto) { continue; } if ($record->cleared == 'CLEARED') { // cleared invoices must not be deleted throw new Sales_Exception_InvoiceAlreadyClearedDelete(); } else { // try to find a invoice after this one // there should be a contract $contractRelation = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Invoice', 'Sql', $record->getId(), NULL, array(), TRUE, array('Sales_Model_Contract'))->getFirstRecord(); if ($contractRelation) { $contract = $contractRelation->related_record; $contract->setTimezone(Tinebase_Core::getUserTimezone()); // get all invoices related to this contract. throw exception if a follwing invoice has been found $invoiceRelations = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Contract', 'Sql', $contract->getId(), NULL, array(), TRUE, array('Sales_Model_Invoice')); foreach ($invoiceRelations as $invoiceRelation) { $invoiceRelation->related_record->setTimezone(Tinebase_Core::getUserTimezone()); if ($record->getId() !== $invoiceRelation->related_record->getId() && $record->creation_time < $invoiceRelation->related_record->creation_time) { throw new Sales_Exception_DeletePreviousInvoice(); } } } else { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Could not find contract relation -> skip contract handling'); } $contract = null; } // remove invoice_id from billables $filter = new Sales_Model_InvoicePositionFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $record->getId()))); $invoicePositions = $invoicePositionController->search($filter); $allModels = array_unique($invoicePositions->model); foreach ($allModels as $model) { if ($model == 'Sales_Model_ProductAggregate') { continue; } $filteredInvoicePositions = $invoicePositions->filter('model', $model); $billableControllerName = $model::getBillableControllerName(); $billableFilterName = $model::getBillableFilterName(); $filterInstance = new $billableFilterName(array()); $filterInstance->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $record->getId()))); $billableControllerName::getInstance()->updateMultiple($filterInstance, array('invoice_id' => NULL)); // set invoice ids of the timeaccounts if ($model == 'Timetracker_Model_Timeaccount') { $filterInstance = new Timetracker_Model_TimeaccountFilter(array()); $filterInstance->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $record->getId()))); Timetracker_Controller_Timeaccount::getInstance()->updateMultiple($filterInstance, array('invoice_id' => NULL)); } } // delete invoice positions $invoicePositionController->delete($invoicePositions->getId()); // set last_autobill a period back if ($contract) { // check product aggregates $filter = new Sales_Model_ProductAggregateFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId()))); $paController = Sales_Controller_ProductAggregate::getInstance(); $productAggregates = $paController->search($filter); $productAggregates->setTimezone(Tinebase_Core::getUserTimezone()); foreach ($productAggregates as $productAggregate) { if ($productAggregate->last_autobill) { $lab = clone $productAggregate->last_autobill; $add = 0 - (int) $productAggregate->interval; $productAggregate->last_autobill = $lab->addMonth($add); $productAggregate->last_autobill->setTime(0, 0, 0); // last_autobill may not be before aggregate starts (may run into this case if interval has been resized) if (!$productAggregate->start_date || $productAggregate->last_autobill < $productAggregate->start_date) { $productAggregate->last_autobill = NULL; } } $productAggregate->setTimezone('UTC'); $paController->update($productAggregate); } } } } return $_ids; }