OCL, Python, Renderer und List Controller rund um die Ressourcenplanung
Produktlinie
Standard
|Expert
Betriebsart
CLOUD ABO
|ON-PREMISES
Module
Leistung & CRM
Budget & Teilprojekt
Fremdkosten
Ressourcenplanung
Business Intelligence
Für die Verwendung in Zeit- und Pivottabellen der Ressourcenplanung gibt es folgende mitgelieferte Custom Renderer für die Listeneinstellungen :
Custom Renderer | Wert |
---|---|
vtcplanning.PlannedMinutesRenderer |
Eingabe (und Anzeige) der Planwerte in den einzelnen Zellen |
vtcplanning.NetCapacityRenderer |
Netto-Verfügbarkeit von Bearbeitern |
vtcplanning.CustomNetCapacityRenderer |
Ab Vertec 6.7.0.5. Netto-Verfügbarkeit von Bearbeitern unter Berücksichtigung der Systemeinstellung Angepasste Nettokapazität (Prozent) . |
vtcplanning.GrossCapacityRenderer |
Brutto-Verfügbarkeit von Bearbeitern |
vtcplanning.RemainingCapacityRenderer |
Rest-Verfügbarkeit von Bearbeitern |
vtcplanning.CustomRemainingCapacityRenderer |
Ab Vertec 6.7.0.5. Rest-Verfügbarkeit von Bearbeitern unter Berücksichtigung der Systemeinstellung Angepasste Nettokapazität (Prozent) . |
All diese Renderer können auf zwei Arten verwendet werden:
Dynamisch bedeutet, dass die Spalte für jeden Eintrag nach rechts wiederholt wird, also in Zeittabellen pro Intervall, in Pivottabellen pro Bearbeiter, Projekt oder Phase.
In diesem Fall zeigt die Zelle den Wert an, welcher zur Kombination Zeile/Spalte passt.
%col%
Ist die Spalte nicht als dynamisch markiert, wird sie nur einmal pro Liste angezeigt.
Der angezeigte Wert bezieht sich also auf die ganze Liste und zeigt den summierten Wert über alle Spalten.
Dynamisch: Nein
Diese Renderer basieren auf den im Modul vtcplanning mitgelieferten List Controllern und können darum innerhalb der Ressourcenplanung in den Listen einfach eingefügt werden.
Um sie auch ausserhalb der Ressourcenplanung zu verwenden, muss von einem dieser Controller geerbt und gewisse Methoden implementiert werden. Informationen dazu finden Sie im Abschnitt Ausserhalb der Ressourcenplanung .
Für die Ressourcenplanung steht die Methode setresourceplanvalue zur Verfügung, mit welcher Ressourcenplanwerte via Python Code gesetzt werden können:
setresourceplanvalue(project_or_phase, date, value)
project / phase | Je nach eingestellter Planungsebene muss hier ein Projekt oder eine Projektphase angegeben werden. |
date | Es wird diejenige Periode mit dem Value gefüllt, in welcher sich das Datum befindet. |
value | Wert in Minuten. |
bearb = vtcapp.currentlogin() projekt = argobject bearb.setresourceplanvalue(projekt, vtcapp.strtodate('15.05.2023'), 180)
setresourceplanvalue(worker, date, value)
worker | Als worker wird ein Planungsbearbeiter oder ein Projektbearbeiter übergeben. |
date | Es wird diejenige Periode mit dem Value gefüllt, in welcher sich das Datum befindet. |
value | Wert in Minuten. |
bearb = vtcapp.currentlogin() projekt = argobject projekt.setresourceplanvalue(bearb, vtcapp.strtodate('15.05.2023'), 180)
Die bereits in Versionen vor 6.6 verfügbare Methode auf
vtcapp
:
vtcapp.setresourceplanvalue(bearbeiter, projekt, phase, date, intervalType, value)
funktioniert weiterhin, der intervalType wird jedoch nicht mehr berücksichtigt (muss jedoch trotzdem angegeben werden).
Die speziell verfügbaren OCL Variablen stehen unter anderem auch in der Ressourcenplanung zur Verfügung. Hier wichtig sind vor allem die folgenden:
varStartDate | Das Start-Datum der Ressourcenplanungsansicht
:
|
varEndDate | Das End-Datum der Ressourcenplanungsansicht
:
|
varContext |
Enthält den aktuell im Baum ausgewählten Eintrag (siehe Bild unten). Die Variable ermöglicht in der Ressourcenplanung die Anpassung von Listen in Abhängigkeit davon, ob eine Ressourcenplanungsansicht für Einzelobjekte oder Listen angezeigt wird. |
varParent | Enthält das Objekt, welchem der im Baum ausgewählte Container gehört (siehe Bild unten). Auf Einzelobjekten ist diese Variable leer. |
In Listenspalten von Ressourcenplanungstabellen sowie in darin enthaltenen List Controllern und Custom Renderern kann auf diese Variablen zugegriffen werden, da in der self.evalocl() Methode ein spezifischer OCL Evaluator angewendet wird, in welchem diese Variablen verfügbar sind.
Im Gegensatz zu den sonstigen Listen in Vertec, wo auf List Controller aus den Spalten heraus zugegriffen werden kann, werden die Darstellungen in der Ressourcenplanung von den List Controllern direkt berechnet.
Bei Verwendung von List Controllern in der Ressourcenplanung stehen zusätzlich folgende Methoden zur Verfügung:
initialize_with_period(self, start, end, subscriber) |
Spezielle Variante von initialize für die Ressourcenplanung, welche zusätzlich Start und Ende der gesamten Periode erhält und den ResourcePlanningProvider initialisiert. |
get_row_objects(self, subscriber) |
Berechnet die Zeilenobjekte, die in der Liste dargestellt werden. Für Ressourcenplanungsansichten werden so alle Zeilen bestimmt, für die ein Planwert eingetragen ist. |
get_row_objects_type(self) |
Berechnet den Typ der Zeilenobjekte. Falls dieser konstant ist, kann er auch als Attribut Nur in Kombination mit |
add_row_object(self, obj) |
Wird verwendet für die Sternzeilen-Funktionalität, um weitere zu planende Einträge in die Liste einzufügen. Diese Methode muss vorhanden sein, damit bei Listen mit List Controller und eingeschalteter Option "Hinzufügen-Zeile anzeigen" eine Sternzeile angezeigt wird. Nur für Ressourcenplanungsansichten unterstützt. |
Sämtlichen Ressourcenplanungsansichten und Auslastungsdimensionen steht ein List Controller zugrunde. Diese werden im Modul vtcplanning mitgeliefert.
Um Tabellen oder Grafiken in der Ressourcenplanung zu erstellen oder zu erweitern, kann von diesen Controllern geerbt werden. Damit stehen je nach Controller noch weitere Methoden zur Verfügung, siehe Beispiel Erben von einem bestehenden List Controller .
Aus Gründen der Performance raten wir davon ab, Ist-Werte direkt in Ressourcentabellen anzuzeigen, wo auch geplant wird, insbesondere nicht für jedes Intervall.
Für Auswertungen mit Ressourcenplanwerten gibt es verschiedene Möglichkeiten.
Wir raten, Auswertungs-Spalten nicht direkt in Ressourcentabellen anzuzeigen, in welchen auch geplant wird, um diese Listen jederzeit performant zu halten.
Es lassen sich aber leicht weitere Ressourcenplanungsansichten erstellen, welche zu Auswertungszwecken angezeigt werden können. Dafür kann vom List Controller der Tabelle, welche man erweitern möchte, geerbt werden.
Zuerst wird der Name des List Controllers eruiert, von welchem man erben möchte. Im Beispiel möchten wir eine Zeittabelle mit weiteren Werten ausstatten.
Unter Einstellungen > Ressourcenplanung > Ressourcenplanungsansichten
suche ich die Tabelle heraus, von der ich erben möchte. Hier zum Beispiel die Projekte - Bearbeiter Zeittabelle:
Im Feld List Controller ist der Controller angegeben, von welchem wir erben möchten: vtcplanning.SingeObjectTimeTableController
. Über den Button mit den drei Punkten kann der entsprechende Code eingesehen werden. Eine Liste aller mitgelieferten Controller finden Sie in der Beschreibung des Moduls "vtcplanning"
.
Nun erstellen wir einen Scripteintrag für unseren List Controller und erben vom oben genannten Controller:
import vtcplanning class Controller(vtcplanning.SingleObjectTimeTableController): pass
Damit haben wir nun einen vollwertigen Controller, der dasselbe macht und die gleichen Renderer zur Verfügung stellt wie der originale.
Diesen können wir bereits als List Controller in unserer Ansicht eintragen. Dafür erstellen wir eine neue Ressourcenplanungsansicht und fügen ihn hinzu, in unserem Beispiel:
Zum Test kann die Tabelle in der Ressourcenplanung nun angezeigt werden. Sie sollte genau gleich aussehen wie die, von welcher wir ausgingen.
Hier stehen uns nun alle Methoden und Attribute eines List Controllers sowie der ResourcePlanningProvider zur Verfügung.
Nun erweitern wir unseren Controller um die um die gewünschten Auswertungswerte. In unserem Beispiel möchten wir folgende zusätzliche Spalten anzeigen:
Die Werte sollen vorberechnet werden, also nicht in jeder Zelle einzeln evaluiert werden. Dafür erweitern wir die initialize_with_period
Methode unseres Controllers und berechnen die Werte vor.
Es gilt: Sobald eine Methode überschrieben wird, wird die originale Methode nicht mehr ausgeführt. Möchte man also eine Methode erweitern, muss das, was die originale Methode macht, nochmal ausgelöst werden. Dafür rufen wir sie zuerst nochmal auf (einfach 1:1 kopieren):
import vtcplanning class Controller(vtcplanning.SingleObjectTimeTableController): def initialize_with_period(self, start, end, subscriber): vtcplanning.SingleObjectTimeTableController.initialize_with_period(self, start, end, subscriber)
Nun sind wir gleich weit und können unsere Werte vorberechnen.
Zu beachten ist folgendes: Die mitgelieferten Controller wie der vtcplanning.SingleObjectTimeTableController
sind allgemein gehalten, können also auf Bearbeitern, Projekten und Projektphasen gleichermassen verwendet werden. Möchten wir unseren Controller ebenso erweitern, müssen wir darauf achten, dass die Expressions, die wir verwenden, für alle Fälle verwendbar sind.
Hier im Beispiel gehen wir von der Planungsebene Projekt aus. Wir decken also Projekte und Bearbeiter ab. Unsere Ist-Werte evaluieren wir mittels Leistungssummen .
import vtcplanning class Controller(vtcplanning.SingleObjectTimeTableController): def initialize_with_period(self, start, end, subscriber): vtcplanning.SingleObjectTimeTableController.initialize_with_period(self, start, end, subscriber) # Calculation of the leistsum objects. Consider the different Object types obj = self.context if obj.istypeof("Projekt"): self.leistSumList = obj.evalocl("self->groupleistungenP('{}','{}','MONTH, BEARBEITER')".format(vtcapp.datetostrgerman(start), vtcapp.datetostrgerman(end))) self.oclMember = "bearbeiter" if obj.istypeof("Projektbearbeiter"): self.leistSumList = obj.evalocl("self->groupleistungenB('{}','{}','MONTH, PROJEKT')".format(vtcapp.datetostrgerman(start), vtcapp.datetostrgerman(end))) self.oclMember = "projekt"
Nun haben wir eine Liste der Werte über die gesamte Periode, gruppiert nach Intervall (im Beispiel Monat).
Damit sind die Werte vorberechnet und im Controller verfügbar. Nun brauchen wir einen Custom Renderer
für die Ist-Spalte. Dafür erben wir vom vtcplanning.MinutesRendererBase
, um dessen Methode get_column_index_from_expression
anwenden zu können.
Wir evaluieren das Start-Datum der aktuellen Spalte und filtern dann die im Controller vorgeladene Liste von Leistungssummen nach Datum sowie nach dem aktuellen Objekt:
class AufwandExtRenderer(vtcplanning.MinutesRendererBase): is_readonly = True # Summiert MinutenExt für den Zeitraum def get_value(self, rowobj, expression, subscriber): column_index = self.get_column_index_from_expression(expression) start_date = vtcapp.datetostr(self.controller.get_date_by_column_index(column_index)) return self.controller.leistSumList.evalocl("self->select(datum.asstring = '{}')->select({}.objid={})->collect(minutenextoffen+minutenextverrechnet)->sum".format(start_date, self.controller.oclMember, rowobj.objid),subscriber)
Nun können wir die Ist-Spalte in die Liste einfügen:
Nun möchten wir noch den Anteil der bereits geleisteten Zeit an der geplanten Zeit ausgeben. Dafür erstellen wir einen neuen Renderer, in welchem wir sowohl auf die vorgeladene Liste von Leistungssummen als auch auf die geplanten Werte zugreifen.
class AufwandAnteilRenderer(vtcplanning.MinutesRendererBase): is_readonly = True converter=None value_type="float" # Berechnet den Anteil der geleisteten an den geplanten Zeiten def get_value(self, rowobj, expression, subscriber): column_index = self.get_column_index_from_expression(expression) start_date = vtcapp.datetostr(self.controller.get_date_by_column_index(column_index)) planwert = self.controller.get_planned_minutes(column_index, rowobj, subscriber) aufwandext = self.controller.leistSumList.evalocl("self->select(datum.asstring = '{}')->select({}.objid={})->collect(minutenextoffen+minutenextverrechnet)->sum".format(start_date, self.controller.oclMember, rowobj.objid),subscriber) if planwert: return float(aufwandext)/float(planwert)
Solange die Werte in der Liste pro Zeile, Spalte und Intervall berechnet werden, braucht es keinen speziellen Provider. Dann reichen die Zugriffe wie im vorherigen Abschnitt gezeigt.
Möchte man aber in derselben Liste Daten anzeigen, die z.B. über den Zeitraum hinausgehen oder sonst Werte miteinbeziehen sollen, welche in der Liste nicht dargestellt werden, braucht es dafür einen eigenen Provider.
In unserem Beispiel möchten wir die obige Tabelle erweitern und pro Zeile das Total der geplanten Werte anzeigen, unabhängig vom ausgewählten Intervall oder der angezeigten Periode, sondern insgesamt.
Dafür erstellen wir nun einen separaten Provider. Zuerst rufen wir die setup_provider
Methode unseres Controllers auf:
import vtcplanning import vtcplanningcore import datetime class Controller(vtcplanning.SingleObjectTimeTableController): def setup_provider(self, subscriber): vtcplanning.SingleObjectTimeTableController.setup_provider(self, subscriber) # Zusätzlichen Provider erstellen source_entry = self.get_source_entry() source_entries = vtcplanning.get_as_list(source_entry) self.total_start = datetime.date(2000, 1, 1) self.total_end = datetime.datetime(2100, 1, 1) self.total_provider = vtcplanningcore.ResourcePlanningProvider(source_entries, self.total_start, self.total_end)
Damit haben wir nun zwei Provider:
Nun erstellen wir einen weiteren Custom Renderer für die Anzeige des Totalwerts in der Liste. Darin können wir nun auf den zusätzlichen Provider zugreifen:
class TotalGeplantRenderer(vtcplanning.MinutesRendererBase): is_readonly = True # Summiert die Planzeiten ohne Einschränkung des Zeitraums def get_value(self, rowobj, expression, subscriber): return self.controller.total_provider.get_planned_minutes_aggregated( self.controller.context, rowobj, self.controller.total_start, self.controller.total_end, subscriber)
Nun können wir die Totalspalte in der Liste einfügen:
Ausserhalb der Ressourcenplanung, also in den normalen Vertec Listen, müssen wir den List Controller selber erstellen und via ResourcePlanningProvider auf die Plandaten zugreifen.
Als erstes Beispiel möchten wir in einer Projektliste pro Projekt anzeigen, wie viel Zeit gesamthaft darauf geplant wurde.
Dafür erstellen wir folgenden List Controller :
import vtcplanningcore import datetime class Controller: def initialize(self, subscriber): self.startdatum = datetime.date(2000, 1, 1) self.enddatum = datetime.datetime(2100, 1, 1) source_entries = self.get_context_entries() self.rscprovider = vtcplanningcore.ResourcePlanningProvider(source_entries, self.startdatum, self.enddatum)
Diesen Controller können wir nun auf unserer Projektliste als List Controller
hinterlegen:
Nun benötigen wir für die Spalte wiederum einen Custom Renderer, in welchem wir auf den oben erstellten Provider zugreifen:
class ProjektPlanwertRenderer: value_type = "integer" converter = "minutes" def get_value(self, rowobj, expression, subscriber): return self.controller.rscprovider.get_planned_minutes_aggregated(rowobj, None, self.controller.startdatum, self.controller.enddatum, subscriber)
Diesen fügen wir dann in den Listeneinstellungen unserer Projektliste als Renderer in der Spalte ein:
Es gibt folgende OCL Methoden, welche für Projekt- und Planungsbearbeiter verwendet werden können:
.getResPlanMinuten(von, bis) |
Gibt die verplante Zeit für den Bearbeiter für die angegebene Periode (von, bis Datum) in Minuten zurück. Macht dasselbe wie der ResourcePlanningProvider.get_planned_minutes (siehe vorherigen Abschnitt). |
.getResRsrcMinuten(von, bis) |
Gibt die Ressourcenzeit für den Bearbeiter für die angegebene Periode (von, bis Datum) in Minuten zurück. Das ist nicht die noch verfügbare Zeit, sondern die gesamte Ressourcenplan-relevante Sollzeit. Der Unterschied zur Sollzeit ist, dass nur Abwesenheiten vom Typ "Frei" die Sollzeit beeinflussen, von der Ressourcenzeit hingegen werden alle Arten von Abwesenheiten (Frei, Ferien, Kompensation) abgezogen (siehe dazu auch die Tabelle über die Arbeitszeit-Ausnahmen im Artikel Abwesenheiten). Macht dasselbe wie der ResourcePlanningProvider.get_net_capacity_minutes (siehe vorherigen Abschnitt). |
Dann kann noch die Ist-Zeit für die gleiche Periode angezeigt und damit gearbeitet werden. Beispiel:
Das Beispiel zeigt einen SQL-Ordner für die Klasse Projektbearbeiter. Als Abfrage gibt es zwei Datumsfelder Von, Bis.
In der Liste kann auf diese Datumsfelder zugegriffen werden (varVon
, varBis
). Die einzelnen Expressions lauten demnach:
self.getResRsrcMinuten(varVon, varBis)
self.getResPlanMinuten(varVon, varBis)
self.getArbeitszeit(varVon, varBis)
self.getResPlanMinuten(varVon, varBis)-self.getArbeitszeit(varVon, varBis)
Hinweis: Dies ist nur ein Anschauungsbeispiel. Aus Performancegründen sollten nicht alle Projektbearbeiter in der Liste dargestellt, sondern eine gefilterte Liste angezeigt werden. Aber hier ist gut ersichtlich, warum diese Liste nicht performant ist: Alle Werte müssen für jeden Bearbeiter separat geladen und nach Datum gefiltert werden, z.T. sogar mehrmals.
Empfehlenswerter ist auf jeden Fall die Variante über den ResourcePlanningProvider, da dort erstens mehr Werte zur Verfügung stehen (z.B. auch für Projekte oder Phasen) und ausserdem von einem besseren Caching profitiert werden kann. Insbesondere Plandaten, also die ResourceLinks direkt, müssen aus Performancegründen immer über den ResourcePlanningProvider abgefragt werden.
Für denselben SQL-Ordner braucht es dafür folgenden Controller:
import vtcplanning class Controller(vtcplanning.TimeTableControllerBase): def initialize(self, subscriber): start = self.evalocl("varVon") end = self.evalocl("varBis") vtcplanning.TimeTableControllerBase.initialize_with_period(self, start, end, subscriber) def setup_provider(self, subscriber): source_entries = self.get_context_entries() self.create_and_set_provider(source_entries, self.period_start, self.period_end, subscriber) def get_capacity_method_arguments(self, column_index, rowobj, subscriber): return (rowobj, self.period_start, self.period_end) def get_planned_minutes_aggregated(self, rowobj, subscriber): return self.get_provider().get_planned_minutes_aggregated(rowobj, None, self.period_start, self.period_end, subscriber)
Der Controller erbt vom vtcplanning.TimeTableControllerBase, da wir die normalen Renderer benutzen möchten, ohne alles nochmal selbst berechnen zu müssen.
Ist dieser auf dem SQL-Ordner als List Controller hinterlegt, können in den Spalten danach die gewohnten Custom Renderer der Ressourcenplanung verwendet werden:
vtcplanning.NetCapacityRenderer
vtcplanning.CustomNetCapacityRenderer
vtcplanning.PlannedMinutesRenderer
vtcplanning.RemainingCapacityRenderer
vtcplanning.CustomRemainingCapacityRenderer
Auch in diesem Beispiel verwenden wir einen SQL-Ordner mit Bearbeitern. Diesmal übergeben wir nur ein Datum, welches als Startdatum dient. In der Liste zeigen wir sowohl die noch verfügbare Kapazität der einzelnen Bearbeiter pro Intervall als auch gesamthaft an.
Dafür braucht es folgenden Controller:
import vtcplanning class Controller(vtcplanning.TimeTableControllerBase): def initialize(self, subscriber): date = self.evalocl("varDate") start = vtcapp.firstdayofmonth(date) end = vtcapp.incmonth(start, 5) vtcplanning.TimeTableControllerBase.initialize_with_period(self, start, end, subscriber) def setup_provider(self, subscriber): source_entries = self.get_context_entries() self.create_and_set_provider(source_entries, self.period_start, self.period_end, subscriber) def get_capacity_method_arguments(self, column_index, rowobj, subscriber): if column_index is None: date_from = self.period_start date_to = self.period_end else: date_from = self.get_date_by_column_index(column_index) date_to = None return (rowobj, date_from, date_to)
get_context_entries()
.get_capacity_method_arguments
implementiert sein. Ich kopiere einfach den Code aus der Originalfunktion.Nun können die gewohnten Renderer in der Liste verwendet werden:
vtcplanning.RemainingCapacityRenderer
oder vtcplanning.CustomRemainingCapacityRenderer
.vtcplanning.RemainingCapacityRenderer
oder vtcplanning.CustomRemainingCapacityRenderer
, zusätzlich Spalte als Dynamisch
markieren, Expression %col%
.Für Auswertungen mit Ressourcenplanwerten eignet sich auch das Business Intelligence Modul, wo eigene Auswertungen erstellt werden können.
Eine solche Auswertung steht als Plug-in: BI Umsatzprognose aus Ressourcenplanung zum Download zur Verfügung.
Wichtige Grundlagen beim Arbeiten mit Ressourcenplanungsdaten in Kürze:
initialize_with_period(self, start, end, subscriber)
implementiert werden.initialize(self, subscriber)
implementiert werden.get_date_by_column_index(column_index)
, siehe Beispiel Kapazitätsliste
.column_index
ist immer das Datum (als Integer), ansonsten Nonerowobj
ist immer das Zeilenobjektsource_entry
ist immer das Objekt, auf dem die Tabelle aufgerufen wird.setup_provider(self, subscriber)
implementiert und darin die Methode create_and_set_provider(self, source_entries, start_date, end_date, subscriber)
mit den entsprechenden Argumenten aufgerufen, siehe Via geerbtem Controller und Standard Custom Renderern
.get_capacity_method_arguments
im Controller implementiert werden. Dafür können Sie einfach die Standardmethode aus dem Modul vtcplanning 1:1 rauskopieren und einfügen, siehe Beispiel Via geerbtem Controller und Standard Custom Renderern
.planned_minutes_aggregated(source_entry, rowobj, self.period_start, self.period_end, subscriber)
im Controller implementiert werden. Hier kopieren Sie am besten die Standardmethode aus dem Modul vtcplanning raus und übergeben, je nach Liste, die entsprechenden Argumente. Diese sind im Artikel über die Vertec Python Funktionen
genau beschrieben. Siehe Beispiel Via geerbtem Controller und Standard Custom Renderern
.get_planned_minutes_aggregated(source_entry, rowobj, self.period_start, self.period_end, subscriber)
kann auf einem ResourcePlanningProvider jeder beliebige Planwert abgefragt werden, siehe Mittels List Controller und ResourcePlanningProvider
self.controller
auf den in der Liste hinterlegten List Controller zugreifen.get_column_index_from_expression(expression)
, mit welchem das Intervall der Spalte eruiert werden kann, siehe Einen eigenen Renderer schreiben
.