Skip to content

Commit

Permalink
Merge branch '2024.11'
Browse files Browse the repository at this point in the history
  • Loading branch information
gitlabci committed Feb 19, 2025
2 parents d936233 + cf0e22a commit 557ae1f
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 6 deletions.
1 change: 1 addition & 0 deletions ci/gitlab-ci/test_php_jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ php-unit-all-tests-ldap-source-parallel:
- "tine20/Tinebase/User.php"
- "tine20/Tinebase/User/*.php"
- "tine20/Tinebase/User/LdapPlugin/*.php"
- "tine20/Tinebase/Group.php"
- "tine20/Tinebase/Group/*.php"
- "tine20/Tinebase/Group/LdapPlugin/*.php"
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /php-unit-all-tests-ad-source/
Expand Down
80 changes: 80 additions & 0 deletions tests/tine20/Sales/InvoiceJsonTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,86 @@ public function testClearing()
}
}

/**
* tests if timeaccounts/timesheets get cleared if the invoice get billed
*/
public function testRecreateInvoicePositionAfterUpdateClearedTimesheet()
{
$this->_createFullFixtures();

// the whole year, 12 months
$date = clone $this->_referenceDate;
$date->addMonth(6);
$this->_invoiceController->createAutoInvoices($date);

$timeaccountFilter = array(
array('field' => 'foreignRecord', 'operator' => 'AND', 'value' => array(
'appName' => 'Sales',
'linkType' => 'relation',
'modelName' => 'Customer',
'filters' => array(
array('field' => 'name', 'operator' => 'equals', 'value' => 'Customer3')
)
))

);
// test if timesheets get cleared
$invoices = $this->_uit->searchInvoices($timeaccountFilter, array());

$invoiceIds = array();

$this->assertEquals(1, $invoices['totalcount']);

foreach($invoices['results'] as $invoice) {
$invoiceIds[] = $invoice['id'];
// fetch invoice by get to have all relations set
$invoice = $this->_uit->getInvoice($invoice['id']);
$invoice['cleared'] = 'CLEARED';
$this->_uit->saveInvoice($invoice);
}
$invoiceId = $invoices['results'][0]['id'];
$this->assertEquals(0,$invoice['price_net']);

Timetracker_Controller_Timesheet::destroyInstance();
$tsController = Timetracker_Controller_Timesheet::getInstance();
$timesheets = $tsController->search(
Tinebase_Model_Filter_FilterGroup::getFilterForModel(Timetracker_Model_Timesheet::class, [
['field' => 'invoice_id', 'operator' => 'equals', 'value' => $invoiceId],
['field' => 'start_time', 'operator' => 'equals', 'value' => '09:20:00'],
['field' => 'start_date', 'operator' => 'equals', 'value' => '2024-05-08'],
]
));

foreach($timesheets as $timesheet) {
$this->assertTrue(in_array($timesheet->invoice_id, $invoiceIds), 'the invoice id must be set!');
$this->assertEquals(1, $timesheet->is_cleared);
}

$invoice = $this->_uit->getInvoice($invoiceId);
$this->assertEquals(1, count($invoice['positions']));
$position = $invoice['positions'][0];
$this->assertEquals('2024-05', $position['month']);
$this->assertEquals(7.0, $position['quantity']);

//test update ts date after status set to is_cleared
Timetracker_Controller_Timesheet::getInstance()->setRequestContext(['confirm' => true]);
$timesheets[0]->start_time = '09:00:00';
$timesheets[0]->end_time = '12:00:00';
$timesheet = $tsController->update($timesheets[0]);
$this->assertEquals(1, $timesheet['is_cleared']);
$this->assertEquals($invoiceId, $timesheet['invoice_id']);

// get the invoice again
$invoices = $this->_uit->searchInvoices($timeaccountFilter, array());
$invoice = $this->_uit->getInvoice($invoices['results'][0]['id']);

$this->assertEquals(1, count($invoice['positions']));
$position = $invoice['positions'][0];

$this->assertEquals('2024-05', $position['month']);
$this->assertEquals(10.0, $position['quantity']);
}

/**
* tests if delete timesheet throw exception when timesheet is cleared with invoice id
*/
Expand Down
3 changes: 2 additions & 1 deletion tine20/Sales/Controller/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,8 @@ protected function _findInvoicePositionsAndInvoiceInterval(&$billableAccountable

foreach ($billableAccountables as &$ba) {
$ba['partOfInvoice'] = false;
if (! $ba['ac']->isBillable($this->_currentMonthToBill, $this->_currentBillingContract, $ba['pa'])) {
//fixme: isBillable only filtered timesheets with is_cleared = 0, how to make cleared timesheet billable ?
if (!$ba['ac']->isBillable($this->_currentMonthToBill, $this->_currentBillingContract, $ba['pa'])) {
if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' isBillable failed for the accountable ' . $ba['ac']->getId() . ' of contract "' . $this->_currentBillingContract->number . '"');
}
Expand Down
16 changes: 12 additions & 4 deletions tine20/Timetracker/Controller/Timesheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,9 @@ protected function _inspectBeforeUpdate($_record, $_oldRecord)
$this->_calcClearedAmount($_record, $_oldRecord);

if ($this->_isTSDateChanged($_record, $_oldRecord) && $_record->is_cleared && !empty($_record->invoice_id)) {
$relation = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Invoice', 'Sql', $_record->invoice_id, 'sibling', ['CONTRACT'], 'Sales_Model_Contract')
->getFirstRecord();
$contract = Sales_Controller_Contract::getInstance()->get($relation->related_id);
Sales_Controller_Invoice::getInstance()->createAutoInvoices(null, $contract, true);
//reset invoicing related fields to find the invoice positions
$_record->is_cleared = false;
$_record->invoice_id = '';
}
}

Expand All @@ -389,6 +388,15 @@ protected function _inspectAfterUpdate($updatedRecord, $record, $currentRecord)
/** @var Timetracker_Model_Timesheet $updatedRecord */
if ($this->_isTSDateChanged($updatedRecord, $currentRecord)) {
$this->_tsChanged($updatedRecord, $currentRecord);
//need to clear timesheet after generate invoice position
if (!$updatedRecord->is_cleared && empty($updatedRecord->invoice_id) && !empty($currentRecord->invoice_id)) {
$result = Sales_Controller_Invoice::getInstance()->checkForUpdate($currentRecord->invoice_id);
if (in_array($currentRecord->invoice_id, $result)) {
$updatedRecord->is_cleared = true;
$updatedRecord->invoice_id = $currentRecord->invoice_id;
$this->getBackend()->update($updatedRecord);
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion tine20/Timetracker/Model/Timeaccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ public function needsInvoiceRecreation(Tinebase_DateTime $date, Sales_Model_Prod
$timesheets = Timetracker_Controller_Timesheet::getInstance()->search($filter);
foreach($timesheets as $timesheet)
{
if ($timesheet->last_modified_time && $timesheet->last_modified_time->isLater($invoice->creation_time)) {
if ($timesheet->last_modified_time && $timesheet->last_modified_time->isLater($invoice->creation_time) && !$timesheet->is_cleared) {
return true;
}
}
Expand Down

0 comments on commit 557ae1f

Please sign in to comment.