Python code for Office reports Before – After
Product line
Standard
|Expert
Operating mode
CLOUD ABO
|ON-PREMISES
Modules
Services & CRM
Budget & Phases
Purchases
Resource Planning
Business Intelligence
There are improvements to the Office report features. First of all, the changes are fully backward compatible. All previous reports continue to run unchanged.
This article here is intended to help you if you want to rebuild existing reports.
For information on how Office reports are structured starting with version 6.5.0.20, see Python code for office reports .
The designation “Frame” confuses many users. That’s why the frames are renamed “Tables”. This is more familiar to most people from MS Access etc. This applies to all uses:
Before | After |
---|---|
class project(Frame): | class project(Table): |
main_frame = project | main_table = project |
def calculate_main_frame(context): | def calculate_main_table(context): |
OclFrameField(“open services”, service) | OclTableField(“open services”, service) |
If you want to rebuild existing code, you first replace frame with table (turn on case-sensitive!), then frame with table. Then you have changed all occurrences in the code.
OCL fields no longer need to be declared, especially if they consist of only a single term, namely the member.
Before | After |
---|---|
class project(Frame): businessclass = “project” fields = [ OclTextField(“code”), ] |
class project(Table): businessclass = “project” |
If businessclass is specified, the field values are returned with the correct data type. If no businessclass is specified, the value is returned as a string.
We recommend specifying the businessclass for two reasons:
pass
.Preferably:
class Projekt(Table): businessclass = "Projekt"
As:
class Projekt(Table): pass
It is also possible to evaluate longer OCL expressions directly in the report instead of in the code. However, the report can quickly become confusing because the OCL expression has to be stored as text, which is then provided with a comment (and not, as before, stored in the comment itself).
Therefore, we recommend that longer OCL expressions should still be stored directly in the code and that they should be called using the field name in the report as before.
Example
OclTextField("text", "if typ->size>0 then typtext + ' ' + text else text endif")
OCL expressions in subbands are not supported. The Ocltablefields still need to be declared.
You can easily get rid of the “simple” OCL fields by simply deleting all fields from the code that consist only of an OCL expression:
Before | After |
---|---|
class Service(Frame): businessclass = “OpenService” fields = [ OclDateField(“date”), OclTextField(“email”, “worker.briefemail”), OclMinuteField(“minutext”), OclCurrencyField(“valuetext”), ] |
class Service(Table): businessclass = “OpenService” fields = [ OclTextField(“email”, “worker.briefemail”), ] |
The Office Report will continue to function as it is if the following are observed:
If the fields in the report are used as OCL expressions, they become case-sensitive, so make sure that the field names are case-sensitive, or the way the expression is written in OCL .
You no longer need a Boolean field to query whether a list or value is empty. You can query the value directly in the report.
Before |
---|
class project(Frame): |
After |
---|
class project(Table): |
For the negative case – you only want to show a tape if it has no list or value – you can not
use directly in the report:
This works not only for lists, but for all values. In general:
You go through the corresponding OclBooleanFields. Whenever it concerns an entire list or a single field, you can delete the declaration. In the report, you have to adjust the expression at the Cond-Band accordingly (see example above).
It is now possible to specify the field name for field calculations. This makes it possible to divide the same field calculation for several fields:
class Projekt(Table): businessclass = "Projekt" fields = [ CurrencyField("offeneLeistungen", "calc_leistungen"), CurrencyField("offeneSpesen", "calc_spesen"), ] def calc_leistungen(context): return context.evalocl("offeneleistungen.wertext->sum") def calc_spesen(context): return context.evalocl("offenespesen.wertext->sum")
class Projekt(Table): businessclass = "Projekt" fields = [ CurrencyField("SummeOffeneLeistungen", "calc_summen"), CurrencyField("SummeOffeneSpesen", "calc_summen"), ] def calc_summen(context, fieldname): if fieldname == " SummeOffeneLeistungen ": return context.evalocl("offeneleistungen.wertext->sum") if fieldname == " SummeOffeneSpesen ": return context.evalocl("offenespesen.wertext->sum")
Probably the biggest change is the implementation of an init method for rows:
def initialize_row(context, row):
This is called when each new row is created. It is called before the automatic field calculations (via OCL or calculation function), i.e. it can calculate values and store them in the context object, which can then be used by field calculation features.
All fields can also be accessed. If they are calculated fields, they will be calculated when accessed.
class Leistung(Table): businessclass = "OffeneLeistung" class Projekt(Table): businessclass = "Projekt" fields = [ TableField("leistungen", Leistung), ] def initialize_row(context, row): row.leistungen = Leistung(context, context.evalocl("offeneLeistungen->orderby(datum)"))
For example, the example in the Field Reference for Field Calculation Methods section could be worded as follows:
class Projekt(Table): businessclass = "Projekt" fields = [ CurrencyField("summeOffeneLeistungen"), CurrencyField("summeOffeneSpesen"), ] def initialize_row(context, row): row.summeOffeneLeistungen = context.evalocl("offeneleistungen.wertext->sum") row.summeOffeneSpesen = context.evalocl("offenespesen.wertext->sum")
It is worthwhile to rebuild existing code if you have different field calculations that access the same lists and always have to filter them, e.g. to the up-to-date object. Instead:
Can this be done as follows:
This is not only clearer, but of course also faster.
Subtables only need to be created if they are really needed or meet the required criteria. This no longer has to be done in the bands or with Boolean expressions.
The conversion is also interesting if you have previously created the tables by hand somewhere in the code. It is now often possible to place the code for the subtables in the initialize_row of the subtables – which makes the code more readable and structured.
Adding rows manually via add_row
has been simplified.
Instead of:
phasenTable = Projektphase(context) projectRow.phasen = phasenTable for phase in projekt.phasen: phasenRow = phasenTable.add_row(phase)
new submits:
for phase in project.phasen: phasenRow = projectRow.phasen.add_row(phase)
In calculation methods of table fields, the corresponding table can be accessed via table.
class Leistung(Table): businessclass = "OffeneLeistung" class Projekt(Table): businessclass = "Projekt" fields = [ TableField("leistungen", Leistung, "calculate_leistungen"), ] def calculate_leistungen(context): leistlist = context.evalocl("offeneleistungen->orderby(datum)") for leist in leistlist: if leist.wertext > 20: leistrow = context.table.add_row(leist)
The variable main_table
to identify the top-level table only need to be specified if it is not clear which is this main table.
This is identified by the fact that it does not have a supertable.
If there are several tables without a parent table, the parent table must still be specified.
Normally, the rows of the type
main_table = Projekt
can simply be deleted.
If several tables are run in parallel (and not hierarchically), the row must remain.
Often, in List Office reports, you have to define a report table that has exactly 1 row in order to be able to display fields on it for display in the base area of the report or the objects as a list. Previously, you needed the following construct to create this row:
class Report(Frame): fields = [ FrameField('projekte', 'Projekt', 'calculate_projekte'), ] def calculate_projekte(context): return Projekt(context, context.rootlist) def calculate_main_frame(context): frame = Report(context) frame.add_row() return frame main_frame = Report
New for this is the SingleRowTable
. This contains exactly one row, which is automatically created. So the following code is sufficient:
class Report(SingleRowTable): fields = [ TableField('projekte', 'Projekt', 'calculate_projekte'), ] def calculate_projekte(context): return Projekt(context, context.rootlist)
It is important to know that this row has no object. It is therefore not possible to compute fields on it via OCL. context.evalocl()
goes wrong, because the context has no object.
However, it is possible to calculate fields as shown above. Field values can also be set in an initialize_row .
What also works are OCL calculations on concrete objects of the type context.rootlist.evalocl()
When Registrations of office reports , translation into project language can be activated. This affects the language used for translation (texts marked with translation in the report template): the project language is used instead of the interface language.
In order that this language can be used in the report code or that the report can also be output in a different language, the following new methods are available:
context.language="IT"
It has the following values:
– DE: German (Switzerland)
– DD: German (Germany)
– EN: English
– FR: French
– IT: Italian
If a jargon is to be taken into account (project or mandate language), the suffix can also be used 0
(for project language) or 1
(for mandate language): DE0, DE1, etc.
– context.translate(text): This method is available in the report code to translate texts according to the report language (context.language).
The last value of context.language set in the code is used to translate the texts in the report template.
If an unknown language code is entered, the original texts are shown. The report is simply not translated.
This translation system does not work for MLString fields because they have a different translation mechanism. See the article on Multilingualism with vertec for more information on what MLString fields are and how they are translated.
If you want to translate such terms in Office reports, you have to use the OCL method .asstringbylanguage :
context.evalocl("typ.text.asStringByLanguage(language)")
Possible languages/parameters are 'DE’, 'DD’ (from 6.5.0.9), 'FR’, 'IT’, 'EN’ or 'NV’. Jargon discrimination is not possible for MLString fields.
class Leistung(Table): businessclass = "OffeneLeistung" fields = [ OclTextField("text", "typ.text.asStringByLanguage(language)"), ] def initialize_row(context, row): context.language = "FR"
or
class Leistung(Table): businessclass = "OffeneLeistung" fields = [ TextField("text"), ] def initialize_row(context, row): context.language = context.language[:2] row.text = context.evalocl("typ.text.asStringByLanguage(language)")
With context.language[:2]
find the first two symbols of the existing Language attribute. Since the default language in reports is always defined with the jargon suffix (DE0..), and this is not possible for MLString fields (see above), the translation to the current report language is achieved.