====== 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 me ir.actions.server code obj.assign_task_to_me() Assign task to me action client_action_multi project.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 ======