Use of service sums, groupServices OCL Operator
Accelerated summation and grouping in the form of an OCL operator is available for optimizing custom evaluations or for use in custom reports.
The groupServices
operator exists in different variants, which differ mainly in that they are applicable to different types of objects:
Operator | Description |
---|---|
groupServices |
Global service sum operator. Refer to all services in the system. Values are output in key currency. Since OCL operators must always be based on a specific data type, the operator is registered to |
groupServicesP |
Project sum operator. Can be applied to a single project or to a list of projects. Values are output in key currency. |
groupServicesPC |
Project sum operator, but values are output in project currency. Can be applied to a single project or to a list of projects. If this operator is applied to a list of projects, it is necessary to make sure that all projects in the list have the same currency. Otherwise, the amounts in the different currencies will simply be added together, which would result in nonsensical numbers. |
groupServicesPCG |
Global operator which calculates all project sums in project currency, without currency conversion. A global operator is not called on a single object, but on If you work with several currencies, you must group the service subs by project, otherwise values in different currencies will be summed up.
|
groupServicesU |
User sum operator. Can be executed on a single user or a list of users. Values are output in key currency. |
groupServicesPh |
Phase sum operator. Can be applied to a single phase or to a list of phases. Values are output in key currency. |
groupServicesPhC |
Phase sum operator, but values are output in project currency. |
groupServicesC |
Service sum operator, expects a string argument for currency: groupServicesC(from, to, group, currency) Can be executed on projects, phases and users and returns the values in the specified currency, calculated according to the indicated exchange rates. If an empty string is given as currency argument, results are output in key currency.
|
The syntax for all these operators is as follows:
obj->groupServicesOperator(from, to, group)
obj |
Object to which the operator is applied. Can be of type project, user or phase, single or list. Global operators are called on the current session ( |
from, to |
From, to date, passed as a string in the format Services are summed for the specified period. If an empty string is given, all services are used.
|
group |
Grouping statement. Controls the grouping and sorting of the resulting sums. Can contain various grouping terms, separated by commas. If an empty string is given, the sums are not grouped. Possible grouping terms:
|
The result is a list of objects of type ServiceSum, which are sorted according to the specified grouping.
A service sum object has different attributes and a value for each grouping term:
Member | Description |
---|---|
minutesExtOpen | Total of external minutes not yet charged. |
minutesExtOpenUnprod | Total of external minutes not yet charged of services entered on projects with unproductive project types. |
minutesExtCharged | Total of external minutes charged |
minutesExtChargedUnprod | Total of external minutes charged of services entered on projects with unproductive project types. |
minutesIntOpen | Total of internal minutes not yet charged. |
minutesIntOpenUnprod | Total of internal minutes not yet charged of services entered on projects with unproductive project types. |
minutesIntCharged | Total of internal minutes charged |
minutesIntChargedUnprod | Total internal minutes charged of services entered on projects with unproductive project types. |
servicesCount | Quantity of services in this service sum. |
project | Project of this service sum, if grouped by project. |
user | User, if grouped by user. |
phase | Phase, if grouped by phases. |
type | Service type, if grouped by service type. |
invoice | Invoice, if grouped by invoices. |
entrydate | Start date of the date group, if grouped by year, month or day. |
feeUserOpen | Total fee according to the user rate, not charged. |
feeUserCharged | Total fee according to the user rate, charged. |
feeExtOpen | Total of external fee not yet charged. |
feeExtOpenUnprod | Total of external fee not yet charged, for services entered on projects with unproductive project types. |
feeExtCharged | Total of the external fee charged. |
feeExtChargedUnprod | Total of external fee charged, for services entered on projects with unproductive project types. |
feeIntOpen | Total of internal fee not yet charged. |
feeIntOpenUnprod | Total of internal fee not yet charged, for services entered on projects with unproductive project types. |
feeIntCharged | Total of internal fee charged. |
feeIntChargedUnprod | Total internal fee charged, for services entered on projects with unproductive project types. |
costOpen | Total of fee according to the cost rate, not yet charged. |
costCharged | Total of fee according to the cost rate, charged. |
On a project (or a list of projects) the groupServicesP
operator is called with the following arguments:
->groupServicesP('01.01.2024','31.12.2024','MONTH,PROJECT’)
As a result, we get a list of LeistSum objects, including the following attributes:
entrydate.formatDateTime('mm.yyyy')
project
minutesintOpen + minutesIntCharged
feeExtOpen + feeExtCharged
Special cases for the calculation arise, for example, in the case of fixed-price invoices without services or in the case of invoice discounts. In this case, the external value of the services cannot simply be used for the calculation of the service sums. How these special cases are dealt with in detail is explained below:
Special case | Calculation |
---|---|
Fixed-price phases without services (if the phase is on a fixed-price invoice without services, see there) |
Is displayed as a service sum as follows:
|
Fixed-price invoice without services |
Is displayed as a service sum as follows:
|
Invoices with discount |
If there is a discount on an invoice, it is deducted pro rata from the feeExt of the service sum. The percentage is calculated as follows:
|
Fixed-price phases and invoices with fixed services |
In the case of fixed-price invoices or fixed-price phases, the fixed price is distributed proportionally to the external fee of the services that are not fixed (see also fee for fixed-price phases and invoices), i.e. have not checked the fix external values checkbox. However, if ALL services of an invoice are fixed, then the fixed price on the phase or on the invoice cannot be allocated to the services. In this case, the service sum cannot reflect the fixed price. |
As a result of these special cases, deviations may arise between the sum of the services and the service sums. If, for example:
openServices->union(charged)->select((entrydate >= encodedate(2024,1,1)) and (entrydate < encodedate(2025,1,1))).wertExt->sum
returns a different value than:
self->groupServicesP('01.01.2024','31.12.2024','')->collect(feeExtOpen + feeExtCharged)->sum
check whether it has any of the above-mentioned special cases in the period analysed.
In the case of fixed services on fixed-price phases or invoices, no difference can be found here, since neither the services nor the service sums include the fixed amount. In this case, you will only see the difference if you compare the sums of the services with the sums of the invoices.
Vertec ships with Firebird 5 as of version 6.7.0.9 and later, this issue no longer exists. However, if a groupServicesOperator
is called on a list of projects, phases, or users with an earlier version of Firebird, there is the following limitation:
If the list of calling objects (i.e. not the quantity of services in the result, but the projects, phases or users on which the operator is called) is greater than 1500 entries, there is an error from the Firebird server due to invalid SQL:
General SQL Error. Implementation limit exceeded too many values (more than 1500) in member list to match against.
One way to work around this problem is to use the global groupServices
operator, which applies to all services.
Example: If there are more than 1500 projects and you want to call project->groupServicesP(...)
, you can work around this:
Session.allInstances->first->groupServices('01.01.2025', '30.06.2025', 'PROJECT')
Returns a list of service sums for all services, grouped by projects.
Since the service sums are calculated optimally via SQL, the behavior of changes to basic data is slightly different than otherwise in Vertec.
Service sums are always recalculated as soon as any service is changed in the system. However, the recalculation only takes place if the service sum values are somehow visible. It is therefore not advisable to have a list of service sums on the screen at the same time as entering new services, as all sums would be recalculated with each new entry. Calculating service sums is relatively fast, but if you recalculate unnecessarily often, the delay is still noticeable.
In particular, it is not recommended to use groupServicesOperators
in column expressions of lists, since the performance gain in calculating the sum is usually overcompensated by the large quantity of individual SQL queries.
Various values in Vertec are derived, which means that they are calculated each time they are accessed. This means that they are always up-to-date, but not retrievable in the database.
To ensure that these values are still available for the accelerated summation, there are so-called shadow attributes, which are written when calculating the derived attributes. These shadow attributes are persistent, i.e. they are stored in the database and can be queried directly.
The following shadow attributes are available. They are prefixed with sql.
Attribute | Class |
---|---|
sqlFeeExt |
Service, Expense, Outlay |
sqlMinutesExt |
Service |
sqlFeeInt, sqlDiscountAmount |
Expense and Outlay |
sqlRabattBetrag (only versions before 6.4.0.17) |
Invoice |
sqlValutaDatum (only versions before 6.4.0.17) |
Invoice |