3 * Tine 2.0 - http://www.tine20.org
6 * @license http://www.gnu.org/licenses/agpl.html
7 * @copyright Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
8 * @author Alexander Stintzing <a.stintzing@metaways.de>
14 * Test class for Sales Invoice Controller
16 class Sales_InvoiceControllerTests extends Sales_InvoiceTestCase
18 protected $_testUser = NULL;
21 * Runs the test methods of this class.
26 public static function main()
28 $suite = new PHPUnit_Framework_TestSuite('Tine 2.0 Sales Invoice Controller Tests');
29 PHPUnit_TextUI_TestRunner::run($suite);
34 * @see TestCase::tearDown()
36 protected function tearDown()
38 // switch back to admin user
39 if ($this->_testUser) {
40 Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
47 protected function _createFailingContracts()
49 // add contract not to bill
50 $this->_contractRecords->addRecord($this->_contractController->create(new Sales_Model_Contract(array(
52 'title' => Tinebase_Record_Abstract::generateUID(),
53 'description' => '5 unittest no auto',
54 'container_id' => $this->_sharedContractsContainerId,
55 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer3')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(),
56 'start_date' => $this->_referenceDate,
60 // add contract without customer
61 $contract = new Sales_Model_Contract(array(
63 'title' => Tinebase_Record_Abstract::generateUID(),
64 'description' => '6 unittest auto not possible',
65 'container_id' => $this->_sharedContractsContainerId,
66 'start_date' => $this->_referenceDate,
68 'billing_address_id' => $this->_addressRecords->filter('customer_id', $this->_customerRecords->filter('name', 'Customer3')->getFirstRecord()->getId())->filter('type', 'billing')->getFirstRecord()->getId(),
71 $contract->relations = array(
73 'own_model' => 'Sales_Model_Contract',
74 'own_backend' => Tasks_Backend_Factory::SQL,
76 'own_degree' => Tinebase_Model_Relation::DEGREE_SIBLING,
77 'related_model' => 'Sales_Model_CostCenter',
78 'related_backend' => Tasks_Backend_Factory::SQL,
79 'related_id' => $this->_costcenterRecords->getFirstRecord()->getId(),
80 'type' => 'LEAD_COST_CENTER'
84 $this->_contractRecords->addRecord($this->_contractController->create($contract));
86 // add contract without address
87 $contract = new Sales_Model_Contract(array(
89 'title' => Tinebase_Record_Abstract::generateUID(),
90 'description' => '7 unittest auto not possible',
91 'container_id' => $this->_sharedContractsContainerId,
92 'start_date' => $this->_referenceDate,
96 $this->_contractRecords->addRecord($this->_contractController->create($contract));
100 * tests auto invoice creation
102 public function testFullAutoInvoice()
104 $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
106 $this->_createFullFixtures();
107 $this->_createFailingContracts();
109 $this->assertEquals(7, $this->_contractRecords->count());
111 $date = clone $this->_referenceDate;
115 // the whole year, 12 months
117 $result = $this->_invoiceController->createAutoInvoices($date);
122 $this->assertEquals(6, count($result['failures']));
125 foreach($result['failures'] as $failure) {
126 $failures .= $failure;
129 $this->assertTrue(strstr($failures, 'no customer') !== FALSE);
130 $this->assertTrue(strstr($failures, 'no billing') !== FALSE);
131 $this->assertTrue(strstr($failures, 'no costcenter') !== FALSE);
133 // also add an hour to get the last end
135 $this->_invoiceController->createAutoInvoices($date);
137 $this->_invoiceController->createAutoInvoices($date);
139 $all = $this->_invoiceController->getAll();
141 $cc1 = $this->_costcenterRecords->filter('remark', 'unittest1')->getFirstRecord();
142 $cc2 = $this->_costcenterRecords->filter('remark', 'unittest2')->getFirstRecord();
143 $cc3 = $this->_costcenterRecords->filter('remark', 'unittest3')->getFirstRecord();
144 $cc4 = $this->_costcenterRecords->filter('remark', 'unittest4')->getFirstRecord();
146 $all->setTimezone(Tinebase_Core::getUserTimezone());
148 $customer1Invoices = $all->filter('costcenter_id', $cc1->getId())->sort('start_date');
149 $customer2Invoices = $all->filter('costcenter_id', $cc2->getId())->sort('start_date');
150 $customer3Invoices = $all->filter('costcenter_id', $cc3->getId())->sort('start_date');
151 $customer4Invoices = $all->filter('costcenter_id', $cc4->getId())->sort('start_date');
153 // customer 1 must have one invoice (timeaccount with budget has been billed the first month)
154 $this->assertEquals(1, $customer1Invoices->count(), 'Customer 1 must have 1 invoice!');
156 // customer 2 must have one invoice (timeaccount with budget has been billed the first time)
157 $this->assertEquals(1, $customer2Invoices->count(), 'Customer 2 must have 1 invoice!');
159 // there are timesheets in 2 intervals, so no empty invoice should be generated
160 $this->assertEquals(2, $customer3Invoices->count(), 'Customer 3 must have 2 invoices!');
162 // there are 2 products, interval 3,6 -> so every quarter in this year and the first of next year must be found
163 $this->assertEquals(5, $customer4Invoices->count(), 'Customer 4 must have 5 invoices!');
165 // test invoice positions
166 $allInvoicePositions = Sales_Controller_InvoicePosition::getInstance()->getAll();
168 $this->assertEquals(1, $allInvoicePositions->filter('invoice_id', $customer1Invoices->getFirstRecord()->getId())->count());
169 $this->assertEquals(1, $allInvoicePositions->filter('invoice_id', $customer2Invoices->getFirstRecord()->getId())->count());
171 // each invoice should contain 1 timeaccount
172 foreach($customer3Invoices as $ci) {
173 $this->assertEquals(1, $allInvoicePositions->filter('invoice_id', $ci->getId())->count());
176 // we need 9,3,9,3,9 invoice positions
178 foreach($customer4Invoices as $ci) {
179 $ip = $allInvoicePositions->filter('invoice_id', $ci->getId());
180 $this->assertEquals(($i % 2 == 1) ? 9 : 3, $ip->count());
184 // contract 1 gets billed at the begin of the period
185 $c1IArray = $customer1Invoices->start_date;
187 $this->assertEquals($this->_referenceYear . '-01-01 00:00:00', $c1IArray[0]->toString());
189 $c1IArray = $customer1Invoices->end_date;
190 $this->assertEquals($this->_referenceYear . '-01-31 23:59:59', $c1IArray[0]->toString());
192 // contract 2 gets billed at the end of the period, and the second period ends at 1.8.20xx
193 $c2IsArray = $customer2Invoices->start_date;
194 $c2IeArray = $customer2Invoices->end_date;
196 $this->assertEquals($this->_referenceYear . '-05-01 00:00:00', $c2IsArray[0]->toString());
197 $this->assertEquals($this->_referenceYear . '-05-31 23:59:59', $c2IeArray[0]->toString());
199 // test correct timesheet handling of customer 3
200 $c3IsArray = $customer3Invoices->start_date;
201 $c3IeArray = $customer3Invoices->end_date;
203 $this->assertEquals($this->_referenceYear . '-05-01 00:00:00', $c3IsArray[0]->toString());
204 $this->assertEquals($this->_referenceYear . '-05-31 23:59:59', $c3IeArray[0]->toString());
206 $this->assertEquals($this->_referenceYear . '-09-01 00:00:00', $c3IsArray[1]->toString());
207 $this->assertEquals($this->_referenceYear . '-09-30 23:59:59', $c3IeArray[1]->toString());
209 // test customer 4 having products only
210 $c4IsArray = $customer4Invoices->start_date;
211 $c4IeArray = $customer4Invoices->end_date;
213 // should contain billeachquarter & billhalfyearly
214 $this->assertEquals($this->_referenceYear . '-01-01 00:00:00', $c4IsArray[0]->toString());
215 $this->assertEquals($this->_referenceYear . '-06-30 23:59:59', $c4IeArray[0]->toString());
217 // should contain billeachquarter
218 $this->assertEquals($this->_referenceYear . '-04-01 00:00:00', $c4IsArray[1]->toString());
219 $this->assertEquals($this->_referenceYear . '-06-30 23:59:59', $c4IeArray[1]->toString());
221 // should contain billeachquarter & billhalfyearly
222 $this->assertEquals($this->_referenceYear . '-07-01 00:00:00', $c4IsArray[2]->toString());
223 $this->assertEquals($this->_referenceYear . '-12-31 23:59:59', $c4IeArray[2]->toString());
225 // should contain billeachquarter
226 $this->assertEquals($this->_referenceYear . '-10-01 00:00:00', $c4IsArray[3]->toString());
227 $this->assertEquals($this->_referenceYear . '-12-31 23:59:59', $c4IeArray[3]->toString());
229 // look if hours of timesheets gets calculated properly
230 $c3Invoice = $customer3Invoices->getFirstRecord();
231 $filter = new Sales_Model_InvoicePositionFilter(array());
232 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $c3Invoice->getId())));
233 $c3InvoicePositions = Sales_Controller_InvoicePosition::getInstance()->search($filter);
235 $this->assertEquals(1, $c3InvoicePositions->count());
236 $this->assertEquals(3.5, $c3InvoicePositions->getFirstRecord()->quantity);
238 $invoice = $customer1Invoices->getFirstRecord();
239 $invoice->relations = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Invoice', 'Sql', $invoice->getId())->toArray();
241 $filter = new Sales_Model_InvoicePositionFilter(array());
242 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice['id'])));
243 $invoice->positions = Sales_Controller_InvoicePosition::getInstance()->search($filter);
245 $invoice->cleared = 'CLEARED';
246 $invoice = $this->_invoiceController->update($invoice);
248 // check correct number generation
249 $this->assertEquals("R-00001", $invoice->number);
251 $invoice = $customer2Invoices->getFirstRecord();
252 $invoice->relations = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Invoice', 'Sql', $invoice->getId())->toArray();
253 $filter = new Sales_Model_InvoicePositionFilter(array());
254 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice['id'])));
255 $invoice->positions = Sales_Controller_InvoicePosition::getInstance()->search($filter);
257 $invoice->cleared = 'CLEARED';
258 $invoice = $this->_invoiceController->update($invoice);
260 $this->assertEquals("R-00002", $invoice->number);
262 // check disallow editing invoice after clearing
263 $invoice->credit_term = 20;
264 $this->setExpectedException('Sales_Exception_InvoiceAlreadyClearedEdit');
266 $this->_invoiceController->update($invoice);
269 public function testDeleteInvoice()
271 $this->_createFullFixtures();
273 $date = clone $this->_referenceDate;
276 $this->_invoiceController->createAutoInvoices($date);
278 $paController = Sales_Controller_ProductAggregate::getInstance();
279 $productAggregates = $paController->getAll();
280 $contracts = $this->_contractController->getAll();
281 $contracts->sort('id', 'DESC');
283 $c1 = $contracts->getFirstRecord();
285 $this->assertEquals(5, $productAggregates->count());
287 $taController = Timetracker_Controller_Timeaccount::getInstance();
288 $tsController = Timetracker_Controller_Timesheet::getInstance();
290 $allTimesheets = $tsController->getAll();
291 $allTimeaccounts = $taController->getAll();
293 foreach($allTimesheets as $ts) {
294 $this->assertTrue($ts->invoice_id != NULL);
297 foreach($allTimeaccounts as $ta) {
298 if (intval($ta->budget) == 0) {
299 $this->assertTrue($ta->invoice_id == NULL);
303 $allInvoices = $this->_invoiceController->getAll('start_date', 'DESC');
304 $this->assertEquals(9, $allInvoices->count(), print_r($allInvoices->toArray(), 1));
306 foreach($allInvoices as $invoice) {
307 $this->_invoiceController->delete($invoice);
310 $allTimesheets = $tsController->getAll();
311 $allTimeaccounts = $taController->getAll();
313 foreach($allTimeaccounts as $ta) {
314 if (intval($ta->budget) == 0) {
315 $this->assertTrue($ta->invoice_id == NULL, print_r($ta->toArray(), 1));
319 foreach($allTimesheets as $ts) {
320 $this->assertTrue($ts->invoice_id == NULL, print_r($ts->toArray(), 1));
324 protected function _createInvoiceUpdateRecreationFixtures($createTimesheet = true)
326 $this->_createFullFixtures();
328 // we dont want this contract 1 to be part of the runs below, move it out of the way
329 $this->_contractRecords->getByIndex(0)->start_date->addMonth(12);
330 Sales_Controller_Contract::getInstance()->update($this->_contractRecords->getByIndex(0));
332 $date = clone $this->_referenceDate;
333 $customer4Timeaccount = $this->_timeaccountRecords->filter('title', 'TA-for-Customer4')->getFirstRecord();
334 $customer4Timeaccount->status = 'to bill';
335 $customer4Timeaccount->budget = NULL;
337 if (null === $this->_timesheetController)
338 $this->_timesheetController = Timetracker_Controller_Timesheet::getInstance();
339 if (null === $this->_timeaccountController)
340 $this->_timeaccountController = Timetracker_Controller_Timeaccount::getInstance();
341 $this->_timeaccountController->update($customer4Timeaccount);
343 // this is a ts on 20xx-03-18
344 $this->sharedTimesheet = new Timetracker_Model_Timesheet(array(
345 'account_id' => Tinebase_Core::getUser()->getId(),
346 'timeaccount_id' => $customer4Timeaccount->getId(),
347 'start_date' => $date->addMonth(2)->addDay(17),
349 'description' => 'ts from ' . (string) $date,
351 if (true === $createTimesheet)
352 $this->_timesheetController->create($this->sharedTimesheet);
354 //run autoinvoicing with 20xx-04-01
355 $date = clone $this->_referenceDate;
357 $result = $this->_invoiceController->createAutoInvoices($date);
358 $this->assertEquals(2, count($result['created']));
363 public function testInvoiceRecreation()
365 $this->markTestSkipped('FIXME: this fails randomly :(');
367 $result = $this->_createInvoiceUpdateRecreationFixtures();
369 $oldInvoiceId0 = $result['created'][0];
370 $ipc = Sales_Controller_InvoicePosition::getInstance();
371 $f = new Sales_Model_InvoicePositionFilter(array(
372 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
373 array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId0),
376 $positions = $ipc->search($f);
377 $this->assertEquals(9, $positions->count());
379 $oldInvoiceId1 = $result['created'][1];
380 $ipc = Sales_Controller_InvoicePosition::getInstance();
381 $f = new Sales_Model_InvoicePositionFilter(array(
382 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
383 array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId1),
386 $positions = $ipc->search($f);
387 $this->assertEquals(4, $positions->count());
389 $contract4 = $this->_contractRecords->getByIndex(3);
390 $filter = new Sales_Model_ProductAggregateFilter(
392 array('field' => 'interval', 'operator' => 'equals', 'value' => 3),
393 //array('field' => 'contract_id', 'operator' => 'equals', 'value' => $this->_contractRecords->getByIndex(3)->getId()),
395 $filter->addFilter(new Tinebase_Model_Filter_ForeignId(//ExplicitRelatedRecord(
396 array('field' => 'contract_id', 'operator' => 'AND', 'value' =>
399 'field' => ':id', 'operator' => 'equals', 'value' => $contract4->getId()
403 'controller' => 'Sales_Controller_Contract',
404 'filtergroup' => 'Sales_Model_ContractFilter',
405 //'own_filtergroup' => 'Sales_Model_ProductAggregateFilter',
406 //'own_controller' => 'Sales_Controller_ProductAggregate',
407 //'related_model' => 'Sales_Model_Contract',
408 'modelName' => 'Sales_Model_Contract',
413 $pA = Sales_Controller_ProductAggregate::getInstance()->search($filter);
414 $this->assertEquals(1, $pA->count());
415 $pA = $pA->getFirstRecord();
417 Sales_Controller_ProductAggregate::getInstance()->update($pA);
418 $contract4->title = $contract4->getTitle() . ' changed';
420 $this->_contractController->update($contract4);
422 $this->sharedTimesheet->id = NULL;
423 $this->_timesheetController->create($this->sharedTimesheet);
425 $result = $this->_invoiceController->checkForContractOrInvoiceUpdates();
426 $this->assertEquals(true, (count($result)===2||count($result)===3));
428 $mapping = $this->_invoiceController->getAutoInvoiceRecreationResults();
429 $this->assertEquals(true, isset($mapping[$oldInvoiceId0]));
430 $this->assertEquals(true, isset($mapping[$oldInvoiceId1]));
431 $newInvoiceId0 = $mapping[$oldInvoiceId0];
432 $newInvoiceId1 = $mapping[$oldInvoiceId1];
433 $this->assertNotEquals($oldInvoiceId0, $newInvoiceId0);
434 $this->assertNotEquals($oldInvoiceId1, $newInvoiceId1);
436 $this->_checkInvoiceUpdateExistingTimeaccount($newInvoiceId1);
438 $f = new Sales_Model_InvoicePositionFilter(array(
439 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
440 array('field' => 'id', 'operator' => 'equals', 'value' => $newInvoiceId0),
443 $positions = $ipc->search($f);
444 $this->assertEquals(10, $positions->count());
446 $f = new Sales_Model_InvoicePositionFilter(array(
447 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
448 array('field' => 'id', 'operator' => 'equals', 'value' => $newInvoiceId1),
451 $positions = $ipc->search($f);
452 $this->assertEquals(1, $positions->count());
458 public function testInvoiceUpdateExistingTimeaccount()
460 $result = $this->_createInvoiceUpdateRecreationFixtures();
462 $this->sharedTimesheet->id = NULL;
463 $this->_timesheetController->create($this->sharedTimesheet);
465 $maybeRecreated = $this->_invoiceController->checkForUpdate($result['created'][1]);
466 if (isset($maybeRecreated[0])) {
467 $result = $maybeRecreated;
469 $result = array($result['created'][1]);
472 $this->_checkInvoiceUpdateExistingTimeaccount($result[0]);
474 //check that the same update run doesnt do anything anymore
475 $maybeRecreated = $this->_invoiceController->checkForUpdate($result[0]);
476 if (isset($maybeRecreated[0])) {
477 $result = $maybeRecreated;
480 $this->_checkInvoiceUpdateExistingTimeaccount($result[0]);
483 public function testCheckForContractOrInvoiceUpdatesExistingTimeaccount()
485 $result = $this->_createInvoiceUpdateRecreationFixtures();
487 $this->sharedTimesheet->id = NULL;
488 $this->_timesheetController->create($this->sharedTimesheet);
490 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
491 if (isset($maybeRecreated[0])) {
492 $result = $maybeRecreated;
494 $result = array($result['created'][1]);
497 $this->_checkInvoiceUpdateExistingTimeaccount($result[0]);
499 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
500 if (isset($maybeRecreated[0])) {
501 $result = $maybeRecreated;
504 $this->_checkInvoiceUpdateExistingTimeaccount($result[0]);
507 protected function _checkInvoiceUpdateExistingTimeaccount($invoiceId, $result = 4)
509 $ipc = Sales_Controller_InvoicePosition::getInstance();
510 $f = new Sales_Model_InvoicePositionFilter(array(
511 array('field' => 'model', 'operator' => 'equals', 'value' => 'Timetracker_Model_Timeaccount'),
512 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
513 array('field' => 'id', 'operator' => 'equals', 'value' => $invoiceId),
516 $positions = $ipc->search($f);
517 $this->assertEquals(1, $positions->count());
518 $this->assertEquals($result, $positions->getFirstRecord()->quantity);
521 public function testCheckForContractOrInvoiceUpdatesWithUpdatedTimesheet()
523 $result = $this->_createInvoiceUpdateRecreationFixtures();
525 $this->sharedTimesheet->id = NULL;
526 $this->sharedTimesheet = $this->_timesheetController->create($this->sharedTimesheet);
528 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
529 if (isset($maybeRecreated[0])) {
530 $result = $maybeRecreated;
532 $result = array($result['created'][1]);
535 $this->assertEquals(true, isset($result[0]));
537 $this->_checkInvoiceUpdateExistingTimeaccount($result[0]);
541 $this->sharedTimesheet->duration = 180;
542 $this->sharedTimesheet = $this->_timesheetController->update($this->sharedTimesheet);
544 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
545 if (isset($maybeRecreated[0])) {
546 $result = $maybeRecreated;
549 $this->_checkInvoiceUpdateExistingTimeaccount($result[0], 5);
552 protected function _checkInvoiceUpdateWithNewTimeaccount($invoiceId)
554 $ipc = Sales_Controller_InvoicePosition::getInstance();
555 $f = new Sales_Model_InvoicePositionFilter(array(
556 array('field' => 'model', 'operator' => 'equals', 'value' => 'Timetracker_Model_Timeaccount'),
557 array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
558 array('field' => 'id', 'operator' => 'equals', 'value' => $invoiceId),
561 $positions = $ipc->search($f);
562 $this->assertEquals(1, $positions->count());
563 $this->assertEquals(2, $positions->getFirstRecord()->quantity);
568 public function testInvoiceUpdateWithNewTimeaccount()
570 $result = $this->_createInvoiceUpdateRecreationFixtures(false);
572 $this->_timesheetController->create($this->sharedTimesheet);
574 $maybeRecreated = $this->_invoiceController->checkForUpdate($result['created'][1]);
575 if (isset($maybeRecreated[0])) {
576 $result = $maybeRecreated;
578 $result = array($result['created'][1]);
581 $this->_checkInvoiceUpdateWithNewTimeaccount($result[0]);
583 //check that the same update run doesnt do anything anymore
584 $maybeRecreated = $this->_invoiceController->checkForUpdate($result[0]);
585 if (isset($maybeRecreated[0])) {
586 $result = $maybeRecreated;
589 $this->_checkInvoiceUpdateWithNewTimeaccount($result[0]);
592 public function testCheckForContractOrInvoiceUpdatesWithNewTimeaccount()
594 $result = $this->_createInvoiceUpdateRecreationFixtures(false);
596 $this->_timesheetController->create($this->sharedTimesheet);
598 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
599 if (isset($maybeRecreated[0])) {
600 $result = $maybeRecreated;
602 $result = array($result['created'][1]);
605 $this->_checkInvoiceUpdateWithNewTimeaccount($result[0]);
607 $maybeRecreated = $this->_invoiceController->checkForContractOrInvoiceUpdates();
608 if (isset($maybeRecreated[0])) {
609 $result = $maybeRecreated;
612 $this->_checkInvoiceUpdateWithNewTimeaccount($result[0]);
618 * make sure timeaccounts won't be billed if they shouldn't
620 public function testBudgetTimeaccountBilled()
622 $this->_createFullFixtures();
624 $date = clone $this->_referenceDate;
627 // do not set to bill, this ta has a budget
628 $customer1Timeaccount = $this->_timeaccountRecords->filter('title', 'TA-for-Customer1')->getFirstRecord();
629 $customer1Timeaccount->status = 'not yet billed';
631 $tsController = Timetracker_Controller_Timesheet::getInstance();
632 $taController = Timetracker_Controller_Timeaccount::getInstance();
633 $taController->update($customer1Timeaccount);
635 // this is a ts on 20xx-01-18
636 $timesheet = new Timetracker_Model_Timesheet(array(
637 'account_id' => Tinebase_Core::getUser()->getId(),
638 'timeaccount_id' => $customer1Timeaccount->getId(),
639 'start_date' => $date->addDay(17),
641 'description' => 'ts from ' . (string) $date,
644 $tsController->create($timesheet);
646 // this is a ts on 20xx-02-03
647 $timesheet->id = NULL;
648 $timesheet->start_date = $date->addDay(17);
649 $timesheet->description = 'ts from ' . (string) $date;
651 $tsController->create($timesheet);
653 $date = clone $this->_referenceDate;
656 $result = $this->_invoiceController->createAutoInvoices($date);
658 $this->assertEquals(1, count($result['created']));
660 $customer1Timeaccount->status = 'to bill';
661 $taController->update($customer1Timeaccount);
665 $result = $this->_invoiceController->createAutoInvoices($date);
666 $this->assertEquals(1, count($result['created']));
668 $invoiceId = $result['created'][0];
669 $invoice = $this->_invoiceController->get($invoiceId);
672 foreach($invoice->relations as $relation) {
673 if ($relation->related_model == 'Timetracker_Model_Timeaccount') {
674 $this->assertEquals('TA-for-Customer1', $relation->related_record->title);
679 $this->assertTrue($found, 'the timeaccount could not be found in the invoice!');
684 * tests if the rights work: Sales_Acl_Rights::SET_INVOICE_NUMBER, Sales_Acl_Rights::MANAGE_INVOICES
686 public function testSetManualNumberRight()
688 $this->_createCustomers();
689 $this->_createCostCenters();
691 $customer = $this->_customerRecords->filter('name', 'Customer1')->getFirstRecord();
692 $invoice = $this->_invoiceController->create(new Sales_Model_Invoice(array(
693 'number' => 'R-3000',
694 'customer_id' => $customer->getId(),
695 'description' => 'Manual',
696 'address_id' => $this->_addressRecords->filter('customer_id', $customer->getId())->getFirstRecord()->getId(),
697 'costcenter_id' => $this->_costcenterRecords->getFirstRecord()->getId()
701 $group = Tinebase_Group::getInstance()->getGroupByName('Users');
702 $groupId = $group->getId();
705 $user = new Tinebase_Model_FullUser(array(
706 'accountLoginName' => 'testuser',
707 'accountPrimaryGroup' => $groupId,
708 'accountDisplayName' => 'Test User',
709 'accountLastName' => 'User',
710 'accountFirstName' => 'Test',
711 'accountFullName' => 'Test User',
712 'accountEmailAddress' => 'unittestx8@tine20.org',
715 $user = Admin_Controller_User::getInstance()->create($user, 'pw', 'pw');
716 $this->_testUser = Tinebase_Core::getUser();
718 Tinebase_Core::set(Tinebase_Core::USER, $user);
720 $e = new Exception('No Message');
723 $invoice = $this->_invoiceController->create(new Sales_Model_Invoice(array(
724 'number' => 'R-3001',
725 'customer_id' => $customer->getId(),
726 'description' => 'Manual Forbidden',
727 'address_id' => $this->_addressRecords->filter('customer_id', $customer->getId())->getFirstRecord()->getId(),
728 'costcenter_id' => $this->_costcenterRecords->getFirstRecord()->getId()
730 } catch (Exception $e) {
733 $this->assertTrue(get_class($e) == 'Tinebase_Exception_AccessDenied');
734 $this->assertTrue($e->getMessage() == 'You don\'t have the right to manage invoices!');
736 Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
738 $fe = new Admin_Frontend_Json();
739 $userRoles = $fe->getRoles('user', array(), array(), 0, 1);
740 $userRole = $fe->getRole($userRoles['results'][0]['id']);
742 $roleRights = $fe->getRoleRights($userRole['id']);
743 $roleMembers = $fe->getRoleMembers($userRole['id']);
744 $roleMembers['results'][] = array('name' => 'testuser', 'type' => 'user', 'id' => $user->accountId);
746 $app = Tinebase_Application::getInstance()->getApplicationByName('Sales');
748 $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Sales_Acl_Rights::MANAGE_INVOICES);
749 $fe->saveRole($userRole, $roleMembers['results'], $roleRights['results']);
751 Tinebase_Core::set(Tinebase_Core::USER, $user);
753 $e = new Exception('No Message');
756 $invoice = $this->_invoiceController->create(new Sales_Model_Invoice(array(
757 'number' => 'R-3001',
758 'customer_id' => $customer->getId(),
759 'description' => 'Manual Forbidden',
760 'address_id' => $this->_addressRecords->filter('customer_id', $customer->getId())->getFirstRecord()->getId(),
761 'costcenter_id' => $this->_costcenterRecords->getFirstRecord()->getId()
763 } catch (Exception $e) {
766 $this->assertEquals('Tinebase_Exception_AccessDenied', get_class($e));
767 $this->assertEquals('You have no right to set the invoice number!', $e->getMessage());
771 * tests if a product aggregate gets billed in the correct periods
773 public function testOneProductContractInterval()
775 $startDate = clone $this->_referenceDate;
777 $this->_createProducts();
779 $this->_createCustomers(1);
780 $this->_createCostCenters();
782 $monthBack = clone $this->_referenceDate;
783 $monthBack->subMonth(1);
784 $addressId = $this->_addressRecords->filter(
785 'customer_id', $this->_customerRecords->filter(
786 'name', 'Customer1')->getFirstRecord()->getId())->filter(
787 'type', 'billing')->getFirstRecord()->getId();
789 $this->assertTrue($addressId !== NULL);
791 // this contract begins 6 months before the first invoice will be created
792 $this->_createContracts(array(array(
794 'title' => 'MyContract',
795 'description' => 'unittest',
796 'container_id' => $this->_sharedContractsContainerId,
797 'billing_point' => 'begin',
798 'billing_address_id' => $addressId,
801 'start_date' => $startDate->subMonth(6),
802 'last_autobill' => clone $this->_referenceDate,
805 array('product_id' => $this->_productRecords->getByIndex(0)->getId(), 'quantity' => 1, 'interval' => 1, 'last_autobill' => $monthBack),
809 $startDate = clone $this->_referenceDate;
810 $startDate->addDay(5);
811 $startDate->addMonth(24);
813 $result = $this->_invoiceController->createAutoInvoices($startDate);
814 $this->assertEquals(25, $result['created_count']);
816 $invoices = $this->_invoiceController->getAll('start_date');
817 $firstInvoice = $invoices->getFirstRecord();
818 $this->assertInstanceOf('Tinebase_DateTime', $firstInvoice->start_date);
819 $this->assertEquals('0101', $firstInvoice->start_date->format('md'));
821 $this->assertEquals(25, $invoices->count());
823 $filter = new Sales_Model_InvoicePositionFilter(array());
824 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'in', 'value' => $invoices->getArrayOfIds())));
826 $pagination = new Tinebase_Model_Pagination(array('sort' => 'month', 'dir' => 'ASC'));
828 $invoicePositions = Sales_Controller_InvoicePosition::getInstance()->search($filter, $pagination);
830 // get sure each invoice positions has the same month as the invoice and the start_date is the first
831 foreach($invoices as $invoice) {
832 $month = (int) $invoice->start_date->format('n');
835 $this->assertEquals('01', $invoice->start_date->format('d'));
836 $this->assertEquals($invoice->end_date->format('t'), $invoice->end_date->format('d'), print_r($invoice->toArray(), 1));
838 $this->assertEquals(1, $invoice->start_date->format('d'));
840 $pos = $invoicePositions->filter('invoice_id', $invoice->getId())->getFirstRecord();
841 $this->assertEquals($invoice->start_date->format('Y-m'), $pos->month);
842 $this->assertEquals($invoice->end_date->format('Y-m'), $pos->month);
845 $this->assertEquals(25, $invoicePositions->count());
847 $this->assertEquals($this->_referenceYear . '-01', $invoicePositions->getFirstRecord()->month);
849 $invoicePositions->sort('month', 'DESC');
851 $this->assertEquals($this->_referenceYear + 2 . '-01', $invoicePositions->getFirstRecord()->month);
855 * test product only contract setting last_autobill and resetting last_autobill on delete
857 public function testLastAutobillAfterDeleteInvoice()
859 $startDate = clone $this->_referenceDate;
860 $lab = clone $this->_referenceDate;
862 $this->_createProducts(array(array(
864 'description' => 'timesheets',
866 'accountable' => 'Timetracker_Model_Timeaccount'
869 $this->_createCustomers(1);
870 $this->_createCostCenters();
872 // has budget, is to bill
873 $ta = $this->_createTimeaccounts(array(array(
875 'description' => 'blabla',
880 )))->getFirstRecord();
882 // has timeaccount without budget, must be billed at end of the period (each month has at least one timesheet)
883 $this->_createContracts(array(array(
885 'title' => 'MyContract',
886 'description' => 'unittest',
887 'container_id' => $this->_sharedContractsContainerId,
888 'billing_address_id' => $this->_addressRecords->filter(
889 'customer_id', $this->_customerRecords->filter(
890 'name', 'Customer1')->getFirstRecord()->getId())->filter(
891 'type', 'billing')->getFirstRecord()->getId(),
893 'start_date' => $startDate,
894 'last_autobill' => NULL,
897 array('product_id' => $this->_productRecords->getByIndex(0)->getId(),
898 'quantity' => 1, 'interval' => 1, 'billing_point' => 'end'),
903 $tsDate = clone $this->_referenceDate;
908 $this->_createTimesheets(array(array(
909 'account_id' => Tinebase_Core::getUser()->getId(),
910 'timeaccount_id' => $ta->getId(),
911 'start_date' => $tsDate,
913 'description' => 'ts from ' . (string) $tsDate,
915 $tsDate->addMonth(1);
920 $contract = $this->_contractController->getAll()->getFirstRecord();
921 $this->assertEquals($startDate->__toString(), $contract->start_date->__toString());
923 // find product aggregate
924 $paController = Sales_Controller_ProductAggregate::getInstance();
925 $productAggregate = $paController->getAll()->getFirstRecord();
926 $productAggregate->setTimezone(Tinebase_Core::getUserTimezone());
928 $this->assertEquals(NULL, $productAggregate->last_autobill);
930 // create 6 invoices - each month one invoice - last autobill must be increased each month
931 for ($i = 1; $i < 7; $i++) {
932 $myDate = clone $this->_referenceDate;
933 $myDate->addMonth($i)->addHour(3);
935 $testDate = clone $this->_referenceDate;
936 $testDate->addMonth($i);
938 $result = $this->_invoiceController->createAutoInvoices($myDate);
939 $this->assertEquals(1, $result['created_count']);
941 $productAggregate = $paController->get($productAggregate->getId());
942 $productAggregate->setTimezone(Tinebase_Core::getUserTimezone());
943 $this->assertEquals($testDate, $productAggregate->last_autobill);
946 $testDate = clone $this->_referenceDate;
947 $testDate->addMonth(6);
948 $this->assertEquals($testDate, $productAggregate->last_autobill);
950 // delete all created invoices again
951 $allInvoices = $this->_invoiceController->getAll('start_date', 'DESC');
953 foreach($allInvoices as $invoice) {
954 $this->_invoiceController->delete($invoice);
957 $productAggregate = $paController->get($productAggregate->getId());
958 $productAggregate->setTimezone(Tinebase_Core::getUserTimezone());
960 $this->assertEquals($this->_referenceDate, $productAggregate->last_autobill);
962 // create 6 invoices again - each month one invoice - last autobill must be increased each month
963 for ($i = 1; $i < 7; $i++) {
964 $myDate = clone $this->_referenceDate;
965 $myDate->addMonth($i)->addHour(3);
967 $testDate = clone $this->_referenceDate;
968 $testDate->addMonth($i);
970 $result = $this->_invoiceController->createAutoInvoices($myDate);
971 $this->assertEquals(1, $result['created_count']);
973 $productAggregate = $paController->get($productAggregate->getId());
974 $productAggregate->setTimezone(Tinebase_Core::getUserTimezone());
975 $this->assertEquals($testDate, $productAggregate->last_autobill);
978 $testDate = clone $this->_referenceDate;
979 $testDate->addMonth(6);
980 $this->assertEquals($testDate, $productAggregate->last_autobill);
987 * products must be billed on the beginning of a period
988 * the first dates must fit
990 public function testProductWithOneYearInterval()
992 // last year, the 1.1 in usertimezone
993 $date = clone $this->_referenceDate;
995 $startDateContract = clone $this->_referenceDate;
996 $startDateContract->subMonth(13);
998 // this has been billed for the last year.
999 $startDateProduct = clone $this->_referenceDate;
1000 $startDateProduct->subMonth(12);
1002 $this->_createProducts();
1004 $this->_createCustomers(1);
1005 $this->_createCostCenters();
1007 $addressId = $this->_addressRecords->filter(
1008 'customer_id', $this->_customerRecords->filter(
1009 'name', 'Customer1')->getFirstRecord()->getId())->filter(
1010 'type', 'billing')->getFirstRecord()->getId();
1012 // the contract has an interval of 0, but it has to be billed
1013 $this->_createContracts(array(array(
1015 'title' => 'MyContract',
1016 'description' => 'unittest',
1017 'container_id' => $this->_sharedContractsContainerId,
1018 'billing_point' => 'begin',
1019 'billing_address_id' => $addressId,
1021 'start_date' => $startDateContract,
1022 'last_autobill' => clone $this->_referenceDate,
1024 'products' => array(
1025 array('product_id' => $this->_productRecords->getByIndex(0)->getId(),
1026 'quantity' => 1, 'interval' => 12, 'last_autobill' => $startDateProduct),
1030 $startDate = clone $this->_referenceDate;
1031 $startDate->addHour(3);
1035 $result = $this->_invoiceController->createAutoInvoices($startDate);
1036 $this->assertEquals(1, $result['created_count']);
1038 $invoice = $this->_invoiceController->get($result['created'][0]);
1040 $invoicePositions = Sales_Controller_InvoicePosition::getInstance()->getAll('month')->filter('invoice_id', $result['created'][0]);
1041 $this->assertEquals(12, $invoicePositions->count());
1045 foreach($invoicePositions as $ipo) {
1046 $this->assertEquals($this->_referenceYear . '-' . ($i > 9 ? '' : '0') . $i, $ipo->month, print_r($invoicePositions->toArray(), 1));
1047 $this->assertEquals($ipo->invoice_id, $invoice->getId());
1053 * make sure that timesheets get created for the right month
1055 public function testTimesheetOnMonthEndAndBegin()
1058 clone $this->_referenceDate,
1059 clone $this->_referenceDate,
1060 clone $this->_referenceDate,
1061 clone $this->_referenceDate
1063 // 0: 1.1.xxxx, 1: 31.1.xxxx, 2: 1.2.xxxx, 3: 28/29.2.xxxx
1064 $dates[1]->addMonth(1)->subDay(1);
1065 $dates[2]->addMonth(1);
1066 $dates[3]->addMonth(2)->subDay(1);
1068 $customer = $this->_createCustomers(1)->getFirstRecord();
1069 $this->_createCostCenters();
1072 $ta = $this->_createTimeaccounts(array(array(
1073 'title' => 'TaTest',
1074 'description' => 'blabla',
1076 'status' => 'not yet billed',
1078 )))->getFirstRecord();
1080 foreach($dates as $date) {
1081 $this->_createTimesheets(array(
1083 'account_id' => Tinebase_Core::getUser()->getId(),
1084 'timeaccount_id' => $ta->getId(),
1085 'start_date' => $date,
1087 'description' => 'ts from ' . (string) $date,
1092 $this->assertEquals(4, $this->_timesheetRecords->count());
1094 $csDate = clone $this->_referenceDate;
1095 $csDate->subMonth(10);
1097 $lab = clone $this->_referenceDate;
1098 // set start position (must be manually set on introducing the invoice module)
1100 $this->_createProducts();
1101 $this->_createContracts(array(array(
1103 'title' => 'MyContract',
1104 'description' => 'unittest',
1105 'container_id' => $this->_sharedContractsContainerId,
1106 'billing_point' => 'begin',
1107 'billing_address_id' => $this->_addressRecords->filter(
1108 'customer_id', $customer->getId())->filter(
1109 'type', 'billing')->getFirstRecord()->getId(),
1111 'start_date' => $csDate,
1113 'products' => array(
1114 array('start_date' => $csDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 1, 'billing_point' => 'end', 'product_id' => $this->_productRecords->filter('name', 'Hours')->getFirstRecord()->getId()),
1118 $json = new Sales_Frontend_Json();
1120 $date = clone $this->_referenceDate;
1121 // this is set by cli if called by cli
1122 $date->setTime(3,0,0);
1124 $result = $this->_invoiceController->createAutoInvoices($date);
1125 $this->assertEquals(0, $result['created_count'], (string) $date);
1128 $result = $this->_invoiceController->createAutoInvoices($date);
1129 $this->assertEquals(1, $result['created_count'], (string) $date);
1130 $invoice1Id = $result['created'][0];
1131 $invoice = $json->getInvoice($invoice1Id);
1132 $this->assertEquals(1, count($invoice['positions']), print_r($invoice['positions'], 1));
1135 $result = $this->_invoiceController->createAutoInvoices($date);
1136 $this->assertEquals(1, $result['created_count'], (string) $date);
1137 $invoice2Id = $result['created'][0];
1138 $invoice = $json->getInvoice($invoice2Id);
1139 $this->assertEquals(1, count($invoice['positions']));
1142 $result = $this->_invoiceController->createAutoInvoices($date);
1143 $this->assertEquals(0, $result['created_count'], (string) $date);
1145 $filter = new Timetracker_Model_TimesheetFilter(array());
1146 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice1Id)));
1147 $timesheets = $this->_timesheetController->search($filter);
1148 $this->assertEquals(2, $timesheets->count());
1150 $filter = new Timetracker_Model_TimesheetFilter(array());
1151 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice2Id)));
1152 $timesheets = $this->_timesheetController->search($filter);
1153 $this->assertEquals(2, $timesheets->count());
1155 // now try to delete the first invoice, which is not allowed
1156 $this->setExpectedException('Sales_Exception_DeletePreviousInvoice');
1158 $this->_invoiceController->delete(array($invoice1Id));
1162 * make sure that timesheets get created for the right month
1164 public function test2MonthIntervalTimesheetOnMonthEndAndBegin()
1167 clone $this->_referenceDate,
1168 clone $this->_referenceDate,
1169 clone $this->_referenceDate,
1170 clone $this->_referenceDate
1172 // 0: 1.1.xxxx, 1: 31.1.xxxx, 2: 1.2.xxxx, 3: 28/29.2.xxxx
1173 $dates[1]->addMonth(1)->subDay(1);
1174 $dates[2]->addMonth(1);
1175 $dates[3]->addMonth(2)->subDay(1);
1177 // create much more timesheets
1178 $dt = clone $this->_referenceDate;
1179 for ($i = 0; $i < 80; $i++) {
1181 $dates[] = clone $dt;
1184 $customer = $this->_createCustomers(1)->getFirstRecord();
1185 $this->_createCostCenters();
1188 $ta = $this->_createTimeaccounts(array(array(
1189 'title' => 'TaTest',
1190 'description' => 'blabla',
1192 'status' => 'not yet billed',
1194 )))->getFirstRecord();
1196 foreach($dates as $date) {
1197 $this->_createTimesheets(array(
1199 'account_id' => Tinebase_Core::getUser()->getId(),
1200 'timeaccount_id' => $ta->getId(),
1201 'start_date' => $date,
1203 'description' => 'ts from ' . (string) $date,
1208 $this->assertEquals(84, $this->_timesheetRecords->count());
1209 $this->_createProducts();
1210 $csDate = clone $this->_referenceDate;
1211 $csDate->subMonth(10);
1213 $lab = clone $this->_referenceDate;
1214 $this->_createContracts(array(array(
1216 'title' => 'MyContract',
1217 'description' => 'unittest',
1218 'container_id' => $this->_sharedContractsContainerId,
1219 'billing_point' => 'begin',
1220 'billing_address_id' => $this->_addressRecords->filter(
1221 'customer_id', $customer->getId())->filter(
1222 'type', 'billing')->getFirstRecord()->getId(),
1224 'start_date' => $csDate,
1226 'products' => array(
1227 array('start_date' => $csDate, 'end_date' => NULL, 'quantity' => 1, 'interval' => 1, 'billing_point' => 'end', 'product_id' => $this->_productRecords->filter('name', 'Hours')->getFirstRecord()->getId())
1231 $json = new Sales_Frontend_Json();
1233 $date = clone $this->_referenceDate;
1234 // this is set by cli if called by cli
1235 $date->setTime(3,0,0);
1237 $result = $this->_invoiceController->createAutoInvoices($date);
1238 $this->assertEquals(0, $result['created_count']);
1241 $result = $this->_invoiceController->createAutoInvoices($date);
1242 $this->assertEquals(1, $result['created_count']);
1244 $invoice1Id = $result['created'][0];
1245 $filter = new Timetracker_Model_TimesheetFilter(array());
1246 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice1Id)));
1247 $timesheets = $this->_timesheetController->search($filter);
1248 $this->assertEquals(63, $timesheets->count());
1251 $result = $this->_invoiceController->createAutoInvoices($date);
1252 $this->assertEquals(1, $result['created_count'], (string) $date);
1253 $invoice2Id = $result['created'][0];
1254 $invoice = $json->getInvoice($invoice2Id);
1255 $this->assertEquals(1, count($invoice['positions']));
1258 $result = $this->_invoiceController->createAutoInvoices($date);
1259 $this->assertEquals(0, $result['created_count']);
1261 $filter = new Timetracker_Model_TimesheetFilter(array());
1262 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoice2Id)));
1263 $timesheets = $this->_timesheetController->search($filter);
1264 $this->assertEquals(21, $timesheets->count());
1270 public function testManualInvoice()
1272 $customer = $this->_createCustomers(1)->getFirstRecord();
1273 $this->_createCostCenters();
1275 $invoice = $this->_invoiceController->create(new Sales_Model_Invoice(array(
1277 'description' => 'test',
1278 'address_id' => $this->_addressRecords->getFirstRecord()->getId(),
1279 'costcenter_id' => $this->_costcenterRecords->getFirstRecord()->getId(),
1281 'price_net' => 200.20,
1282 'price_gross' => 238.45,
1286 $this->assertEquals(19.5, $invoice->sales_tax);
1287 $this->assertEquals(200.20, $invoice->price_net);
1288 $this->assertEquals(238.45, $invoice->price_gross);
1292 * tests if timesheets get resetted properly after deleting the invoice
1293 * and recreate the same invoice again containing the same timesheets
1295 public function testDeleteAndRunAgainInvoice()
1297 $this->_createFullFixtures();
1299 $date = clone $this->_referenceDate;
1303 $result = $this->_invoiceController->createAutoInvoices($date);
1305 $this->assertEquals(6, count($result['created']));
1307 $tsController = Timetracker_Controller_Timesheet::getInstance();
1309 // get first valid invoice id from all timesheets
1310 $tsInvoiceIds = array_unique($tsController->getAll()->invoice_id);
1311 sort($tsInvoiceIds);
1312 $tsInvoiceIds = array_reverse($tsInvoiceIds);
1313 $this->assertTrue(! empty($tsInvoiceIds[0]));
1314 $myInvoice = $this->_invoiceController->get($tsInvoiceIds[0]);
1316 $f = new Timetracker_Model_TimesheetFilter(array());
1317 $f->addFilter(new Tinebase_Model_Filter_Text(
1318 array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $myInvoice->getId())
1320 $myTimesheets = $tsController->search($f);
1321 $this->assertEquals(2, $myTimesheets->count(), 'timesheets not found for invoice ' . $myInvoice->getId());
1323 $this->_invoiceController->delete(array($myInvoice->getId()));
1324 $allTimesheets = $tsController->getAll();
1325 foreach($allTimesheets as $ts) {
1326 $this->assertSame(NULL, $ts->invoice_id, 'invoice id should be reset');
1329 $this->_invoiceController->createAutoInvoices($date);
1331 $tsId = $myTimesheets->getFirstRecord()->getId();
1333 $myTimesheet = $tsController->get($tsId);
1334 $f = new Timetracker_Model_TimesheetFilter(array());
1335 $f->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $myTimesheet->invoice_id)));
1337 $myTimesheets = $tsController->search($f);
1338 $this->assertEquals(2, $myTimesheets->count());
1340 foreach($myTimesheets as $ts) {
1341 $this->assertEquals(40, strlen($ts->invoice_id));
1345 public function testInterval12LastAutobill()
1347 $startDate = clone $this->_referenceDate;
1348 $startDate->subYear(1);
1350 $this->_createProducts(array(
1351 array('name' => 'bill yearly',
1352 'description' => 'bill every year',
1353 'price' => '1002','accountable' => 'Sales_Model_Product')
1355 $this->_createCustomers(1);
1356 $this->_createCostCenters();
1357 $addressId = $this->_addressRecords->filter(
1358 'customer_id', $this->_customerRecords->filter(
1359 'name', 'Customer1')->getFirstRecord()->getId())->filter(
1360 'type', 'billing')->getFirstRecord()->getId();
1362 // this contract begins 6 months before the first invoice will be created
1363 $this->_createContracts(array(array(
1365 'title' => 'MyContract',
1366 'description' => 'unittest',
1367 'container_id' => $this->_sharedContractsContainerId,
1368 'billing_point' => 'begin',
1369 'billing_address_id' => $addressId,
1371 'start_date' => $startDate,
1372 'last_autobill' => $startDate,
1374 'products' => array(
1375 array('product_id' => $this->_productRecords->getByIndex(0)->getId(), 'quantity' => 1, 'interval' => 12, 'last_autobill' => $startDate),
1379 $startDate = clone $this->_referenceDate;
1380 $startDate->subMonth(1);
1382 $startDate = clone $this->_referenceDate;
1383 $startDate->addDay(5);
1384 $result = $this->_invoiceController->createAutoInvoices($startDate);
1386 $this->assertEquals(1, $result['created_count']);
1388 $invoices = $this->_invoiceController->getAll();
1389 $firstInvoice = $invoices->getFirstRecord();
1390 $this->assertEquals(1, $invoices->count());
1392 $filter = new Sales_Model_InvoicePositionFilter(array());
1393 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'in', 'value' => $invoices->getArrayOfIds())));
1395 $invoicePositions = Sales_Controller_InvoicePosition::getInstance()->search($filter);
1397 $this->assertEquals(12, $invoicePositions->count());
1399 $contract = $this->_contractRecords->getFirstRecord();
1400 $contract->setTimezone(Tinebase_Core::getUserTimezone());
1402 $autobillDate = clone $this->_referenceDate;
1404 for ($i = 0; $i < 8; $i++) {
1405 $startDate->addDay(1);
1406 $result = $this->_invoiceController->createAutoInvoices($startDate);
1407 $this->assertEquals(0, $result['created_count']);
1410 $productAggregate = Sales_Controller_ProductAggregate::getInstance()->getAll()->getFirstRecord();
1411 $productAggregate->setTimezone(Tinebase_Core::getUserTimezone());
1412 $this->assertEquals($autobillDate, $productAggregate->last_autobill);
1416 * tests if uncleared invoices gets deleted
1418 public function testUnclearedDeletion()
1420 $this->_createFullFixtures();
1422 $date = clone $this->_referenceDate;
1426 $result = $this->_invoiceController->createAutoInvoices($date);
1428 $this->assertEquals(6, count($result['created']));
1430 $invoice = $this->_invoiceController->get($result['created'][0]);
1431 $invoice->cleared = 'CLEARED';
1432 $this->_invoiceController->update($invoice);
1434 $cli = new Sales_Frontend_Cli();
1435 $cli->removeUnbilledAutoInvoices();
1437 $invoices = $this->_invoiceController->getAll();
1439 $this->assertEquals(1, $invoices->count());
1443 * if no productaggregates are defined for a contract, but
1444 * accountables are related, use default billing Info from accountable
1445 * (product will be created if it does not exist - is needed in the invoice position)
1447 public function testDefaultAutobillInterval()
1449 $startDate = clone $this->_referenceDate;
1450 $startDate->subYear(1);
1452 $this->_createCustomers(1);
1453 $this->_createCostCenters();
1455 $this->_createTimeaccounts(array(array(
1457 'description' => 'blabla',
1459 'status' => 'to bill',
1463 $addressId = $this->_addressRecords->filter(
1464 'customer_id', $this->_customerRecords->filter(
1465 'name', 'Customer1')->getFirstRecord()->getId())->filter(
1466 'type', 'billing')->getFirstRecord()->getId();
1468 // this contract begins 6 months before the first invoice will be created
1469 $this->_createContracts(array(array(
1471 'title' => 'MyContract',
1472 'description' => 'unittest',
1473 'container_id' => $this->_sharedContractsContainerId,
1474 'billing_point' => 'begin',
1475 'billing_address_id' => $addressId,
1477 'start_date' => $startDate,
1481 $startDate = clone $this->_referenceDate;
1482 $startDate->subMonth(1);
1484 $startDate = clone $this->_referenceDate;
1485 $startDate->addDay(5);
1486 $result = $this->_invoiceController->createAutoInvoices($startDate);
1488 $this->assertEquals(1, $result['created_count']);
1490 $filter = new Sales_Model_ProductFilter(array());
1491 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'accountable', 'operator' => 'equals', 'value' => 'Timetracker_Model_Timeaccount')));
1493 $products = Sales_Controller_Product::getInstance()->search($filter);
1494 $this->assertEquals(1, $products->count());
1497 $this->assertEquals('Timetracker_Model_Timeaccount', $products->getFirstRecord()->accountable);
1499 $filter = new Sales_Model_InvoicePositionFilter(array());
1500 $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'invoice_id', 'operator' => 'equals', 'value' => $result['created'][0])));
1502 $invoicePositions = Sales_Controller_InvoicePosition::getInstance()->search($filter);
1504 $this->assertEquals(1, $invoicePositions->count());