Site Tools


Hotfix release available: 2025-05-14a "Librarian". upgrade now! [56.1] (what's this?)
New release available: 2025-05-14 "Librarian". upgrade now! [56] (what's this?)
Hotfix release available: 2024-02-06b "Kaos". upgrade now! [55.2] (what's this?)
Hotfix release available: 2024-02-06a "Kaos". upgrade now! [55.1] (what's this?)
New release available: 2024-02-06 "Kaos". upgrade now! [55] (what's this?)
Hotfix release available: 2023-04-04b "Jack Jackrum". upgrade now! [54.2] (what's this?)
Hotfix release available: 2023-04-04a "Jack Jackrum". upgrade now! [54.1] (what's this?)
New release available: 2023-04-04 "Jack Jackrum". upgrade now! [54] (what's this?)
Hotfix release available: 2022-07-31b "Igor". upgrade now! [53.1] (what's this?)
Hotfix release available: 2022-07-31a "Igor". upgrade now! [53] (what's this?)
New release available: 2022-07-31 "Igor". upgrade now! [52.2] (what's this?)
New release candidate 2 available: rc2022-06-26 "Igor". upgrade now! [52.1] (what's this?)
New release candidate available: 2022-06-26 "Igor". upgrade now! [52] (what's this?)
Hotfix release available: 2020-07-29a "Hogfather". upgrade now! [51.4] (what's this?)
it:openerp:general

OpenERP

Server Konfigurationsdatei

Standardmäßig extended view für admin setzen

<!-- Set Extended View for Administrator -->
    <record model="res.users" id="base.user_root">
      <field name="view">extended</field>
    </record>

Felder anhand von Status sperren

'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),

Spalten Create_Date und Write_Date einblenden:

_columns = {
   'create_date': fields.datetime('Creation Date' , readonly=True),
   'write_date': fields.datetime('Write Date' , readonly=True),
}

Encoding

Unbedingt alle Python Dateien mit UTF-8 explizit kodieren, um ASCII-Konvertierungsfehler zu vermeiden.

# -*- coding: utf-8 -*-

Domain Filter auf one2many Spalte

Active Spalte

Legt man eine Bool-Spalte namens “active” an, wird dieses standardmäßig in Domains abgefragt.

Message an ui

from tools.translate import _
raise osv.except_osv(
                        _('Could not cancel purchase order !'),
                        _('You must first cancel all picking attached to this purchase order.'))

Übersetzung der message

In der .po Datei

#. module: purchase
#: code:addons/purchase/purchase.py:721
#, python-format
msgid ""
"You have to select a product UOM in the same category than the purchase UOM "
"of the product"
msgstr ""
"Sie müssen eine Mengeneinheit auswählen, die sich in derselben Kategorie wie "
"die Produktmengeneinheit (ME) des Einkaufs befindet"

einen String übersetzen

from tools.translate import _
 
 
result = _('text zu übersetzen')

summmen von float fields im tree

<field name="residual" sum="Residual Amount"/>

ein browse-objekt per xml-id holen

print pool.get("ir.model.data").get_object(cr, uid, "cp_crm", "salutation_1").title

kontextsensitive Suchformulare

 _columns={
        'partner_id': fields.many2one('res.partner', string='Partner'),
        'company_id': fields.many2one('res.company', string='Company'),
        'account_receivable': fields.many2one('account.account', domain="[('type', '=', 'receivable'), ('company_id', '=', company_id)]", string="Account Receivable"),
        'account_payable': fields.many2one('account.account', domain="[('type', '=', 'payable'), ('company_id', '=', company_id)]", string="Account Payable"),
        'company_name': fields.related('company_id', 'name', type='char', string='Company name'),
        'partner_name': fields.related('partner_id', 'name', type='char', string='Partner name'),

Durch ('company_id', '=', company_id) nimmt die Domäne aus dem aktuellen Kontext den Wert company_id.

Weiterhin kann auf ein Ändern eines Feldes aus dem Kontext reagiert werden, um die Domäne abzudaten.

<field name="company_id" widget="selection" width="150" on_change="on_change_company_id(company_id)"/>
<field name="account_payable" width="150"/>
<field name="account_receivable" width="150"/>
    def on_change_company_id(self, cr, uid, ids, company_id):
        d={}
        if company_id!=False:
            d['account_receivable']=[('company_id', '=', company_id)]
        return{'domain':d}

Context => auf übergeordnete Felder zugreifen

<field name="product_id" colspan="4" context="partner_id=parent.partner_id,quantity=product_qty,pricelist=parent.pricelist_id,uom=product_uom,warehouse=parent.warehouse_id"/>
   <field name="levels"/> <!-- one2many Verknüpfung -->
   <field name="units" nolabel="1" mode="tree,form" context="{'levels': map(lambda x: x[1], levels)}"> <!-- neue context variable levels = liste aus ids aus levels -->
     <tree string="Units">
       <field name="name" />
       <field name="level_id" />
     </tree>
     <form string="Unit">
       <field name="name"/>
       <field name="level_id" domain="[('id', 'in', context['levels'])]"/> <!-- many2one nur auf levels die im übergeordneten levels stehen -->
     </form>
   </field>

Spezifischen View in Field durch context verwenden

#zur Verfügung stehen search_view_ref, form_view_ref und tree_view_ref
<field name="test" context="{'search_view_ref':'modul.xml_id'}"/>

Filter durch Context setzen

#filter im such view
<filter string="Das ist ein Test Filter" name="test_filter" domain="[('id','>',0)]/>"
 
#filter setzen im field das auf andere Entität geht
<field name="test_field" context="{'search_default_test_filter':1}"
# 1 = filter an, 0 = filter aus

SQL Constraints

Im Modul res.groups habe folgende Anweisung entdeckt:

_sqlconstraints = [
  ('name_uniq', 'unique (name)', 'The name of the group must be unique!')
]

Man kann also super leicht contraints definieren…z.B. praktisch für NLV: maximal ein gültiger Standort zu einem Zeitpunkt und nicht zwei.

xml_id in id umwandeln

<record id="server_action_assign_task" model="ir.actions.server">
			<field name="name">Assign task to me</field>
			<field name="type">ir.actions.server</field>
			<field name="model_id" search="[('model', '=', 'project.task')]" />
      		<field name="state">code</field>
      		<field name="code">obj.assign_task_to_me()</field>
		</record>
		<record id="ir_value_assign_task" model="ir.values">
			<field name="name">Assign task to me</field>
			<field name="key">action</field>
			<field name="key2">client_action_multi</field>
			<field name="model">project.task</field>
			<field name="value" eval="'ir.actions.server,%d'%server_action_assign_task"/>
			<field name="object" eval="True"/>
		</record>

domain in action window mit xml_id

<field name="domain" eval="[('user_id','=',ref('base.user_root'))]"/>

lambda Ausdrücke in defaults columns

    _defaults = {
         'name':lambda self, cursor, user, context:self.pool.get(
                                                'res.users'
                                                ).read(
                                                        cursor,
                                                        user,
                                                        user,
                                                        ['name'],
                                                        context
                                                        )['name'],
         'state':lambda * a:'draft',
         'smtpport':lambda *a:25,
         'smtpserver':lambda *a:'localhost',
         'company':lambda *a:'yes',
         'user':lambda self, cursor, user, context:user,
         'send_pref':lambda *a: 'html',
         'smtptls':lambda *a:True,
     }

defaults überschreiben

Hiermit wird der default Wert des Feldes date überschrieben.

<field name="works" colspan="4" nolabel="1" mode="tree,form" context="{'default_date': date_current + time.strftime(' %%H:%%M:%%S')}">

Praktische Parameter der openerp_server.py

Um nur ein spezifisches Modul beim start neu zu laden:

openerp_server.py -d <dbname> --update=<modulname>

-----------------

Felder: one2many - was wird angezeigt

Wenn das Feld related auf ein one2many Feld zeigt, dann wird dort das erste Element verwendet unter Beachtund der Sortierung (_order).

Erweitern von Standard Selection Fields

Beispielsweise gibt es bei res.partner ein Auswahlfeld für den Adresstyp. Um diesen zu erweitern, muss im init.py der eigenen Klasse per Programmcode die neuen Selections hinzugefügt werden. Geht so:

<note important>Miki hat herausgefunden, dass die alten Selection-Werte erhalten bleiben. Damit ist folgendes Vorgehen eigentlich nicht mehr so notwendig und vereinfacht sich so, dass nur ein weiteres selection-Feld hinzugefügt werden muss. Dieses ergänzt dann die Auswahlwerte.</note>

def __init__(self, pool, cr):
        #TIP extend selection
        super(osv.osv, self).__init__(pool, cr)
        pool = pooler.get_pool(cr.dbname)
        partner_object = pool.get("res.partner.address")
        partner_object._columns['type'].selection.append(('house', 'House Address'))

Feldattribute in Abhängigkeit vom Status setzen, z.B. readonly nur bei Draft

'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),

ähnlich im XML:

<group attrs="{'readonly': [('field_name', '=', 'value')]}">

Many2Many Felder Füllen

        Note: The type of field values to pass in vals for relationship fields is specific:

                *

                  For a many2many field, a list of tuples is expected. Here is the list of tuple that are accepted, with the corresponding semantics

                  (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
                  (1, ID, { values })    update the linked record with id = ID (write *values* on it)
                  (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
                  (3, ID)                cut the link to the linked record with id = ID (delete the relationship between the two objects but does not delete the target object itself)
                  (4, ID)                link to existing record with id = ID (adds a relationship)
                  (5)                    unlink all (like using (3,ID) for all linked records)
                  (6, 0, [IDs])          replace the list of linked IDs (like using (5) then (4,ID) for each ID in the list of IDs)

                  Example:
                     [(6, 0, [8, 5, 6, 4])] sets the many2many to ids [8, 5, 6, 4]

                *

                  For a one2many field, a lits of tuples is expected. Here is the list of tuple that are accepted, with the corresponding semantics

                  (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
                  (1, ID, { values })    update the linked record with id = ID (write *values* on it)
                  (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)

                  Example:
                     [(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]

                *

                  For a many2one field, simply use the ID of target record, which must already exist, or False to remove the link.
                *

                  For a reference field, use a string with the model name, a comma, and the target object id (example: 'product.product, 5')

Mittels eineer Funktion lässt sich dies folgendermassen erledigen:

# Felddeklaration:
        'move_lines':fields.function(_get_lines, method=True, type='many2many', relation='account.move.line', string='Entry Lin
# Funktionsdefinition
   # Give Journal Items related to the payment reconciled to this invoice
    # Return ids of partial and total payments related to the selected invoices
    def _get_lines(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            id = invoice.id
            res[id] = []
            if not invoice.move_id:
                continue
            data_lines = [x for x in invoice.move_id.line_id if x.account_id.id == invoice.account_id.id]
            partial_ids = []
            for line in data_lines:
                ids_line = []
                if line.reconcile_id:
                    ids_line = line.reconcile_id.line_id
                elif line.reconcile_partial_id:
                    ids_line = line.reconcile_partial_id.line_partial_ids
                l = map(lambda x: x.id, ids_line)
                partial_ids.append(line.id)
                res[id] =[x for x in l if x <> line.id and x not in partial_ids]
        return res

Aus einem Tree oder From View einen anderen View per Python-Code öffnen

#wizard erstellen
class open_ref_wizard(osv.osv_memory):
 
    def open_ref(self, cr, uid, data, context):
 
        return {
            'name': 'test',
            'context': context, 
            'view_type': 'form',
            'view_mode': 'form,tree,calendar',
            'res_model': model,
            'res_id': id,
            'view_id': False,
            'type': 'ir.actions.act_window',
            'target': 'new',
            #weitere parameter:
            'nodestroy': True  #keine Ahnung was der macht
            'views': [(view_id_cal, 'calendar'), (view_id_form, 'form'), (view_id_tree, 'tree')],
        }
    }
 
open_ref_wizard()

<note tip>res_id ist die Instanz-ID von res_model und muss ein int sein (kein List, Tuple, Array) </note>

<note warning>Das hat nicht funktioniert: wenn man in view_id eine Liste oder Int setzt, dann kommt zwar der web-client damit klar, aber nicht der GUI Client. Mit dem Parameter views gehts.</note>

Old-Style Wizards

<!-- wizard definieren -->
<wizard string="Open Ref" model="cp_cash.liquidity.source" name="cp_cash.open_ref" id="open_ref" />
 
<!-- button in tree oder form einbinden -->
<button string="Open Ref" name="%(open_ref)d" type="action" icon="gtk-open" />

Message an UI

raise osv.osv.except_osv("Message", Values_For_Message, exc_Type)

Events in Feldern

Felder in Views haben ein “onchange” event Attribut: http://doc.openerp.com/v6.0/developer/2_6_views_events/events/events.html#on-change Format:

MethodenName(Parameter)

Die Methode muss folgende Signatur haben:

MethodenName(self, cursor, userid, selectierteElementeIds, parameter)

Um den View über die Änderungen zu informieren, wird eine collection Zurückgegeben:

{'values':{'KlassenAttribut':Wert}}

Objekt-ID per XML-ID holen

xml_id = "message_new_allocation"
try:
  _, id = self.pool.get('ir.model.data').get_object_reference(cr, uid, module_name, xml_id)
except:
  id = None

@Miki: wo ist die get_object Methode???

menue.xml

<openerp>
	<data>
        <record model="ir.actions.act_window" id="ACTION_ID">
            <field name="name">Label of the window</field>
            <field name="res_model">cp_base.the_cool_entity</field>
            <field name="view_mode">tree,form,graph</field>
        </record>
	<record model="ir.actions.server" id="ACTION_ID">
            <field name="name">Label</field>
            <field name="model_id" search="[('name', '=', 'cp_base.irgendwas')]" />
            <field name="type">ir.actions.server</field>
            <field name="state">code</field>
            <field name="code">
            #method must have params(self, cr, uid, *args)
            obj.a_cool_method()
 
            </field>
        </record>
 
 
        <menuitem name="Label" icon="terp-project" parent="base.menu_administration" id="menu1"/>
        <menuitem name="SubmenuLabel" parent="menu1" id="menu2" action="ACTION_ID" type="server"/>
 
	</data>
</openerp>

view.xml

<openerp>
    <data>
        <record id="view_payment_tree" model="ir.ui.view">
            <field name="name">cp_cash.payment.tree</field>
            <field name="model">cp_cash.payment</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Payment">
					<field name="pay_date" />
                    <field name="income" />
                    <field name="amount" />
                    <field name="name" />
					<field name="invoice_date" />
					<field name="due_date" />
	              <field name="recurring_monthly" />
	              <field name="recurring_until" />
	              <field name="partner_id" />
	              <field name="company_id" />
                </tree>
            </field>
        </record>
        <record id="view_payment_search" model="ir.ui.view">
        	<field name="name">cp_cash.payment.search</field>
        	<field name="model">cp_cash.payment</field>
        	<field name="type">search</field>
        	<field name="arch" type="xml">
        		<search string="Search">
        			<filter string="Recurring Monthly" domain="[('recurring_monthly', '=', True)]" help="Recurring Monthly True" icon="terp-project"/>
        		</search>
        	</field>
        </record>
 
        <record id="view_payment_form" model="ir.ui.view">
            <field name="name">cp_cash.payment.form</field>
            <field name="model">cp_cash.payment</field>
            <field name="type">form</field>
            <field name="arch" type="xml">
                <form string="Payment">
                	<field name="name"/>
                	<field name="partner_id"/>
                	<field name="recurring_monthly"/>
                	<field name="recurring_until"/>
                	<group colspan="4" col="6">
                		<separator string="Amounts and Dates" colspan="6"/>
                		<group colspan="6" col="4">
	                		<field name="income"/>
	                		<field name="amount"/>
                			<field name="pay_date"/>
                		</group>
                		<separator string="Invoice Details" colspan="6"/>
	                	<group colspan="4" col="6">
	                		<field name="invoice_date"/>
 		               		<field name="due_date"/>
  		              	</group>
                	</group>
                	<separator string="Notes" colspan="4"/>
                	<field name="notes" colspan="4" nolabel="1"/>
                </form>
            </field>
        </record>
 
        <record model="ir.ui.view" id="view_payments_line_graph">
		   <field name="name">payments.line.graph</field>
		   <field name="model">cp_cash.payment</field>
		   <field name="type">graph</field>
		   <field name="arch" type="xml">
		         <graph string="Payments">
		              <field name="pay_date" group="1" />
		              <field name="amount" operator="+" />
		        </graph>
		    </field>
		</record>
    </data>
</openerp>

Action-Button und Methode aus Model

Methode in Model:

def assign_task_to_me(self, cr, uid, ids, *args):
 
=======    ...
       return True #or raise Exception

View XML:

<button name="assign_task_to_me" string="Assign task to me" type="object" icon="gtk-execute" />

Kontextbezogenes Editieren

<field name="works" colspan="4" nolabel="1" mode="tree,form" context="{'default_date': date_current + time.strftime(' %%H:%%M:%%S')}">

SQL Constraints in Python-Klassen

     _sql_constraints = [
         ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "You have to select an employee or
         ('date_check', "CHECK ( number_of_days_temp > 0 )", "The number of days must be greater than 0 !"),
         ('date_check2', "CHECK ( (type='add') OR (date_from < date_to))", "The start date must be before the end date !")
     ]

Tree Farben

<tree colors="red:state in ('refuse');blue:state in ('draft');black:state in ('confirm','validate','validate1')" string="Leaves" >
it/openerp/general.txt · Last modified: 2025/06/24 08:34 by 20.171.207.8