From 2c44edbe66788cd1823282dfde3d5526a2f01480 Mon Sep 17 00:00:00 2001 From: v-dumas Date: Thu, 28 May 2026 10:26:08 +0200 Subject: [PATCH 1/7] =?UTF-8?q?N=C2=B09160=20-=20V1=20pas=20finie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datamodel.itop-incident-mgmt-itil.xml | 68 ++++++++++--------- .../datamodel.itop-request-mgmt-itil.xml | 54 ++++++--------- .../datamodel.itop-request-mgmt.xml | 50 +++++--------- .../itop-tickets/datamodel.itop-tickets.xml | 44 ++++++++++++ 4 files changed, 116 insertions(+), 100 deletions(-) diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml index 3be94a74b2..42f62d18a9 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml +++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml @@ -460,6 +460,18 @@ parent_incident_id ref + + id AND status NOT IN ('rejected','resolved','closed')]]> + + parent_request_id + UserRequest + true + DEL_MANUAL + + + parent_request_id + ref + parent_problem_id Problem @@ -987,6 +999,9 @@ + + + @@ -1303,45 +1318,31 @@ { if (!MetaModel::IsValidClass('UserRequest')) return true; // Do nothing - $oLog = $this->Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } + $sParentEntryPublic = $this->Get('public_log')->GetModifiedEntry('html'); + $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); + if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { + return true; // nothing to update + } + $sChildEntryPublic = Dict::Format('Class:Incident:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; + $sChildEntryPrivate = Dict::Format('Class:Incident:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); + $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; + $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); + + while($oRequest = $oChildRequestSet->Fetch()) { + if ($sParentLogPublic != '') { + $oRequest->Set('public_log', $sChildEntryPublic); + } + if ($sParentLogPrivate != '') { + $oRequest->Set('private_log', $sChildEntryPrivate); + } $oRequest->DBUpdate(); - } } return true; }]]> + false public @@ -1558,6 +1559,9 @@ 10 + + 15 + 20 diff --git a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml index 766272cbec..4302036e72 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml +++ b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml @@ -1451,45 +1451,29 @@ public LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } +{ + $sParentEntryPublic = $this->Get('public_log')->GetModifiedEntry('html'); + $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); + if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { + return true; // nothing to update + } + $sChildEntryPublic = Dict::Format('Class:UserRequest:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; + $sChildEntryPrivate = Dict::Format('Class:UserRequest:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); + $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; + $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); + + while($oRequest = $oChildRequestSet->Fetch()) { + if ($sParentLogPublic != '') { + $oRequest->Set('public_log', $sChildEntryPublic); + } + if ($sParentLogPrivate != '') { + $oRequest->Set('private_log', $sChildEntryPrivate); + } $oRequest->DBUpdate(); - } } return true; - - }]]> +}]]> false diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index db1edcb30d..2fd7736364 100755 --- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml +++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml @@ -1487,43 +1487,27 @@ LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } + $sParentEntryPublic = $this->Get('public_log')->GetModifiedEntry('html'); + $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); + if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { + return true; // nothing to update + } + $sChildEntryPublic = Dict::Format('Class:UserRequest:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; + $sChildEntryPrivate = Dict::Format('Class:UserRequest:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); + $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; + $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); + + while($oRequest = $oChildRequestSet->Fetch()) { + if ($sParentLogPublic != '') { + $oRequest->Set('public_log', $sChildEntryPublic); + } + if ($sParentLogPrivate != '') { + $oRequest->Set('private_log', $sChildEntryPrivate); + } $oRequest->DBUpdate(); - } } return true; - }]]> diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index f9a5ec37f6..753c999165 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -351,6 +351,50 @@ }]]> + + false + public + LifecycleAction + GetTargetClass() !== get_class($this))) { + ErrorLog::Debug("Attribute $sChildParentAttCode should be an external key of class $sChildClass, pointing to class ".get_class($this),"DataModel"); + return true; // Do nothing + } + + $sParentClass = get_class($this); + $aParentEntries = []; + $aChildEntries = []; + foreach ($aLogAttCodes as $sAttCode) { + if (MetaModel::IsValidAttCode($sParentClass, $sAttCode) && (get_class(MetaModel::GetAttributeDef($sParentClass, $sAttCode)) == 'AttributeCaseLog') + && MetaModel::IsValidAttCode($sChildClass, $sAttCode) && (get_class(MetaModel::GetAttributeDef($sChildClass, $sAttCode)) == 'AttributeCaseLog') + && !IsNullOrEmptyString($this->Get($sAttCode)->GetModifiedEntry('html'))) ) { + $aParentEntries[$sAttCode] = $this->Get($sAttCode)->GetModifiedEntry('html'); + $aChildEntries[$sAttCode] = Dict::Format("Class:$sParentClass:LogFromParentPrefix", $this->Get('ref')).$aParentEntries[$sAttCode]; + } + } + } + if ($aParentEntries == []) { + return true; // nothing to update + } + + $sOQL = "SELECT :ticket_class WHERE :parent_code=:ticket"; + $oChildSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket_class' => $sChildClass, 'parent_code' => $sChildParentAttCode, 'ticket' => $this->GetKey()]); + while($oChild = $oChildSet->Fetch()) { + foreach ($aChildEntries as $sAttCode => $sEntry) { + $oChild->Set($sAttCode, $sEntry); + } + $oChild->DBUpdate(); + } + return true; + + }]]> +
From 09ea64e5d5669c832a0f40dd8f00c5ed03a3e0d9 Mon Sep 17 00:00:00 2001 From: v-dumas Date: Fri, 29 May 2026 17:10:01 +0200 Subject: [PATCH 2/7] =?UTF-8?q?N=C2=B09160=20-=20Refactor=20UpdateChildxxx?= =?UTF-8?q?xxLog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datamodel.itop-incident-mgmt-itil.xml | 64 +------ .../datamodel.itop-request-mgmt-itil.xml | 22 +-- .../datamodel.itop-request-mgmt.xml | 27 +-- .../en.dict.itop-request-mgmt.php | 2 + .../itop-tickets/datamodel.itop-tickets.xml | 49 ++++-- .../itop-tickets/UpdateChildTicketLogTest.php | 159 ++++++++++++++++++ 6 files changed, 202 insertions(+), 121 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml index 42f62d18a9..dfa5fe9793 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml +++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml @@ -1316,30 +1316,10 @@ LifecycleAction Get('public_log')->GetModifiedEntry('html'); - $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); - if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { - return true; // nothing to update + if (MetaModel::IsValidClass('UserRequest')) { + return parent::UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log', 'private_log']); } - $sChildEntryPublic = Dict::Format('Class:Incident:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; - $sChildEntryPrivate = Dict::Format('Class:Incident:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - - $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); - - while($oRequest = $oChildRequestSet->Fetch()) { - if ($sParentLogPublic != '') { - $oRequest->Set('public_log', $sChildEntryPublic); - } - if ($sParentLogPrivate != '') { - $oRequest->Set('private_log', $sChildEntryPrivate); - } - $oRequest->DBUpdate(); - } - return true; - + return true; }]]> @@ -1349,43 +1329,7 @@ LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT Incident WHERE parent_incident_id=:ticket"; - $oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oIncident = $oChildIncidentSet->Fetch()) - { - $oIncident->set('public_log',$sLogPublic); - $oIncident->DBUpdate(); - } - - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT Incident WHERE parent_incident_id=:ticket"; - $oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oIncident = $oChildIncidentSet->Fetch()) - { - $oIncident->set('private_log',$sLogPrivate); - $oIncident->DBUpdate(); - } - } - return true; - + return parent::UpdateChildTicketLog('Incident', 'parent_incident_id', ['public_log', 'private_log']); }]]> diff --git a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml index 4302036e72..2347202607 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml +++ b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml @@ -1452,27 +1452,7 @@ LifecycleAction Get('public_log')->GetModifiedEntry('html'); - $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); - if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { - return true; // nothing to update - } - $sChildEntryPublic = Dict::Format('Class:UserRequest:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; - $sChildEntryPrivate = Dict::Format('Class:UserRequest:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); - - while($oRequest = $oChildRequestSet->Fetch()) { - if ($sParentLogPublic != '') { - $oRequest->Set('public_log', $sChildEntryPublic); - } - if ($sParentLogPrivate != '') { - $oRequest->Set('private_log', $sChildEntryPrivate); - } - $oRequest->DBUpdate(); - } - return true; + return parent::UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log', 'private_log']); }]]> diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index 2fd7736364..9fbe0569a2 100755 --- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml +++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml @@ -1486,28 +1486,11 @@ public LifecycleAction Get('public_log')->GetModifiedEntry('html'); - $sParentEntryPrivate = $this->Get('private_log')->GetModifiedEntry('html'); - if (IsNullOrEmptyString($sParentEntryPublic) && IsNullOrEmptyString($sParentEntryPrivate)) { - return true; // nothing to update - } - $sChildEntryPublic = Dict::Format('Class:UserRequest:PublicLogFromParentPrefix', $this->Get('ref')).$sParentLogPublic; - $sChildEntryPrivate = Dict::Format('Class:UserRequest:PrivateLogFromParentPrefix', $this->Get('ref')).$sParentLogPrivate; - - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket' => $this->GetKey()]); - - while($oRequest = $oChildRequestSet->Fetch()) { - if ($sParentLogPublic != '') { - $oRequest->Set('public_log', $sChildEntryPublic); - } - if ($sParentLogPrivate != '') { - $oRequest->Set('private_log', $sChildEntryPrivate); - } - $oRequest->DBUpdate(); - } - return true; +{ + return $this->UpdateChildTicketLog('UserRequest', 'parent_request_id', + ['public_log' => 'public_log', + 'private_log' => 'private_log', + ]); }]]> diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php index 1aeaac0830..7f1a582488 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php @@ -41,6 +41,8 @@ 'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me', 'Class:Problem:KnownProblemList' => 'Known problems', 'Tickets:Related:OpenIncidents' => 'Open incidents', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %1$s:', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request %1$s:', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index 753c999165..917d243731 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -352,12 +352,26 @@ + 'private_log']) + * So in the example parent.public_log will be copied into each child.private_log + * with a prefix using a dictionary entry like 'Class:/Method:UpdateChildTicketWith:' with one placeholder %1$s for the parent ticket ref + * resulting in an entry like "Copy of public log entry from parent Incident I-000123: " in the child private_log + * + */]]> + false public LifecycleAction Get($sAttCode)->GetModifiedEntry('html'))) ) { - $aParentEntries[$sAttCode] = $this->Get($sAttCode)->GetModifiedEntry('html'); - $aChildEntries[$sAttCode] = Dict::Format("Class:$sParentClass:LogFromParentPrefix", $this->Get('ref')).$aParentEntries[$sAttCode]; - } - } - } - if ($aParentEntries == []) { - return true; // nothing to update - } + foreach ($aLogAttCodes as $sParentAttCode => $sChildAttCode) { + if (MetaModel::IsValidAttCode($sParentClass, $sParentAttCode) && MetaModel::GetAttributeDef($sParentClass, $sParentAttCode) instanceof AttributeCaseLog + && MetaModel::IsValidAttCode($sChildClass, $sChildAttCode) && MetaModel::GetAttributeDef($sChildClass, $sChildAttCode) instanceof AttributeCaseLog + && (!utils::IsNullOrEmptyString($this->Get($sParentAttCode)->GetModifiedEntry('html')))) { + $aChildEntries[$sChildAttCode] = Dict::Format('Class:'.$sParentClass.'/Method:UpdateChildTicketWith:'.$sParentAttCode, $this->Get('ref')).' '.$this->Get($sParentAttCode)->GetModifiedEntry('html'); + } + } + if ($aChildEntries == []) { + return true; // nothing to update + } - $sOQL = "SELECT :ticket_class WHERE :parent_code=:ticket"; - $oChildSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket_class' => $sChildClass, 'parent_code' => $sChildParentAttCode, 'ticket' => $this->GetKey()]); - while($oChild = $oChildSet->Fetch()) { + $sOQL = "SELECT $sChildClass WHERE $sChildParentAttCode = :ticket_id"; + $oChildSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket_id' => $this->GetKey()]); + while ($oChild = $oChildSet->Fetch()) { + if (is_object($oChild)) { // Seems that empty set, maybe in case of OQL syntax error, can return a single empty object instead of no object at all foreach ($aChildEntries as $sAttCode => $sEntry) { - $oChild->Set($sAttCode, $sEntry); + $oChild->Set($sAttCode, $sEntry); } $oChild->DBUpdate(); + } } return true; diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php new file mode 100644 index 0000000000..58e6bb9cd4 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php @@ -0,0 +1,159 @@ + +// + +namespace Combodo\iTop\Test\UnitTest\Module\iTopTickets; + +use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use ormCaseLog; +use MetaModel; + +class UpdateChildTicketLogTest extends ItopDataTestCase +{ + public function testUpdateChildTicketLog_PublicLogOnTwoChild(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(2); + $this->assertCount(2, $aChildrenTree[$iParentTicket], 'The test setup should create exactly two child tickets.'); + $sParentPublicLogEntry = 'This is a public log entry for the parent ticket.'; + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + + // When I enter a public_log entry for the parent ticket + $oParentTicket->Set('public_log', $sParentPublicLogEntry); + $oParentTicket->DBUpdate(); + + // Then the log should be copied to all descendants and contain parent references recursively + $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + } + + public function testUpdateChildTicketLog_PrivateAndPublicLog(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(3); + $sParentPublicLogEntry = 'This is a public log entry for the parent ticket.'; + $sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.'; + + // When I enter both a public_log and a private_log entry for the parent ticket + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $oParentTicket->Set('public_log', $sParentPublicLogEntry); + $oParentTicket->Set('private_log', $sParentPrivateLogEntry); + $oParentTicket->DBUpdate(); + + // Then both logs should be copied to all descendants and keep ancestor references recursively + $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + } + + public function testUpdateChildTicketLog_PrivateLogOnThreeLevels(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(1, 3); + $sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.'; + + // When I enter both a public_log and a private_log entry for the parent ticket + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $oParentTicket->Set('private_log', $sParentPrivateLogEntry); + $oParentTicket->DBUpdate(); + + // Then the private log should be copied on each level with parent + grand-parent references + $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + } + + private function GivenChildTicketsRecursive(int $iParentTicket, int $iCount, int $iRemainingLevels, int $iOrg, int &$iRefCounter): array + { + if ($iRemainingLevels <= 0) { + return []; + } + + $aChildren = []; + for ($i = 1; $i <= $iCount; $i++) { + $iRefCounter++; + $sRef = sprintf('R-%05d', $iRefCounter); + $iChildTicket = $this->GivenObjectInDB('UserRequest', [ + 'title' => "Child Ticket $sRef for Log Update Test", + 'description' => "This is child ticket $sRef for testing log updates.", + 'org_id' => $iOrg, + 'parent_request_id' => $iParentTicket, + 'ref' => $sRef, + ]); + + if ($iRemainingLevels > 1) { + $aChildren[$iChildTicket] = $this->GivenChildTicketsRecursive($iChildTicket, $iCount, $iRemainingLevels - 1, $iOrg, $iRefCounter); + } else { + $aChildren[] = $iChildTicket; + } + } + + return $aChildren; + } + + private function AssertLogContainsAncestorReferencesRecursively(int $iParentTicket, array $aChildrenTree, string $sLogAttCode, string $sExpectedEntry, array $aAncestorRefs = []): void + { + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $sParentRef = $oParentTicket->Get('ref'); + $aRefsToFind = array_merge($aAncestorRefs, [$sParentRef]); + + foreach ($aChildrenTree as $iChildOrIndex => $aChildNodeOrId) { + if (is_array($aChildNodeOrId)) { + $iChildTicket = (int) $iChildOrIndex; + $aGrandChildrenTree = $aChildNodeOrId; + } else { + $iChildTicket = (int) $aChildNodeOrId; + $aGrandChildrenTree = []; + } + + $oChildTicket = MetaModel::GetObject('UserRequest', $iChildTicket); + $sLastLogEntry = $oChildTicket->Get($sLogAttCode)->GetLatestEntry(); + $this->assertNotEmpty($sLastLogEntry, "The $sLogAttCode entry was not copied to child ticket #$iChildTicket."); + $this->assertStringContainsString($sExpectedEntry, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain the original parent entry."); + foreach ($aRefsToFind as $sExpectedRef) { + $this->assertStringContainsString($sExpectedRef, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain ancestor reference $sExpectedRef."); + } + + if ($aGrandChildrenTree !== []) { + $this->AssertLogContainsAncestorReferencesRecursively($iChildTicket, $aGrandChildrenTree, $sLogAttCode, $sExpectedEntry, $aRefsToFind); + } + } + } + /** + * @return array + * @throws \Exception + */ + private function GivenUserRequests(int $iCount, int $iLevel = 2): array + { + $iOrg = $this->GivenObjectInDB('Organization', [ + 'name' => 'Test Organization for Log Update', + ]); + // Given a parent ticket + $iParentTicket = $this->GivenObjectInDB('UserRequest', [ + 'title' => 'Parent Ticket for Log Update Test', + 'description' => 'This is the parent ticket for testing log updates.', + 'org_id' => $iOrg, + 'ref' => 'R-00001', + ]); + + $iRemainingLevels = max(0, $iLevel - 1); + $iRefCounter = 1; + $aChildrenTree = [ + $iParentTicket => $this->GivenChildTicketsRecursive($iParentTicket, $iCount, $iRemainingLevels, $iOrg, $iRefCounter), + ]; + + return [$iParentTicket, $aChildrenTree]; + } +} From d7ef9acf1aee57307f16f00554fb02b798cac527 Mon Sep 17 00:00:00 2001 From: v-dumas Date: Fri, 29 May 2026 17:18:41 +0200 Subject: [PATCH 3/7] =?UTF-8?q?N=C2=B09160=20-=20Refactor=20UpdateChildxxx?= =?UTF-8?q?xxLog=20and=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datamodel.itop-incident-mgmt-itil.xml | 4 ++-- .../datamodel.itop-request-mgmt-itil.xml | 12 +++++++++++- .../datamodel.itop-request-mgmt.xml | 5 +---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml index dfa5fe9793..663ba74ff9 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml +++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml @@ -1317,7 +1317,7 @@ UpdateChildTicketLog('UserRequest', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] ); } return true; }]]> @@ -1329,7 +1329,7 @@ LifecycleAction UpdateChildTicketLog('Incident', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log']); }]]> diff --git a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml index 2347202607..9169fe93eb 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml +++ b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml @@ -1452,8 +1452,17 @@ LifecycleAction UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log' ]); }]]> + + + false + public + LifecycleAction + UpdateChildTicketLog('Incident', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log']); + }]]> false @@ -1490,6 +1499,7 @@ parent::OnUpdate(); $this->Set('last_update', time()); $this->UpdateChildRequestLog(); + $this->UpdateChildIncidentLog(); }]]> diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index 9fbe0569a2..97025b5810 100755 --- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml +++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml @@ -1487,10 +1487,7 @@ LifecycleAction UpdateChildTicketLog('UserRequest', 'parent_request_id', - ['public_log' => 'public_log', - 'private_log' => 'private_log', - ]); + return $this->UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] ); }]]> From 6bd31230e1091729661611ed9a7b3551953c74e6 Mon Sep 17 00:00:00 2001 From: v-dumas Date: Mon, 1 Jun 2026 17:10:40 +0200 Subject: [PATCH 4/7] =?UTF-8?q?N=C2=B09160=20-=20Dictionary=20entries=20wi?= =?UTF-8?q?th=20italique=20and=20hyperlink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dictionaries/en.dict.itop-incident-mgmt-itil.php | 2 ++ .../dictionaries/en.dict.itop-request-mgmt-itil.php | 2 ++ .../dictionaries/en.dict.itop-request-mgmt.php | 4 ++-- datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php index 2966031a09..f0182d3bbb 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php @@ -44,6 +44,8 @@ 'UI-IncidentManagementOverview-OpenIncidentByStatus' => 'Open incidents by status', 'UI-IncidentManagementOverview-OpenIncidentByAgent' => 'Open incidents by agent', 'UI-IncidentManagementOverview-OpenIncidentByCustomer' => 'Open incidents by customer', + 'Class:Incident/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent Incident %2$s:

', + 'Class:Incident/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent Incident [[Incident:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php index a2d2fe1861..2db410ba9e 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php @@ -37,6 +37,8 @@ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Open requests by customer', 'Class:UserRequest:KnownErrorList' => 'Known Errors', 'Class:UserRequest:KnownErrorList+' => 'Known Errors related to Functional CI linked to the current ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php index 7f1a582488..7e610a9bdd 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php @@ -41,8 +41,8 @@ 'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me', 'Class:Problem:KnownProblemList' => 'Known problems', 'Tickets:Related:OpenIncidents' => 'Open incidents', - 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %1$s:', - 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request %1$s:', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index 917d243731..e573bffbd3 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -387,7 +387,7 @@ if (MetaModel::IsValidAttCode($sParentClass, $sParentAttCode) && MetaModel::GetAttributeDef($sParentClass, $sParentAttCode) instanceof AttributeCaseLog && MetaModel::IsValidAttCode($sChildClass, $sChildAttCode) && MetaModel::GetAttributeDef($sChildClass, $sChildAttCode) instanceof AttributeCaseLog && (!utils::IsNullOrEmptyString($this->Get($sParentAttCode)->GetModifiedEntry('html')))) { - $aChildEntries[$sChildAttCode] = Dict::Format('Class:'.$sParentClass.'/Method:UpdateChildTicketWith:'.$sParentAttCode, $this->Get('ref')).' '.$this->Get($sParentAttCode)->GetModifiedEntry('html'); + $aChildEntries[$sChildAttCode] = Dict::Format('Class:'.$sParentClass.'/Method:UpdateChildTicketWith:'.$sParentAttCode, $this->GetKey(), $this->Get('ref')).$this->Get($sParentAttCode)->GetModifiedEntry('html'); } } if ($aChildEntries == []) { From 86b401591fe52cdd14899e9bf44fd951ee9536c9 Mon Sep 17 00:00:00 2001 From: v-dumas Date: Mon, 1 Jun 2026 17:26:43 +0200 Subject: [PATCH 5/7] =?UTF-8?q?N=C2=B09160=20-=20Fix=20test=20to=20search?= =?UTF-8?q?=20for=20parent=20ticket=20ref=20OR=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itop-tickets/UpdateChildTicketLogTest.php | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php index 58e6bb9cd4..5781d0711b 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php @@ -39,7 +39,7 @@ public function testUpdateChildTicketLog_PublicLogOnTwoChild(): void $oParentTicket->DBUpdate(); // Then the log should be copied to all descendants and contain parent references recursively - $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); } public function testUpdateChildTicketLog_PrivateAndPublicLog(): void @@ -56,14 +56,14 @@ public function testUpdateChildTicketLog_PrivateAndPublicLog(): void $oParentTicket->DBUpdate(); // Then both logs should be copied to all descendants and keep ancestor references recursively - $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); - $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); } - public function testUpdateChildTicketLog_PrivateLogOnThreeLevels(): void + public function testUpdateChildTicketLog_PrivateLogOnMultipleLevels(): void { //Given a parent ticket with two child ticket - list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(1, 3); + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(1, 4); $sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.'; // When I enter both a public_log and a private_log entry for the parent ticket @@ -71,43 +71,17 @@ public function testUpdateChildTicketLog_PrivateLogOnThreeLevels(): void $oParentTicket->Set('private_log', $sParentPrivateLogEntry); $oParentTicket->DBUpdate(); - // Then the private log should be copied on each level with parent + grand-parent references - $this->AssertLogContainsAncestorReferencesRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + // Then the private log should be copied on each level with parent + grand-parent +... references + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); } - private function GivenChildTicketsRecursive(int $iParentTicket, int $iCount, int $iRemainingLevels, int $iOrg, int &$iRefCounter): array - { - if ($iRemainingLevels <= 0) { - return []; - } - - $aChildren = []; - for ($i = 1; $i <= $iCount; $i++) { - $iRefCounter++; - $sRef = sprintf('R-%05d', $iRefCounter); - $iChildTicket = $this->GivenObjectInDB('UserRequest', [ - 'title' => "Child Ticket $sRef for Log Update Test", - 'description' => "This is child ticket $sRef for testing log updates.", - 'org_id' => $iOrg, - 'parent_request_id' => $iParentTicket, - 'ref' => $sRef, - ]); - - if ($iRemainingLevels > 1) { - $aChildren[$iChildTicket] = $this->GivenChildTicketsRecursive($iChildTicket, $iCount, $iRemainingLevels - 1, $iOrg, $iRefCounter); - } else { - $aChildren[] = $iChildTicket; - } - } - - return $aChildren; - } - - private function AssertLogContainsAncestorReferencesRecursively(int $iParentTicket, array $aChildrenTree, string $sLogAttCode, string $sExpectedEntry, array $aAncestorRefs = []): void + private function AssertLogContainsParentsRefOrKeyRecursively(int $iParentTicket, array $aChildrenTree, string $sLogAttCode, string $sExpectedEntry, array $aAncestors = []): void { $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); - $sParentRef = $oParentTicket->Get('ref'); - $aRefsToFind = array_merge($aAncestorRefs, [$sParentRef]); + $aAncestorsToFind = array_merge($aAncestors, [[ + 'id' => (string) $iParentTicket, + 'ref' => (string) $oParentTicket->Get('ref'), + ]]); foreach ($aChildrenTree as $iChildOrIndex => $aChildNodeOrId) { if (is_array($aChildNodeOrId)) { @@ -122,12 +96,17 @@ private function AssertLogContainsAncestorReferencesRecursively(int $iParentTick $sLastLogEntry = $oChildTicket->Get($sLogAttCode)->GetLatestEntry(); $this->assertNotEmpty($sLastLogEntry, "The $sLogAttCode entry was not copied to child ticket #$iChildTicket."); $this->assertStringContainsString($sExpectedEntry, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain the original parent entry."); - foreach ($aRefsToFind as $sExpectedRef) { - $this->assertStringContainsString($sExpectedRef, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain ancestor reference $sExpectedRef."); + foreach ($aAncestorsToFind as $aAncestor) { + $bContainsAncestorRef = strpos($sLastLogEntry, $aAncestor['ref']) !== false; + $bContainsAncestorId = strpos($sLastLogEntry, $aAncestor['id']) !== false; + $this->assertTrue( + $bContainsAncestorRef || $bContainsAncestorId, + "The $sLogAttCode entry on child ticket #$iChildTicket does not contain ancestor ref '{$aAncestor['ref']}' nor ancestor id '{$aAncestor['id']}'." + ); } if ($aGrandChildrenTree !== []) { - $this->AssertLogContainsAncestorReferencesRecursively($iChildTicket, $aGrandChildrenTree, $sLogAttCode, $sExpectedEntry, $aRefsToFind); + $this->AssertLogContainsParentsRefOrKeyRecursively($iChildTicket, $aGrandChildrenTree, $sLogAttCode, $sExpectedEntry, $aAncestorsToFind); } } } @@ -156,4 +135,32 @@ private function GivenUserRequests(int $iCount, int $iLevel = 2): array return [$iParentTicket, $aChildrenTree]; } + + private function GivenChildTicketsRecursive(int $iParentTicket, int $iCount, int $iRemainingLevels, int $iOrg, int &$iRefCounter): array + { + if ($iRemainingLevels <= 0) { + return []; + } + + $aChildren = []; + for ($i = 1; $i <= $iCount; $i++) { + $iRefCounter++; + $sRef = sprintf('R-%05d', $iRefCounter); + $iChildTicket = $this->GivenObjectInDB('UserRequest', [ + 'title' => "Child Ticket $sRef for Log Update Test", + 'description' => "This is child ticket $sRef for testing log updates.", + 'org_id' => $iOrg, + 'parent_request_id' => $iParentTicket, + 'ref' => $sRef, + ]); + + if ($iRemainingLevels > 1) { + $aChildren[$iChildTicket] = $this->GivenChildTicketsRecursive($iChildTicket, $iCount, $iRemainingLevels - 1, $iOrg, $iRefCounter); + } else { + $aChildren[] = $iChildTicket; + } + } + + return $aChildren; + } } From df4e9ba246811519947139fd3df7a5c949805e68 Mon Sep 17 00:00:00 2001 From: Vincent Dumas <42336698+v-dumas@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:01:27 +0200 Subject: [PATCH 6/7] Fix class used to control attcode existance Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index e573bffbd3..99ee4c78e8 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -375,7 +375,7 @@ ErrorLog::Debug("Invalid class ($sChildClass) or attribute code ($sChildParentAttCode) provided to UpdateChildTicketLog","DataModel"); return true; // Do nothing } - $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sChildParentAttCode); + $oAttDef = MetaModel::GetAttributeDef($sChildClass, $sChildParentAttCode); if (!$oAttDef instanceof AttributeExternalKey || ($oAttDef->GetTargetClass() !== get_class($this))) { ErrorLog::Debug("Attribute $sChildParentAttCode should be an external key of class $sChildClass, pointing to class ".get_class($this),"DataModel"); return true; // Do nothing From 5355c71a2c29d8de71c4b43ff825b1f9aa60755a Mon Sep 17 00:00:00 2001 From: v-dumas Date: Mon, 1 Jun 2026 18:31:21 +0200 Subject: [PATCH 7/7] =?UTF-8?q?N=C2=B09160=20-=20Add=20missing=20dictionar?= =?UTF-8?q?y=20entries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dictionaries/en.dict.itop-incident-mgmt-itil.php | 4 ++++ .../dictionaries/fr.dict.itop-incident-mgmt-itil.php | 10 +++++++--- .../dictionaries/en.dict.itop-request-mgmt-itil.php | 4 ++-- .../dictionaries/fr.dict.itop-request-mgmt-itil.php | 2 ++ .../dictionaries/en.dict.itop-request-mgmt.php | 4 ++-- .../dictionaries/fr.dict.itop-request-mgmt.php | 2 ++ 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php index f0182d3bbb..a5383abb92 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php @@ -195,6 +195,10 @@ 'Class:Incident/Attribute:parent_incident_id+' => '', 'Class:Incident/Attribute:parent_incident_ref' => 'Parent incident ref', 'Class:Incident/Attribute:parent_incident_ref+' => '', + 'Class:Incident/Attribute:parent_request_id' => 'Parent request', + 'Class:Incident/Attribute:parent_request_id+' => '', + 'Class:Incident/Attribute:parent_request_ref' => 'Parent request ref', + 'Class:Incident/Attribute:parent_request_ref+' => '', 'Class:Incident/Attribute:parent_change_id' => 'Parent change', 'Class:Incident/Attribute:parent_change_id+' => '', 'Class:Incident/Attribute:parent_change_ref' => 'Parent change ref', diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php index c7672c7027..7b329d4a2b 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php @@ -179,15 +179,19 @@ 'Class:Incident/Attribute:pending_reason+' => '', 'Class:Incident/Attribute:parent_incident_id' => 'Incident parent', 'Class:Incident/Attribute:parent_incident_id+' => '', - 'Class:Incident/Attribute:parent_incident_ref' => 'Référence incident parent', + 'Class:Incident/Attribute:parent_incident_ref' => 'Réf. incident parent', 'Class:Incident/Attribute:parent_incident_ref+' => '', + 'Class:Incident/Attribute:parent_request_id' => 'Demande parente', + 'Class:Incident/Attribute:parent_request_id+' => '', + 'Class:Incident/Attribute:parent_request_ref' => 'Réf. demande parente', + 'Class:Incident/Attribute:parent_request_ref+' => '', 'Class:Incident/Attribute:parent_change_id' => 'Changement parent', 'Class:Incident/Attribute:parent_change_id+' => '', - 'Class:Incident/Attribute:parent_change_ref' => 'Ref Changement parent', + 'Class:Incident/Attribute:parent_change_ref' => 'Réf. changement parent', 'Class:Incident/Attribute:parent_change_ref+' => '', 'Class:Incident/Attribute:parent_problem_id' => 'Problème lié', 'Class:Incident/Attribute:parent_problem_id+' => '', - 'Class:Incident/Attribute:parent_problem_ref' => 'Référence problème lié', + 'Class:Incident/Attribute:parent_problem_ref' => 'Réf. problème lié', 'Class:Incident/Attribute:parent_problem_ref+' => '', 'Class:Incident/Attribute:related_request_list' => 'Requêtes filles', 'Class:Incident/Attribute:related_request_list+' => '', diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php index 2db410ba9e..4c7c504fd0 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php @@ -37,8 +37,8 @@ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Open requests by customer', 'Class:UserRequest:KnownErrorList' => 'Known Errors', 'Class:UserRequest:KnownErrorList+' => 'Known Errors related to Functional CI linked to the current ticket', - 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %2$s:

', - 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request [[UserRequest:%1$s]]:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log automatic copy from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log automatic copy from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php index b61397a8fd..ec07842c85 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php @@ -42,6 +42,8 @@ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par client', 'Class:UserRequest:KnownErrorList' => 'Erreurs connues', 'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Copie automatique du log public de la demande parente %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php index 7e610a9bdd..5e8f40bacb 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php @@ -41,8 +41,8 @@ 'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me', 'Class:Problem:KnownProblemList' => 'Known problems', 'Tickets:Related:OpenIncidents' => 'Open incidents', - 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent User Request %2$s:

', - 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent User Request [[UserRequest:%1$s]]:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log automatic copy from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log automatic copy from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php index 802d264f02..5e9bc7a2ef 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php @@ -42,6 +42,8 @@ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par organisation', 'Class:UserRequest:KnownErrorList' => 'Erreurs connues', 'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Copie automatique du log public de la demande parente %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:

', 'Menu:UserRequest:MyWorkOrders' => 'Tâches qui me sont assignées', 'Menu:UserRequest:MyWorkOrders+' => '', 'Class:Problem:KnownProblemList' => 'Problèmes connus',