====== OpenERP ======
====== Server Konfigurationsdatei ======
* Damit kann man scheinbar einen Login hinterlegen - praktisch
* Auto-Save für Formulare
* [[http://doc.openerp.com/v6.0/developer/1_1_Introduction/3_configuration.html]]
====== Standardmäßig extended view für admin setzen ======
extended
====== 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 ======
[[http://www.openerp.com/forum/topic23503.html]]
====== 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 ====
====== 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.
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 =====
====== Spezifischen View in Field durch context verwenden ======
#zur Verfügung stehen search_view_ref, form_view_ref und tree_view_ref
====== Filter durch Context setzen ======
#filter im such view
====== 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 ======
Assign task to meir.actions.servercodeobj.assign_task_to_me()Assign task to meactionclient_action_multiproject.task
====== domain in action window mit xml_id ======
====== 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.
====== Praktische Parameter der openerp_server.py ======
Um nur ein spezifisches Modul beim start neu zu laden:
openerp_server.py -d --update=
-----------------
====== 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:
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.
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:
====== 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()
res_id ist die Instanz-ID von res_model und **muss ein int** sein (kein List, Tuple, Array) 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.
====== Old-Style Wizards ======
====== 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 ======
Label of the windowcp_base.the_cool_entitytree,form,graphLabelir.actions.servercode
#method must have params(self, cr, uid, *args)
obj.a_cool_method()
====== view.xml ======
cp_cash.payment.treecp_cash.paymenttreecp_cash.payment.searchcp_cash.paymentsearchcp_cash.payment.formcp_cash.paymentformpayments.line.graphcp_cash.paymentgraph
====== 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**:
====== Kontextbezogenes Editieren ======
====== 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 ======