Diferencia entre revisiones de «OpenERP»
Línea 1265: | Línea 1265: | ||
===== Vistes Form ===== | ===== Vistes Form ===== | ||
La vista formulari per a un objecte d’OpenObject consisteix en la correcta distribució en la pantalla dels camps de l’objecte amb l’objectiu de facilitar-ne la visualització i/o l’edició d’un recurs. | La vista formulari per a un objecte d’OpenObject consisteix en la correcta distribució en la pantalla dels camps de l’objecte amb l’objectiu de facilitar-ne la visualització i/o l’edició d’un recurs. L’element arrel de l’XML que defineix l’arquitectura de la vista és form. | ||
Els camps en una vista formulari d’OpenObject sempre estan distribuïts en la pantalla segons les següents normes: | Els camps en una vista formulari d’OpenObject sempre estan distribuïts en la pantalla segons les següents normes: |
Revisión del 16:07 26 sep 2013
OpenERP és un producte de codi obert que es distribueix sota dos tipus de desplegament: On-premise, sota els SO Linux i Windows, amb dues versions (Community, gratuïta, i Enterprise, de pagament); i el SaaS.
La llicència de la versió 6 i 7 és és AGPLv3 o AGPL+Private User.
L’OpenERP és desenvolupat sota una arquitectura web client-servidor de tres capes:
- La base de dades en un servidor PostgreSQL
- Un servidor OpenERP que conté tota la lògica de negoci
- Un servidor web, que en la versió 6.x està ubicat conjuntament amb el servidor OpenERP
- La capa client que té diverses possibilitats: web, GTK i possibles clients desenvolupats amb els protocols XML-RPC i Net-RPC
Instal·lació
Podem seguir aquest manual https://doc.openerp.com/7.0/es/install/#installation-link
Explotació i adequació
L’explotació i adequació de l’ERP d’una organització és una tasca imprescindible, ja que garanteix que el programari es mantingui en condicions de ser utilitzat per l’organització per tal de donar sortida a les seves necessitats. Per poder-ho dur a terme, per una banda cal identificar les necessitats (tasca pròpia de consultors) i, per una altra, tenir un coneixement profund de l’ERP, tant en les funcionalitats que facilita (tasca de consultors i implantadors) com en les qüestions tècniques vinculades a l’ERP (tasca d’analistes i programadors).
L’OpenERP és un programari de gestió empresarial desenvolupat sobre el framework OpenObject de tipus RAD (Rapid Application Development).
La facilitat dels entorns RAD rau en el fet que el desenvolupament d’aplicacions és molt simple pel programador, de manera que amb poc esforç es pot obtenir aplicacions d’altes prestacions.
L’OpenObject facilita diversos components que permeten construir l’aplicació:
- La capa ORM (Object Relational Mapping) entre els objectes Python i la base de dades PostgreSQL. El dissenyador-programador no efectua el disseny de la base de dades; únicament dissenya classes, per les quals la capa ORM d’OpenObject n’efectuarà el mapat sobre el SGBD PostgreSQL.
- Una arquitectura MVC (model-vista-controlador) en la qual el model resideix en les dades de les classes dissenyades amb Python, la vista resideix en els formularis, llistes, calendaris, gràfics… definits en fitxers XML i el controlador resideix en els mètodes definits en les classes que proporcionen la lògica de negoci.
- Un sistema de fluxos de treball o workflows.
- Dissenyadors d’informes.
- Facilitats de traducció de l’aplicació a diversos idiomes.
Tal com es pot observar, són molts els components d’OpenObject a conèixer per poder adequar l’OpenERP a les necessitats de l’organització, en cas que les funcionalitats que aporta l’OpenERP, tot i ser moltes, no siguin suficients.
La base de dades d'OpenERP
En l’OpenERP no hi ha un disseny explícit de la base de dades, sinó que la base de dades d’una empresa d’OpenERP és el resultat del mapatge del disseny de classes de l’ERP cap el SGBD PostgreSQL que és el que proporciona la persistència necessària per als objectes.
En conseqüència, l’OpenERP no facilita cap disseny entitat-relació sobre la base de dades d’una empresa ni tampoc cap diagrama del model relacional.
Si sorgeix la necessitat de detectar la taula o les taules on resideix determinada informació, és perquè es coneix l’existència d’aquesta informació gestionada des de l’ERP i, per tant, es coneix algun formulari de l’ERP a través del qual s’introdueix la informació.
L’OpenERP possibilita, mitjançant els clients web i GTK, recuperar el nom de la classe Python que defineix la informació que s’introdueix a través d’un formulari i el nom de la dada membre de la classe corresponent a cada camp del formulari. Aquesta informació permet arribar a la taula i columna afectades, tenint en compte dues qüestions:
- El nom de les classes Python d’OpenERP sempre són en minúscula (s’utilitza el guió baix per fer llegible els mots compostos) i segueix la nomenclatura nom_del_modul.nom1.nom2.nom3… en la qual s’utilitza el punt per indicar un cert nivell de jerarquia. Cada classe Python d’OpenERP és mapada en una taula de PostgreSQL amb moltes possibilitats que el seu nom coincideixi amb el nom de la classe, tot substituint els punts per guions baixos.
- Els noms dels atributs d’una classe Python sempre són en minúscula (s’utilitza el guió baix per fer llegible els mots compostos). Cada dada membre d’una classe Python d’OpenERP que sigui persistent (una classe pot tenir dades membres calculades no persistents) és mapat com un atribut en la corresponent taula de PostgreSQL amb el mateix nom.
D’aquesta manera, coneixent el nom de la classe i el nom de la dada membre, és molt possible conèixer el nom de la taula i de la columna corresponents. Es pot configurar els clients web i GTK per tal que informin del nom de la classe i de la dada membre en situar el ratolí damunt les etiquetes dels camps dels formularis.
Figura 1.1 Activar el debug mode
Si observem la figura 1.2, podem observar com:
Dalt apareix un desplegable que diu depurar#574, és la vista per defecte per a analitzar els elements dels formularis.
El camp Referencia cliente es diu client_order_ref de l'objecte sale.order. Per tant, la columna client_order_ref de la taula sale_order.
Figura 1.2 Dades de depuració
Respecte al desplegable de dalt, la resta d'opcions són:
- View Fields que permet obtenir una llista dels camps de la vista actual, amb els paràmetres corresponents.
- Fields View Get que mostra l’XML generat per la vista actual. Cal tenir en compte que si la vista s’ha generat a partir de diverses vistes XML heretant les unes de les altres, aquí se’n mostra el resultat final.
- Manage Views que mostra un llistat de les vistes relacionades amb la vista actual. Des d’aquest punt es poden crear noves vistes, eliminar-les o editar-les, encara que és recomanable utilitzar aquesta funcionalitat només per consultar. Es recomana realitzar les modificacions creant nous mòduls, per no perdre les modificacions davant actualitzacions de l’ERP.
- Edit TreeView, Edit SearchView, Edit Action i Edit Workflow que serveixen per accedir a l’edició de les vistes relacionades amb la vista actual.
- Si estem editant un registre (mode formulari) o consultant-lo (mode pàgina), apareix una nova opció View Log (perm_read) que mostra informació relativa al registre actual.
Accés de només lectura a les dades
Les empreses acostumen a tenir, entre els seus responsables, usuaris finals que poden efectuar consultes no previstes a la base de dades i que, per aconseguir-ho, poden utilitzar eines gràfiques per elaborar consultes o fins i tot, si són prou espavilats, executar consultes SQL des d’una consola d’accés.
En aquesta situació, cal facilitar als usuaris que correspongui, un usuari per accedir al SGBD PostgreSQL amb els privilegis d’accés, en mode consulta, als objectes de la base de dades que correspongui. S’aconsella seguir els següents passos:
1. Crear els usuaris de SGBD amb les contrasenyes que corresponguin.
Amb pgAdmin és fàcil d'afegir un nou rol de login.
2. Donar els privilegis d’accés adequats als usuaris que corresponguin.
Instal·lació de mòduls
Per a instal·lar mòduls que fem nosaltres, cal guardar-los en:
/usr/lib/pymodules/python2.7/openerp/addons/
Poden estar en .zip o descomprimits.
En OpenERP, fem que el nostre usuari tinga permís d'accés i característiques tècniques:
Eixim de sessió i entrem de nou. Ara tenim accés a un menú molt més complet on podem actualitzar la llista de mòduls, trobar el nostre i instal·lar-lo.
Si el mòdul falla en la part de python i no es pot instal·lar, pot ser que quede en un estat en el que no estiga instal·lat, però que no accepte ser esborrat de la llista de mòduls ni ser actualitzat i reparat. La raó és que la instal·lació ha creat les taules i les entrades en la base de dades, però no del tot.
L’exemple el tenim en el primer intent d’instal·lar el mòdul school. Ens diu que no existeix la referència ir.partner.address. Es veritat, en OpenERP ja no està eixe model. Anem al fitxer .py i llevem la línia. Si intentem instal·lar de nou ens dirà el mateix problema. La raó és que ja ha creat una taula school_professor amb el camp many2one que apunta a ir.partner.address i ha clavat en les taules ir_model_constraints, ir_model_relation, ir_model, ir_model_fields, ir_module_module i altres, dades del mòdul.
Solució:
- Parar el servici OpenERP en el servidor per a que esborre la caché que manté del mòdul.
- Entrar en psql <nom empresa> amb l’usuari postgres
- Executar aquesta consulta:
select id,name from ir_module_module where name like 'uml%'; (suposant que el mòdul es diu uml_test)
- Imaginem que diu que el mòdul té el id=217, ara cal executar aquestes ordres sql:
delete from ir_model_fields where model like '%school%'; delete from ir_model_data where module='uml_test'; delete from ir_model_relation where name like '%course%'; delete from ir_model_constraint where module=217; delete from ir_model where name like '%school%'; drop table course_student_subscription_rel ; drop table school_course; drop table school_event; drop table school_professor; drop table school_student;
- Pot ser que alguna falle perquè encara té alguna referència en altres taules. Cal asegurar-se de que no queden restes del mòdul per la base de dades.
- Per últim, eliminem el mòdul de la llista de mòduls:
delete from ir_module_module where name=’uml_test’;
- Eixir del psql i de l’usuari postgres
- Modificar el .py i el xml per a que no tinguem ninguna referència a ir.partner.address
- Arrancar el servici openerp-server
- Tornar a buscar e instal·lar el mòdul.
Desenvolupament de mòduls en OpenERP
Anem a desenvolupar un mòdul molt simple d'exemple mentre expliquem cóm se fan els mòduls. Aquest serà per a una academia que tindrà professors, cursos i alumnes.
Estructura bàsica dels mòduls
Un mòdul pot contenir els següents elements:
- Objecte de negoci: declara com classes de Python qual hereta de la classe osv.Model, la persistència d'aquests recursos és manejada completament per l'ORM d'OpenERP.
- Dades: Arxius XML/CSV amb les metadades (vistes i la declaració de fluxos de treball), les dades de configuració (parametrització de mòduls) i les dades de demostració.
- Informes: RML (format XML). HTML / MAKO o plantilles d'informes d'OpenOffice, que es combinaran amb qualsevol tipus de dades de negocis i generar HTML, ODT o informes en PDF.
Cada mòdul està contingut en el seu propi directori al servidor al directori de addons, configurat a la instal·lació del servidor. Per crear un nou mòdul, són necessaris els següents passos:
- Crear un subdirectori en el directori de addons
- Crear el __ init__.py per a l'importació d'arxius.
- Crear el __ openerp__.py que és el que defineix el mòdul.
- Crear arxius de Python que continguen objectes
- Crear. xml que contenen dades de mòduls com ara vistes, entrades de menú o dades de demostració
- Opcionalment crear informes i fluxos de treball
__init__.py
És l'arxiu d'importació de Python, perquè un mòdul d'OpenERP és també un mòdul ordinari de Python. L'arxiu ha d'importar tots els altres arxius python o submòduls.
Per exemple, si un mòdul conté un sol arxiu de python anomenat openacademy.py, l'arxiu ha de ser similar a:
import openacademy
__openerp__.py
Aquest arxiu, que ha de ser un diccionari Python literal, és responsable de:
- Determinar els arxius XML que es va a analitzar durant la inicialització del servidor
- Determinar les dependències del mòdul creat.
- Declarar metadades addicionals
Aquest fitxer ha de contenir un diccionari de Python amb els següents valors:
- name: el nom del mòdul, en anglès.
- version: la versió del mòdul.
- description: la descripció del mòdul (text).
- author: l’autor del mòdul.
- website: el lloc web de l’autor del mòdul.
- license: la llicència del mòdul (per defecte, la que correspon a la versió de servidor OpenERP on s’instal·larà el mòdul).
- category: categoria a la qual pertany el mòdul. Text separat per barres / amb la ruta jeràrquica de les categories.
- depends: llista Python de mòduls dels quals depèn aquest mòdul. El mòdul base s’acostuma a afegir perquè conté dades utilitzades en vistes, informes…
- init_xml: llista Python de noms de fitxers XML que es carregaran en iniciar el servidor OpenERP amb l’opció -init=module. Les rutes dels fitxers han de ser relatives a la carpeta on és el mòdul. En aquesta llista s’acostuma a posar els fitxers relatius a definició de workflows i les dades a carregar en el procés d’instal·lació del mòdul.
- update_xml: llista Python de noms de fitxers XML que es carregaran en iniciar el servidor OpenERP amb l’opció -update=module. Les rutes dels fitxers han de ser relatives al directori on és el mòdul. En aquesta llista s’inclouen els fitxers relatius a vistes, informes i assistents.
- demo_xml: llista Python de noms de fitxers XML que es carregaran si s’ha instal·lat el servidor OpenERP amb dades de demostració o exemple. Les rutes dels fitxers han de ser relatives al directori on és el mòdul.
- installable: els valors permesos són True o False i determinen si el mòdul és o no és instal·lable.
- active: els valors permesos són True o False i determinen si el mòdul serà instal·lat automàticament en el procés de creació de l’empresa. Per defecte, False.
En el cas de del nostre exemple de openacademy tindrà un aspecte similar a:
{ 'name' : "OpenAcademy", 'version' : "1.0", 'author' : "OpenERP SA", 'category' : "Tools", 'depends' : ['mail'], 'data' : [ 'openacademy_view.xml', 'openacademy_data.xml', 'report/module_report.xml', 'wizard/module_wizard.xml', ], 'demo' : [ 'openacademy_demo.xml' ], 'installable': True, }
Objectes
Tots els recursos OpenERP són objectes: factures, socis. Les metadades també són objecte també: menús, accions, informes ... Els noms d'objectes són jeràrquics, com en els exemples següents:
account.transfer: una transferència de diners account.invoice: una factura account.invoice.line: una línia de factura
En general, la primera paraula és el nom del mòdul.
Els objectes es declaren en python com a subclasse de osv.Model
El ORM d'OpenERP es construeix sobre PostgreSQL. Per tant, és possible consultar l'objecte utilitzat per OpenERP utilitzant la interfície d'objecte (ORM) o mitjançant l'ús d'instruccions SQL directament.
Però és perillós per escriure o llegir directament a la base de dades PostgreSQL, com es pot escurçar passos importants com la comprovació de restriccions o la modificació de flux de treball.
Arxius XML
Arxius XML ubicats al directori dels mòduls s'usen per inicialitzar o actualitzar la base de dades quan s'instal·la o actualitza el mòdul. S'utilitzen per a molts fins, entre els quals podem citar:
- Inicialització i declaració de dades de demostració,
- Declaració de vistes,
- Declaració dels informes,
- Declaració dels fluxos de treball
Les dades poden ser inserides o actualitzades en les taules de PostgreSQL corresponents als objectes OpenERP utilitzant fitxers XML. L'estructura general d'un fitxer XML OpenERP és el següent:
<?xml version="1.0"?> <openerp> <data> <record model="model.name_1" id="id_name_1"> <field name="field1"> "field1 content" </field> <field name="field2"> "field2 content" </field> (...) </record> <record model="model.name_2" id="id_name_2"> (...) </record> (...) </data> </openerp>
Diseny de mòduls amb DIA
Disseny del model
L’eina de diagramació Dia, amb l’extensió per generar mòduls per a OpenERP, facilita el disseny de mòduls per OpenERP. Les possibilitats que permet l’eina Dia són, però, limitades i, en conseqüència, ens cal aprendre com escriure directament els mòduls utilitzant els llenguatges Python i XML.
Un mòdul d’OpenERP incorpora, en el seu interior, tots els elements del patró model-vista-controlador en el qual es basa el desenvolupament d’OpenObject. L’eina Dia permet definir el model via diagrama UML i l’extensió per generar mòduls per a OpenERP ens facilita el mòdul que conté:
- El model, definit a través de llenguatge Python, corresponent al diagrama UML dissenyat.
- Una vista, definida a través del llenguatge XML, generada de forma automàtica.
- Un controlador buit, ja que l’eina Dia no permet dissenyar els mètodes.
Definició d'objectes OpenERP amb Python
El model en OpenERP es descriu a través de classes escrites en Python i OpenObject s’encarrega de fer-les persistents en el SGBD PostgreSQL.
Per definir un objecte (classe) d’OpenERP cal definir una classe de Python i posteriorment instanciar-la, fet que provoca la creació de l’objecte (classe) d’OpenERP. La classe ha d’heretar obligatòriament de la classe osv del mòdul osv.
L’esquelet de la classe Python que permet definir l’objecte OpenERP té la forma:
class name_of_the_object (osv.osv): _name = 'name.of.the.object' _columns = {...} ... name_of_the_object()
Les classes Python que permeten definir objectes (classes) d’OpenERP tenen dos atributs obligatoris i altres opcionals. Els atributs obligatoris són:
- _name: nom de l’objecte (classe) d’OpenERP. Aquest nom s’utilitza de forma automàtica per generar el nom de la taula de PostgreSQL, i canvia els punts per guions baixos.
- _columns: diccionari Python amb la declaració de les dades de l’objecte (classe) d’OpenERP, que passaran a ser les columnes de la taula de PostgreSQL.
Els atributs opcionals són:
- _auto: els valors possibles són True i False. Per defecte True. Determina si s’ha de generar la taula PostgreSQL a partir de la definició de l’objecte OpenERP. El valor False s’utilitza per objectes OpenERP no persistents que es basen en vistes definides en PostgreSQL.
- _constraints: definició de restriccions sobre l’objecte, definides amb codi Python.
- _sql_constraints: definició de restriccions SQL sobre l’objecte, definides a la corresponent taula de PostgreSQL.
- _defaults: diccionari Python que conté els valors per defecte per les dades (definides a _columns) de l’objecte (classe) d’OpenERP que ho precisin.
- _inherit: objecte (classe) d’OpenERP del qual hereta l’objecte (classe) que estem definint.
- _inherits: llista d’objectes (classes) d’OpenERP dels quals hereta l’objecte (classe) que estem definint. La llista ha de seguir la sintaxi d’un diccionari Python de la forma:
{'nom_objecte_pare':'nom_de_camp',...}
- _log_access: valors possibles True i False. Per defecte, True. Indica si els accessos d’escriptura als recursos (objectes) han de quedar enregistrats. En cas afirmatiu, de forma automàtica es creen a la corresponent taula de PostgreSQL les columnes create_uid, create_date, write_uid i write_date per enregistrar els accessos d’escriptura.
- _order: nom dels atributs utilitzats per ordenar els resultats dels mètodes de cerca i lectura. Per defecte, s’utilitza el camp _id generat automàticament en totes les taules de PostgreSQL corresponents a objectes (classes) d’OpenERP. Exemples:
_order = 'name'
_order = 'date_order desc'
- _rec_name: nom de l’atribut de l’objecte (classe) d’OpenERP que conté el nom informatiu de cada recurs (objecte) concret. Per defecte name.
- _sequence: nom de la seqüència SQL que gestiona els identificadors (contingut del camp _id) pels recursos d’aquest objecte (classe) d’OpenERP. Per defecte, nomDeLaTaula_id_seq.
- _sql: codi SQL a executar en la creació de l’objecte, en cas que _auto sigui True.
- _table: nom de la taula SQL si es vol que sigui diferent del nom automàtic a partir del nom de l’objecte substituint els punts per guions baixos.
Una vegada presentats els possibles atributs de les classes Python dissenyades per generar objectes (classes) d’OpenERP, ens cal endinsar-nos en els possibles continguts d’alguns d’aquests atributs.
Tipus de Camps
L’atribut _columns de la classe Python que defineix l’objecte (classe) d’OpenERP ha de contenir un diccionari Python amb la definició dels atributs de l’objecte d’OpenERP, a partir dels camps (fields) predefinits a la classe osv.
La sintaxi general per definir una columna és la següent:
'nom_camp':fields.tipus_de_camp(paràmetres)
on tipus_de_camp ha de ser un dels tipus facilitats per la classe osv i el contingut de paràmetres depèn del tipus del camp.
Hi ha diverses categories de camps:
- Camps simples: boolean, integer, float, char, text, date, datetime, binary i selection.
- Camps relacionals: many2one, one2many, many2many i related.
- Camps function.
- Camps property.
Els tipus de camps simples i relacionals habituals (many2one, one2many i many2many) ja ens són familiars a causa de la seva utilització en el disseny de mòduls amb l’eina de diagramació Dia. Per a tots aquests camps, el contingut de paràmetres en la crida fields.tipus_de_camp(paràmetres) es correspon amb el contingut de l’espai valor en la definició de cada atribut de classe mitjançant l’eina Dia.
Tipus relacional related
Els camps related permeten accedir a un camp d’un altre objecte referenciat des del propi objecte; és a dir, com si es tractés de camps encadenats.
Com a exemple, suposem que en un mòdul d’OpenERP tenim les classes exemplificades en el diagrama UML de la figura. En un mòdul per a OpenERP, a la classe City hi haurà un camp relacional many2one cap a la classe State, i a la classe State hi haurà un camp relacional many2one cap a la classe Country. Un camp related ens permet accedir des de la classe City a la classe Country via la classe State.
Figura Disseny UML amb dues relacions many2one
La definició d’una columna related ha de seguir la sintaxi:
'nom': fields.related('campPontPropi','campOnAccedir', type='tipusDelCamp', string='etiquetaNouCamp' [,store=True/False][,...]),
Els punts suspensius del final indiquen que hi pot haver més paràmetres segons el tipus del camp. Així, en els camps de tipus many2one, caldrà afegir un paràmetre relation per indicar el nom de la classe relacionada. El paràmetre store (per defecte False) permet indicar si el valor accedit ha de quedar enregistrat a la base de dades (cas clar de redundància) per tal de facilitar una major eficiència d’OpenERP en els processos de recerca i de lectura. En cas d’activar aquesta redundància, els valors sempre són mantinguts per OpenERP i mai pels usuaris.
Així, per aconseguir que la classe City de la figura pugui accedir a l’identificador de la classe Country via la classe State, hauríem de tenir:
# Dins la classe module.state: _columns: { ... 'country_id': fields.many2one('module.country','Country'), ... }
# Dins la classe module.city: _columns: { ... 'state_id': fields.many2one('module.state','State'), 'country_id': fields.related('state_id,'country_id', type='many2one', relation='module.country', string='Country',store=False), ... }
Un cas real d’utilització el podem observar en el disseny de l’objecte (classe) res.partner d’OpenERP, on veiem les columnes city, function, subname, phone, mobile, country i email com a camps related a través del camp address de la mateixa classe:
_columns = { ... 'address': fields.one2many('res.partner.address', 'partner_id','Contacts'), ... 'city': fields.related('address', 'city', type='char', string='City'), 'function': fields.related('address', 'function', type='char', string='function'), 'subname': fields.related('address', 'name', type='char', string='Contact Name'), 'phone': fields.related('address', 'phone', type='char', string='Phone'), 'mobile': fields.related('address', 'mobile', type='char', string='Mobile'), 'country': fields.related('address', 'country_id', type='many2one', relation='res.country',string='Country'), 'email': fields.related('address', 'email', type='char', size=240, string='E-mail'), ... }
Exemple d'utilització dels camps related:
L’objecte (classe) school.professor del mòdul school que estem millorant incorpora l’atribut partner_id que permet accedir a l’adreça del professor (objecte res.partner).
Imaginem-nos que és important, per a nosaltres, que el telèfon de cada professor es vegi directament a la fitxa del professor, sense haver d’accedir a la fitxa del partner.
Per aconseguir-ho, com que el telèfon del professor resideix a res.partner i ja accedim a aquest objecte a través del camp partner_id, podem definir el següent camp _related:
'phone':fields.related('partner_id','phone',type='char',size=64,string='Phone',store=False,readonly=True),
Tipus function
Els camps function simulen camps reals però es calculen mitjançant una funció Python enlloc d’emmagatzemar-se a la base de dades PostgreSQL. En casos especials, per augmentar la velocitat de consulta d’OpenERP i facilitar les consultes, es fan redundants a la base de dades, és a dir, s’emmagatzemen a la base de dades, però sempre són calculats i actualitzats a través de funcions i mai pels usuaris.
La definició d’una columna function ha de seguir la sintaxi:
'nom': fields.function(fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='Float', fnct_search=None, string='etiquetaNouCamp', store=False, multi=False [,...]),
en la qual:
- Els paràmetres que van acompanyats d’un símbol =Valor no són obligatoris i el valor indicat és el que es considera en cas de no explicitar el paràmetre.
- type: és el tipus de camp retornat per la funció. Pot ser qualsevol tipus excepte function. Els punts suspensius del final indiquen que hi pot haver més paràmetres segons el tipus del camp. Així, en els camps de tipus 'many2one' caldrà afegir un paràmetre obj per indicar el nom de la classe relacionada.
- store: per indicar si el camp ha de residir a la taula de la base de dades (redundància).
- multi: per indicar que el càlcul de la funció s’efectuï per a diversos camps, a causa que la mateixa funció és invocada en diferents camps (veure, com exemple, el mòdul auction d’OpenERP).
- fnct: és el mètode que calcula el valor del camp. És un paràmetre obligatori i ha d’existir abans de declarar el camp funcional. Ha de retornar, obligatòriament, un diccionari de parelles de la forma {id1:valor1, id2:valor2,…} en el qual id1, id2… han de ser identificadors d’objectes i valor1, valor2… els corresponents valors que han de correspondre amb el tipus indicat a type. La sintaxi ha de ser:
def fnct(self, cr, uid, ids, field_name, arg, context)
- fnct_inv: és un mètode utilitzat per escriure un valor en lloc del camp. La sintaxi ha de ser:
def fnct_inv(obj, cr, uid, id, name, value, fnct_inv_arg, context)
- fnct_search: és el mètode utilitzat per fer recerques per aquest camp. Ha de retornar la llista de tuples que especifiquin el criteri de cerca a utilitzar pel mètode search facilitat per OpenObject. La sintaxi ha de ser:
def fnct_search(obj, cr, uid, obj, name, args)
Els mòduls d’OpenERP estan plens d’exemples de camps funcionals, però no tots són senzills d’entendre en una primera aproximació al tema. Així, per iniciar-nos en els camps funcionals, podem centrar-nos en l’atribut number_of_days del mòdul hr_holidays. Seria interessant que, si no el teniu instal·lat, procedíssiu a instal·lar aquest mòdul, per poder comprovar el que comentarem a continuació.
Fixeu-vos en la part de la definició de la classe que ens interessa comentar:
class hr_holidays(osv.osv): ... def _compute_number_of_days(self, cr, uid, ids, name, args, context=None): result = {} for h in self.browse(cr, uid, ids, context=context): if h.type=='remove': result[h.id] = -h.number_of_days_temp else: result[h.id] = h.number_of_days_temp return result _columns = { ... 'type': fields.selection([('remove','Leave Request'), ('add','Allocation Request')], 'Request Type', required=True,readonly=True, ...), 'number_of_days_temp': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)]}), 'number_of_days': fields.function(_compute_number_of_days, string='Number of Days',store=True), ... } ...
El mètode _compute_number_of_days crida el mètode browse facilitat per OpenObject. La llista dels mètodes facilitats per OpenObject es troba a l’apartat “OpenERP: la vista i el controlador”.
Fixem-nos que number_of_days és un atribut funcional que, malgrat ser calculat mitjançant una funció, el seu resultat resta emmagatzemat a la base de dades, a causa de store=True. Podem comprovar, mirant la descripció de la taula hr_holidays a la taula de PostgreSQL, l’existència de la columna number_of_days.
Si utilitzeu el mòdul, observareu que permet gestionar, per cada empleat, els dies d’absència i els dies treballats fora del calendari laboral. Cada recurs (objecte) de l’objecte (classe) hr.holidays d’OpenERP correspon a un dels dos tipus possibles (add o remove) segons la definició de l’atribut type. L’atribut number_of_days_temp conté el nombre de dies afectats, sempre en positiu. En canvi, l’atribut number_of_days vol contenir el nombre de dies afectats, en positiu si es tracta de dies de treball addicional, i en negatiu si es tracta de dies d’absència.
En conseqüència, el camp number_of_days es pot calcular a partir del camp number_of_days_temp i per això es defineix com un camp funcional, que executa el mètode _compute_number_of_days definit prèviament.
Observem també que el mètode invocat retorna el diccionari anomenat result format per una única parella on la clau és l’identificador del recurs sobre el qual s’executa i el valor és el nombre de dies calculats.
Tipus property
Els camps property són camps dinàmics amb drets d’accés específics, que permeten contenir diferents valors en funció de la companyia.
class res_partner(osv.osv): _name = 'res.partner' _inherit = 'res.partner' _description = 'Partner' ... _columns = { ... 'property_account_payable': fields.property('account.account', type='many2one', relation='account.account', string="Account Payable", view_load=True, domain="[('type', '=', 'payable')]", help="This account will be used instead of the default one as the payable account for the current partner", required=True), 'property_account_receivable': fields.property('account.account', type='many2one', relation='account.account', string="Account Receivable", view_load=True, domain="[('type', '=', 'receivable')]", help="This account will be used instead of the default one as the receivable account for the current partner", required=True), ... }
Valors per defecte
La classe Python per definir els objectes d’OpenERP incorpora l’atribut opcional _defaults que permet la definició dels valors per defecte per un o diversos tipus de dades simples de l’objecte d’OpenERP.
Els valors per defecte dels camps d’un objecte d’OpenERP es defineixen en un diccionari de la forma:
_defaults = { 'nom_del_camp1': funció1 o valor1, 'nom_del_camp2': funció2 o valor2, ... }
Observem que els valors per defecte (sempre corresponents a tipus de dades simples) poden ser valors estàtics o valors dinàmics resultants de la crida de funcions que han d’estar declarades amb anterioritat a la definició del diccionari _defaults.
Les funcions per ser invocades en un valor per defecte dinàmic, han de contenir obligatòriament 4 paràmetres:
- obj: objecte osv corresponent al recurs que s’està creant.
- cr: cursor de base de dades.
- uid: ID de l’usuari que està donant d’alta el recurs.
- context: el context actual (facilitat pel client).
Els mòduls d’OpenERP estan plens d’exemples d’utilització de valors per defecte. Així, per exemple, observem els valors per defecte de l’objecte res.users (definit en el fitxer res_users.py dins el mòdul base) ideat per gestionar els usuaris de l’empresa gestionada per OpenERP:
_defaults = { 'password' : , 'context_lang': 'en_US', 'active' : True, 'menu_id': _get_menu, 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, 'menu_tips': False }
En aquesta definició observem la coexistència de valors estàtics (password, llenguatge, active i menu_tips) i valors dinàmics (menu_id, company_id, company_ids i groups_id). Si observeu la definició completa de la classe res.users podreu comprovar com abans de la definició del diccionari _defaults es troba la definició de diverses funcions, entre les quals hi ha les quatre funcions invocades des de _defaults (_get_menu, _get_company, _get_companies i _get_group). Veureu, també, que les quatre funcions tenen els paràmetres self, cr, uid i context.
En algunes ocasions, la definició d’un valor per defecte estàtic s’efectua amb la crida a una funció lambda de Python sense paràmetres. La definició dels valors per defecte estàtics a l’anterior exemple s’hagués pogut efectuar com:
_defaults = { 'password': lambda *a: , 'context_lang': lambda *a: 'en_US', 'active': lambda *a: True, 'menu_id': _get_menu, 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, 'menu_tips': lambda *a: False }
Per últim, per no veure’s obligats a crear funcions que només siguin invocades en un valor _defaults, es pot utilitzar les funcions lambda de Python en el mateix moment d’efectuar la crida, estalviant-nos la definició de la funció amb nom. En el següent codi corresponent al diccionari _defaults de la classe document.directory (fitxer document_directory.py del mòdul document) observem la definició de dues funcions lambda:
_defaults = { 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'document.directory', context=c), 'user_id': lambda self,cr,uid,ctx: uid, 'domain': '[]', 'type': 'directory', 'ressource_id': 0, 'storage_id': _get_def_storage, 'resource_find_all': True, }
Exemple d'utilització de l'atribut _defaults
L’objecte (classe) school.professor del mòdul school que estem millorant, incorpora l’atribut contract que consisteix en una selecció entre dos possibles valors. En el procés de creació d’un nou professor podem observar que el camp contract apareix sense valor per defecte. Volem definir que el valor per defecte d’aquest camp sigui trainee.
Així mateix, observem que l’objecte (classe) school.professor no incorpora el camp especial active que permet ocultar els recursos (objectes) de la classe quan ja no interessi, sense eliminar-los. Aprofitarem aquest exemple per afegir aquest camp i posar-li el valor 1 (cert) com a valor per defecte.
El codi de la classe school.professor modificada és:
class school_professor(osv.osv): """Professors""" _name = 'school.professor' _columns = { 'name': fields.char('Professor Name', size=64,required=True), 'contract': fields.selection([('trainee','Trainee'), ('named','Named')],'Contract'), 'partner_id': fields.many2one('res.partner', 'Associated Partner'), 'address_id': fields.many2one('res.partner.address', 'Private Address'), 'phone': fields.related('address_id','phone', type='char', string='Phone', store=False, readonly=True), 'hours_available': fields.integer('Hours Per Week'), 'course_ids': fields.one2many('school.course', 'prof_id', 'Courses'), 'active': fields.boolean('Active'), } _defaults = { 'contract': lambda *a: 'trainee', 'active': lambda *a: 1, } school_professor()
Observem que en la definició dels valors per defecte, hem utilitzat una funció lambda de Python, però no hagués estat necessari.
Una vegada actualitzat el mòdul school amb les noves millores, si obrim el formulari de manteniment de professors observarem que en donar d’alta un professor, en el camp Contract apareix per defecte el valor Trainee. En canvi, no apareix el nou camp Active, a causa que no hem modificat encara el formulari XML, però sí que podem veure la seva existència a la taula school_professor de la base de dades, amb valor True per tots els professors que ja existien a la taula i també pels nous professors.
Restriccions
Els objectes (classes) d’OpenERP poden incorporar, de forma opcional, restriccions d’integritat, addicionals a les que es puguin establir sobre la pròpia base de dades. OpenERP valida aquestes restriccions en les modificacions de dades i, en cas de violació, OpenERP mostra una pantalla d’error.
Les restriccions d’un objecte d’OpenERP es declaren a la classe Python que defineix l’objecte amb un atribut _constraints consistent en una llista de tuples, on cada tuple correspon a una restricció:
_constraints = [ (nomMètode,'missatgeError','llistaNomsCamps') (nomMètode,'missatgeError','llistaNomsCamps'), ... ]
Els tuples corresponents a una restricció venen definits per tres camps:
- nomMètode: és el nom del mètode de l’objecte utilitzat per validar la restricció. El seu prototipus ha de tenir la forma def _nomMètode (self, cr, uid, ids) i ha de retornar un valor booleà.
- missatgeError: és el missatge d’error que es mostrarà a l’usuari si la comprovació falla.
- llistaNomsCamps: és la llista dels noms dels camps que s’afegeixen al missatge d’error amb l’objectiu d’ajudar a l’usuari a entendre el motiu pel qual ha fallat la validació de la restricció.
Els mòduls d’OpenERP incorporen validacions _constraints en nombrosos llocs. Un cas el trobem en la validació dels comptes bancaris segons la normativa IBAN.
Codis IBAN i BIC
El codi IBAN (International Bank Account Number) s’utilitza per a identificar a nivell internacional un compte bancari. Sempre comença per dos caràcters que identifiquen el país i va seguit d’una seqüència de dígits. En el cas dels comptes bancaris espanyols, comença per ES i va seguit de 22 dígits, dels quals, els 2 primers són dígits de control i els altres 20 corresponen al CCC (Codi Compte Client) utilitzat des de molt de temps a Espanya.
El codi BIC (Bank Identifier Code) serveix per identificar una entitat bancària.
A la xarxa trobareu gran quantitat de documentació relativa als codis IBAN i BIC.
Fixem-nos en el següent fragment de codi de la classe Python res_partner_bank definida en el mòdul base_iban (fitxer base_iban.py):
_constraints = [ (check_iban, _construct_constraint_msg, ["iban"]), (_check_bank, '\nPlease define BIC/Swift code on bank for bank type IBAN Account to make valid payments', ['bic']) ]
L’anterior atribut _constraints inclou dues restriccions:
- check_iban: aquest és el nom del mètode declarat a la mateixa classe, que s’encarrega de validar el codi IBAN introduït per l’usuari. En cas que sigui erroni, la restricció informa que l’error es produeix en el camp iban i afegeix el missatge que retorna la crida al mètode _construct_constraint_msg (mètode que construeix el missatge ja que és força complex perquè difereix en cada país).
- _check_bank: aquest és el nom del mètode declarat a la mateixa classe que s’encarrega de validar el codi BIC introduït per l’usuari (obligatori en cas que l’usuari hagi indicat que està introduint un codi IBAN). En cas que sigui erroni, la restricció informa que l’error es produeix en el camp bic i mostra el missatge d’error que indica la restricció.
Per comprovar el funcionament d’aquestes restriccions, executeu els següents passos:
- Obriu el manteniment de clients i situeu-vos en un client qualsevol.
- Editeu-lo i aneu a la pestanya Comptabilitat (cal tenir instal·lat el mòdul de comptabilitat). Allà hi veureu una zona de línies anomenada Detalls del banc destinada a incloure els comptes bancaris del client.
- Procediu a donar d’alta un compte bancari amb els següents valors:
Tipus de compte de banc: Compte IBAN (del contrari no fa cap verificació) Número de compte: ES0012341234161234567890 Propietari compte bancari: seleccioneu el client al qui esteu assignant el compte Codi d’identificador bancari: introduïu el text Qualsevol
- Procediu a Desar i Tancar, fet que us portarà de nou a la fitxa del client, on veureu el compte bancari introduït i procediu a intentar enregistrar el client. En aquest moment, OpenERP comprova les restriccions introduïdes i, en aquest cas, com que els codis IBAN i BIC són erronis, apareix la pantalla d’error de la figura. Hi observem dos errors: el primer que ens informa de l’error en validar el camp iban i ens explica com ha de ser el contingut d’aquest camp. El segon ens informa que s’ha produït un error en validar el camp bic.
- Procediu a modificar el valor introduït en el camp iban amb el valor ES7712341234161234567890, que té un format IBAN correcte, tot i que gairebé segur que no existeix realment un CCC de codi 12341234161234567890. Torneu a intentar enregistrar el client. Observareu que ja només apareix l’error relatiu al camp bic.
- Per solucionar l’error del camp bic cal indicar el valor bic d’una de les entitats bancàries donades d’alta a l’empresa activa. Podeu donar d’alta les diverses entitats bancàries a través de l’opció Vendes | Configuració | Llibreta d’adreces | Bancs o, si ho preferiu, instal·leu el mòdul l10n_es_partner (Adaptación de partner para Estado Español) que, entre altres coses, facilita un assistent (Vendes | Configuració | Llibreta d’adreces | Import Bank Data Wizard) que afegeix dades de 191 bancs i caixes espanyoles a partir del registre oficial del Banc d’Espanya. Una vegada indiqueu un codi BIC existent a l’empresa OpenERP, podreu finalitzar l’enregistrament correcte del client.
La vista i el controlador
OpenERP és un programari de gestió empresarial desenvolupat sobre el framework OpenObject de tipus RAD (Rapid Application Development) que facilita una arquitectura MVC (model-vista-controlador) per als desenvolupaments.
Una vegada es domina el disseny del model de dades d’una aplicació desenvolupada sobre OpenObject, com és el cas d’OpenERP, cal entrar en el coneixement del disseny de la vista i del controlador.
En les aplicacions desenvolupades sobre el framework OpenObject, el concepte vista engloba les pantalles que permeten exposar la informació a l’usuari (anomenades vistes o views) per a visualitzar-la i/o editar-la, i els menús, que faciliten un accés organitzat a les vistes. En conseqüència, ens cal aprendre a dissenyar les vistes (views) i els menús.
En el framework OpenObject el disseny del controlador s’efectua amb el llenguatge Python, tot ampliant les classes Python que defineixen el model de dades, amb la incorporació de mètodes.
Els menús i vistes dissenyats en arxius XML, en instal·lar-se en una empresa d’OpenERP, obtenen un identificador numèric dins l’empresa i que OpenERP utilitza per a la gestió de l’aplicació. Aquest identificador acostuma a ser diferent a cada empresa, ja que no totes les empreses tenen els mateixos mòduls instal·lats ni han estat instal·lats en el mateix ordre. A causa que és molt possible que en la fase de disseny el dissenyador hagi de fer referència a menús i vistes es fa necessari un identificador XML per a cada vista/menú que el dissenyador pugui utilitzar. Per això, en la definició XML de menús i vistes s’inclou un identificador (text) que ha de ser únic dins el mòdul i al qual es pot fer referència des del propi mòdul a través del seu nom o des de qualsevol altre mòdul m a través de la sintaxi m.identificador.
Els menús
Les aplicacions desenvolupades amb el framework OpenObject contenen el model, la vista i el controlador seguint el patró de disseny MVC. Dins la vista s’inclouen les diverses pantalles que permeten gestionar la informació i els menús que permeten un accés organitzat a les vistes.
Els menús d’un mòdul OpenObject es dissenyen en arxius XML i han d’estar referenciats des de l’apartat update_xml del fitxer descriptor del mòdul __openerp__.py. Els arxius XML acostumen a incorporar les pantalles (views) i els menús que permeten accedir a les pantalles. Els dos tipus d’elements, però, podrien residir en fitxers XML específics (un per menús i un per views).
Com a exemple, podem analitzar el mòdul school generat per l’eina Dia, en el qual observem la presència del fitxer anomenat school_view.xml:
"update_xml" : ['school_view.xml'],
Els menús d’OpenObject i les seves opcions han de tenir una declaració similar a:
<menuitem id="menuitem_id" name="títol_del_menú" action="action_id" icon="nom_de_icona" web_icon="nom_icona_web" web_icon_hover="nom_icona_web_flotant" parent="menuitem_id" groups="grups_usuaris" sequence="<integer>" />
en la qual els valors específics de cada menú són:
- Atribut id: conté l’identificador XML del menú, únic dins el mòdul.
- Atribut name: conté el títol del menú. Aquest camp és opcional i, si no s’indica, el menú agafarà el nom de l’acció que executi el menú.
- Atribut action: especifica l’identificador de l’acció que executa el menú i que ha d’existir en el mòdul. Quan no s’especifica l’acció s’entén que es tracta de l’arrel d’un menú que conté altres menús i/o opcions. El disseny de la corresponent acció depèn del tipus d’execució associada (obrir una finestra, executar un informe, posar en marxa un assistent…).
- Atribut icon: especifica la icona que acompanya al menú en el client GTK. La icona per defecte és una carpeta. La llista d’icones possibles es pot consultar en el desplegable Icona del manteniment de menús accessible a través de Settings | Personalització | Intefície d’usuari | Elements menú.
- Atribut web_icon: especifica la icona que es mostra en el client web, a la pàgina inicial (Home) .
- Atribut web_icon_hover: especifica la icona que es mostra en el client web, a la pàgina inicial, en passar el ratolí pel damunt. Acostuma a ser la versió acolorida de la icona especificada a l’atribut web_icon.
- Atribut parent: especifica l’identificador del menú pare del qual depèn. Si no es defineix, indica que es tracta d’un menú arrel (menú principal).
- Atribut groups: especifica quin grup d’usuaris pot veure el menú. Si es desitja introduir més d’un grup, cal separar-lo per comes (per exemple, groups="admin,user").
- Atribut sequence: és un enter que s’utilitza per ordenar el menú dins l’arbre de menús, de manera que a major valor, més avall apareix el menú. Aquest valor no és obligatori i per defecte pren el valor 10. Els menús que tinguin el mateix valor s’ordenen per moment temporal de creació.
Les icones dels atributs web_icon o web_icon_hover han de residir a la carpeta del mòdul i el seu nom ha d’anar acompanyat del camí que indiqui la subcarpeta (normalment images o icons) en la qual resideixen (o sense camí si no es troben a cap subcarpeta).
Accions en OpenObject
Les accions defineixen el comportament del sistema en resposta als esdeveniments generats per l’usuari, ja sigui en seleccionar una opció de menú, en prémer un botó, en fer clic damunt un registre…
Hi ha diferents tipus d’accions:
- Window: per obrir una finestra.
- Report: per imprimir un informe.
- Wizard: per iniciar un assistent vinculat a un treball o procés.
- Execute: per executar un mètode en el servidor.
- Group: per reunir un conjunt d’accions en un grup.
Exemple de menús en el mòdul school
Els menús originals del menú school generats per l’eina Dia per a la versió 5.0 d’OpenERP no són vàlids per a la versió 6.1 d’OpenERP, de manera que ens veiem obligats a refer-los segons les normes anteriors. Així, una possibilitat és la següent:
<menuitem name="Courses" id="menu_school"/> <menuitem name="Calendar of Courses" id="menu_school_event" action="action_school_course_event" parent="menu_school"/> <menuitem name="Students" id="menu_school_student" action="action_school_student" parent="menu_school"/> <menuitem name="Configuration" id="menu_school_configuration" parent="menu_school"/> <menuitem name="Courses" id="menu_school_course" action="action_school_course" parent="menu_school_configuration"/> <menuitem name="Professors" id="menu_school_professor" action="action_school_professor" parent="menu_school_configuration"/>
En els elements menuitem anteriors detectem dues definicions de menús i quatre definicions d’opcions que executen accions. Cap dels dos menús incorpora l’atribut icon i, en conseqüència, en el client GTK la icona que els acompanya és una carpeta. El menú principal menu_school tampoc incorpora els atributs web_icon i/o web_icon_hover i, per tant, el mòdul Courses no va acompanyat de cap icona a la pàgina inicial del client web.
Vistes
Les vistes d’OpenERP són les pantalles que faciliten l’accés de l’usuari a la informació, tant per consultar-la com per modificar-la (altes, baixes i modificacions).
OpenObject facilita diversos tipus d’interfícies per facilitar l’accés de l’usuari a la informació (formularis, llistes, diagrames, gràfics, calendaris, arbres i targetes kanban) i totes elles són dinàmiques.
Les vistes d’OpenERP són dinàmiques i es construeixen en temps d’execució a partir de descripcions XML accessibles des del client, fet que en possibilita, fins i tot, la modificació en temps d’execució.
En cas que una vista es modifiqui en temps d’execució (des del client web activant el mode de desenvolupament, o des del client GTK, pel menú Settings | Personalització | Interfície d’usuari | Vistes), simplement cal tancar-la i reobrir-la per observar els canvis.
La descripció XML per les vistes associades a un objecte (classe) d’OpenERP, resideix en fitxers XML dins el mòdul corresponent a l’objecte. En cas de modificació en temps d’execució, el fitxer XML no es modifica i això cal tenir-ho molt present, ja que qualsevol procés d’actualització del mòdul provocarà la pèrdua dels canvis efectuats.
Per evitar la pèrdua de les modificacions efectuades a les vistes corresponents a mòduls susceptibles de ser actualitzats periòdicament (mòduls oficials i extres de la comunitat), el camí és crear un nou mòdul i definir-hi la nova vista com a herència de la vista a modificar, tot introduint els canvis en la vista heretada.
La descripció de les vistes ha de residir en un fitxer XML que ha d’estar referenciat des de l’apartat update_xml del fitxer descriptor del mòdul __openerp__.py. Així, en el mòdul school hi observem la presència del fitxer anomenat school_view.xml i en el fitxer __openerp__.py hi trobem la línia:
"update_xml" : ['school_view.xml'],
El nom del fitxer XML en el qual resideixen les vistes pot ser qualsevol, però és altament aconsellable utilitzar alguna combinació en la qual aparegui el nom del mòdul i quelcom que indiqui el seu contingut (com per exemple el mot view).
La declaració d’un vista ha de ser similar a:
<record model="ir.ui.view" id="view_id"> <field name="name">view.name</field> <field name="model">object_name</field> <!-Valors possibles: tree,form,calendar,search,graph,gantt,kanban--> <field name="priority" eval="16"/> <field name="arch" type="xml"> </field> </record>
en el qual els valors específics de cada vista són:
- Element record que conté l’atribut model amb valor ir.ui.view obligatori per a les vistes, i l’atribut id amb l’identificador XML de la vista dins el mòdul.
- Element field name="name" amb el nom de la vista.
- Element field name="model" amb el nom de l’objecte (classe) d’OpenERP sobre el qual es defineix la vista.
- Element field name="priority" amb la prioritat de la vista. Com més petita, més prioritat. Per defecte: 16.
- Element field name="arch" type="xml" amb l’arquitectura o estructura de la vista que es defineix mitjançant diverses etiquetes XML. Aquesta estructura és diferent segons el tipus de vista (tree, form, calendar, search, graph, gantt, kanban).
Així doncs, cal endinsar-nos en l’estructura dels diversos tipus de vista i en els mecanismes (accions) que possibiliten a l’usuari la seva execució.
Accions Window
Una vista d’OpenObject entra en execució a partir d’un esdeveniment d’usuari (seleccionar una opció de menú, prémer un botó…) que està vinculat a una acció window, responsable de la posada en marxa de la vista.
L’element field name="type" de la declaració d’una vista obliga a definir la vista amb un dels tipus següents: tree, form, calendar, search, graph, gantt, kanban. Cal tenir en compte que sota el tipus tree s’aixopluguen dos tipus diferents: llista jerarquitzada (normalment anomenada arbre -tree-) i llista no jerarquitzada (normalment anomenada llista -list-).
De la mateixa manera que els menús i les vistes, les accions (no només les window) es declaren en arxius XML que han d’estar referenciats des del fitxer descriptor del mòdul. A causa que les vistes es posen en marxa a través d’accions i que hi ha menús que porten associades aquestes accions, és molt comú (però no obligatori) introduir en el fitxer XML les declaracions en el següent ordre:
- Vistes vinculades a un objecte (acostuma a haver-n’hi diverses).
- Accions que executen vistes (pot ser única i que accioni diverses vistes).
- Menús per facilitar a l’usuari l’execució d’accions.
Exemple de vistes, accions i menús relacionats
Fixem-nos en els següents fragments de vistes, accions i menús, en el mòdul school (versió school_04) corresponents a l’objecte professor:
<record model="ir.ui.view" id="view_school_professor_form"> <field name="name">school.professor.form</field> <field name="model">school.professor</field> <field name="type">form</field> <field name="arch" type="xml"> ... <record model="ir.ui.view" id="view_school_professor_tree"> <field name="name">school.professor.tree</field> <field name="model">school.professor</field> <field name="type">tree</field> <field name="arch" type="xml"> ... <record model="ir.actions.act_window" id="action_school_professor"> <field name="name">Professors</field> <field name="res_model">school.professor</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record> <menuitem name="Professors" id="menu_school_professor" action="action_school_professor" parent="menu_school_configuration"/>
Hi observem:
- Dues vistes: view_school_professor_form (de tipus form) i view_school_professor_tree (de tipus tree).
- Una acció: action_school_professor, que no està associada a cap de les dues vistes. Quina s’executa, doncs, en activar l’acció?
- Un menú: menu_school_professor, associat a l’acció anterior.
En executar, des d’OpenERP, l’opció de menú Professors, observem com apareixen els professors en format llista (tree) i que la vista formulari (form) també es pot seleccionar.
Els elements field name="view_type" i field name="view_mode" defineixen, quan no s’explicita la vista a executar (com és el cas), el comportament d’OpenObject per escollir la vista.
L’exemple anterior serveix per il·lustrar que en OpenObject hi ha certs comportaments per defecte que s’activen quan el programador no els ha explicitat, com és el cas de l’elecció de la vista a executar quan el programador no la indica.
Abans de presentar com és la declaració d’una acció window és molt convenient entendre el comportament d’OpenObject en l’elecció i visualització de les possibles vistes sobre un objecte.
OpenObject categoritza totes les vistes (tree, form, calendar, search, graph, gantt i kanban) en dos grans grups:
- tree per les vistes jeràrquiques, que permeten la visualització de dos tipus de vistes:
- tree: visualització arbre
- form: visualització formulari
- form per les vistes no jeràrquiques, que permeten la visualització de diversos tipus de vistes:
- tree: visualització llista
- form: visualització formulari
- calendar: visualització calendari
- graph: visualització gràfic
- gantt: visualització diagrama de gantt
- search: visualització d’una zona de filtres
- kanban: visualització dels recursos com a targetes agrupades sota un criteri, que poden ser editables i/o arrossegables
En la definició d’una acció window s’ha d’indicar si correspon a una vista jeràrquica (categoria tree) o una vista no jeràrquica (categoria form) i, una vegada definida la categoria, cal indicar –en l’ordre que interessi– els tipus de vista (adequats a la categoria) que permetrà visualitzar. Això s’aconsegueix amb dos elements que formen part de la definició XML d’una acció window:
- Element field name="view_type" amb una de les dues categories possibles: form o tree.
- Element field name="view_mode" amb una seqüència ordenada dels tipus de vista que facilita l’acció.
Els valors de view_mode, separats per comes, han de ser coherents amb el valor de view_type i, a més, el mòdul ha d’incorporar alguna vista per cadascun dels tipus indicats a la seqüència de view_mode. En cas que per algun tipus de vista dels indicats a view_mode, hi hagi diverses vistes definides en el mòdul, OpenObject escollirà la vista més prioritària (element priority de la definició de les vistes).
Exemple de view_type i view_mode en la definició d'accions window
En el mòdul school (versió school_04) observem el següent fragment d’acció window:
<record model="ir.actions.act_window" id="action_school_professor"> <field name="name">Professors</field> <field name="res_model">school.professor</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record>
En aquest cas, a causa que view_type té el valor form, l’element view_mode podria contenir, en principi, qualsevol seqüència ordenada dels valors tree, form, calendar, search, graph, gantt i kanban. Fixem-nos que la seqüència és "tree,form" i, en conseqüència, en executar l’acció (manteniment de professors), les dues vistes possibles són llista i formulari i el manteniment s’obre en vista llista. Si el contingut de view_mode hagués estat "form, tree" les vistes possibles haguessin estat les mateixes, però el manteniment s’obriria en mode formulari buit. Per tal que tot funcioni, el mòdul facilita per a l’objecte school.professor, com a mínim, una vista de tipus form i una vista de tipus tree no jeràrquica:
- view_school_professor_form per a la vista form
- view_school_professor_tree per a la vista tree no jeràrquica
En el mòdul school (versió school_04) observem el següent fragment d’acció window:
<record model="ir.actions.act_window" id="action_school_course"> <field name="name">Courses</field> <field name="res_model">school.course</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar</field> </record>
Aquest és un altre cas en el qual view_type té el valor form, però, en canvi, l’element view_mode conté la seqüència "tree,form,calendar" i, en conseqüència, en executar l’acció (manteniment de cursos), tenim tres vistes possibles (llista, formulari i calendari) i el manteniment s’obre en vista llista. Ja que l’acció indica tres vistes possibles, el mòdul hauria de contenir, com a mínim, una vista de cada tipus, i en aquest cas, l’eina Dia, que ha generat l’element view_mode amb els tres tipus de vista, només facilita dues vistes:
- view_school_course_form per a la vista form
- view_school_course_tree per a la vista tree no jeràrquica
Al no crear una vista calendar, el tercer element del view_mode no funciona. Caldría fer una vista també per al calendar.
La declaració d’un acció window ha de ser similar a:
<record model="ir.actions.act_window" id="action_id"> <field name="name">action.name</field> <field name="view_type">form|tree</field> <field name="view_mode">...</field> <field name="view_id" ref="nomVista"/> <field name="search_view_id" ref="nomVistaDeCerca"/> <field name="domain"> ["list of 3-tuples (max 250 characters)"] </field> <field name="context"> {"context dictionary (max 250 characters)"} </field> <field name="res_model">Open.object</field> <field name="target">new</field> </record>
en la qual els valors específics per a cada acció són:
- Element record que conté l’atribut model amb valor ir.actions.act_window obligatori per a les accions window i l’atribut id amb l’identificador XML de l’acció dins el mòdul.
- Element field name="name" amb el nom de l’acció. És obligatori. Els menús sense name que invoquin l’acció, prenen el name de l’acció com a name propi.
- Elements view_type i view_mode, estudiats prèviament.
- Element field name="view_id" amb l’atribut ref que ha de contenir el nom de la vista per mostrar quan s’activa l’acció. En cas que aquest camp no estigui definit, OpenObject decideix en funció del contingut de l’element view_mode.
- Element field name="search_view_id" amb l’atribut ref que ha de contenir el nom de la vista de cerca (capçalera de les pantalles que mostren una llista de registres, per facilitar-ne el filtrat) per mostrar quan s’activa l’acció.
- Element res_model amb el nom de l’objecte sobre el qual opera l’acció.
- Element domain amb una llista Python de condicions utilitzada per refinar els resultats d’una selecció i, en conseqüència, mostrar menys recursos a la vista. Les condicions de la llista s’enllacen amb una clàusula AND i són tuples Python de tres valors ('camp','condició','valor').
- Element context amb un diccionari contextual que s’utilitzarà a la vista que s’obri en executar l’acció. Els diccionaris contextuals es declaren com un diccionari Python. Aquests diccionaris contenen informació de context a utilitzar, com per exemple l’idioma, la moneda, la tarifa, el magatzem…
- Element target per indicar on s’ha d’obrir la finestra. Valors possibles: new (en una nova finestra), current (substituint la finestra actual) i inline (dins la finestra actual).
Vistes Form
La vista formulari per a un objecte d’OpenObject consisteix en la correcta distribució en la pantalla dels camps de l’objecte amb l’objectiu de facilitar-ne la visualització i/o l’edició d’un recurs. L’element arrel de l’XML que defineix l’arquitectura de la vista és form.
Els camps en una vista formulari d’OpenObject sempre estan distribuïts en la pantalla segons les següents normes:
- Per defecte cada camp va precedit d’una etiqueta que és el seu nom.
- Els camps se situen a la pantalla d’esquerra a dreta i de dalt a baix, segons l’ordre en què estan declarats en el fitxer XML que descriu la vista.
- Cada pantalla es troba dividida en 4 columnes, cadascuna de les quals pot contenir una etiqueta o un camp. Ja que cada camp és precedit (per defecte) per una etiqueta amb el seu nom hi haurà dos camps amb les seves respectives etiquetes a cada línia de la pantalla.
OpenObject permet:
- Que un camp pugui ocupar més d’una columna, tot utilitzant l’atribut colspan.
- Agafar un grup de columnes i dividir-les en les columnes que es desitgi, tot utilitzant l’etiqueta group i els atributs colspan i col.
- Distribuir els camps d’un objecte en diverses pestanyes, tot utilitzant les etiquetes notebook i page.
Exemple:
<record model="ir.ui.view" id="view_school_professor_form"> <field name="name">school.professor.form</field> <field name="model">school.professor</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="school.professor"> <field name="name" select="1"/> <field name="contract" select="2"/> <field name="partner_id" select="0"/> <field name="address_id" select="0"/> <field name="phone" select="0"/> <field name="hours_available" select="0"/> <field name="course_ids" colspan="4" select="0"/> </form> </field> </record>
En aquest cas, detectar dins el fitxer school_view.xml el registre corresponent a la vista no ha estat difícil, però en altres casos en els quals hi ha moltes vistes pot ser més dificultós. En canvi, des del client web, tenint obert el formulari i el mode de desenvolupament activat, podem demanar d’editar la vista, obtenint el formulari de la figura.3, en la qual observem el nom de la vista i, a més, podem modificar-la i enregistrar els canvis en el servidor OpenERP.
Fixem-nos que el contingut XML de l’element arch no és altre que el conjunt de camps a visualitzar, de l’objecte (classe) d’OpenERP en el qual es basa la vista. Recordem la definició de l’objecte school.professor (versió school_04):
class school_professor(osv.osv): _name = 'school.professor' _columns = { 'name': fields.char(...), 'contract': fields.selection(...), 'partner_id': fields.many2one(...), 'address_id': fields.many2one(...), 'phone': fields.related(...), 'hours_available': fields.integer(...), 'course_ids': fields.one2many(...), 'active': fields.boolean('Active'), } ...
La definició de la classe Python school_professor incorpora el camp active que no apareix a la vista school.professor.form. Això és a causa que la vista school.professor.form que estem analitzant va ser generada per l’eina Dia i el camp active el vam incorporar posteriorment en el codi Python sense modificar la corresponent vista.
Suposem que volem situar el camp active, que correspon a una casella de verificació, a la 4a fila del formulari. Simplement l’haurem de situar en 7è lloc dins l’XML de l’element arch, entre els camps hours_available i course_ids. Si efectuem la modificació en el fitxer school_view.xml caldrà actualitzar el mòdul; en canvi, si efectuem la modificació des dels clients web o GTK només cal recarregar el formulari. Sigui quin sigui el camí, observarem el camp active amb etiqueta Active a la 4a línia de la pantalla.
En el formulari school.professor.form anterior també podem observar com el camp one2many de nom course_ids va acompanyat de l’atribut colspan=4 que indica que el camp (conjuntament amb la seva etiqueta Courses) ha d’ocupar les 4 columnes de la pantalla.
Dins l’estructura del formulari (contingut de l’element field name="arch") hi pot haver diversos tipus d’elements i aquests elements poden tenir diversos atributs. La taula.1 mostra els atributs comuns als diversos tipus d’elements possibles i la taula mostra els diversos tipus d’elements possibles acompanyats dels atributs específics de cada tipus d’element.
Taula Relació d’atributs comuns pels elements de la definició d’una vista form
Atribut | Utilització |
---|---|
string
|
Etiqueta de l’element que substitueix l’etiqueta definida a la classe. També s’utilitza en els processos de recerca. |
nolabel
|
Permet amagar (valor="1" ) l’etiqueta del camp.
|
colspan
|
Permet indicar el nombre de columnes que ha de tenir el camp. |
rowspan
|
Permet indicar el nombre de files que ha de tenir el camp. |
col
|
Permet indicar el nombre de columnes en què es divideix l’espai assignat al camp. |
invisible
|
Permet ocultar (valor="1" ) el camp i la seva etiqueta.
|
eval
|
Cadena avaluada com a codi Python per obtenir el valor del camp. |
attrs
|
Permet indicar sota quines condicions dinàmiques, basades en els valors d’altres camps del formulari, l’element ha de ser readonly i/o invisible i/o required . Ha de seguir el format:{'atribut':[('nomCamp','operador','valor'), ('nomCamp','operador','valor')...],...} on |
Exemples d'utilització de l'atribut attrs en la ubicació dels camps en vistes
En els mòduls d’OpenERP es pot veure la utilització de l’atribut attrs en múltiples vistes. Per exemple, en la vista Leave Request del mòdul hr_holidays (fitxer hr_holidays_view.xml):
<field name="name" attrs="{'readonly':[('state','!=','draft'), ('state','!=','confirm')]}"/>
El camp name serà de només lectura quan el camp state no tingui els valors draft ni confirm.
<field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly':[('state','!=','draft')]}"/>
El camp category_id serà obligatori quan el camp holiday_type valgui category i de només lectura quan el camp state no valgui draft.
Taula Relació dels elements possibles en la definició d’una vista form
Element | Utilització |
---|---|
field
|
Camp per visualitzar, d’entre les columnes definides a l’objecte (classe) OpenERP en la qual es basa la vista. Cada camp porta un giny (widget) associat segons el tipus de dada del camp. Així, si el camp és de tipus |
button
|
Giny (widget) en forma de botó per ser premut i que està associat a determinades accions. La taula</a> mostra els atributs específics d’aquest element. |
separator
|
Línia de separació horitzontal per estructurar vistes, amb etiqueta opcional. |
newline
|
Permet afegir espai en blanc per completar la línia actual de la vista i saltar a la següent. |
label
|
Títol de text lliure en el formulari. |
group
|
Permet organitzar els camps en grups amb etiquetes opcionals. Aporta un requadre al voltant del grup. |
notebook page
|
Un element notebook és un contenidor de pestanyes, definides cadascuna per un element page .Els atributs específics d’aquest element són:
|
Taula Relació d’atributs específics per l’element field en la definició d’un form
Atribut | Significat |
---|---|
select
|
1 per mostrar el camp en cerques normals i 2 per mostrar el camp només en cerques avançades. A partir de la versió 6 d’OpenERP, les cerques avançades han deixat d’existir i, en canvi, els clients faciliten l’opció de filtres avançats en els quals es pot escollir qualsevol dels camps del model. Per tant, el valor 2 és obsolet a partir de la versió 6 d’OpenERP. |
required
|
Substitueix l’atribut required definit en el model.1 per tal que el camp sigui obligatori. |
readonly
|
Substitueix l’atribut readonly definit en el model.1 per tal que el camp sigui de només lectura. |
default_focus
|
Valor 1 per indicar que aquest camp ha de tenir el focus en obrir el formulari. Només hi pot haver un element amb aquest atribut a 1 .
|
password
|
True per ocultar els caràcters que s’introdueixen en el camp.
|
context
|
Codi Python per definir un diccionari contextual. |
domain
|
Codi Python per definir una llista de tuples amb condicions per restringir valors, en camps relacionals. |
on_change
|
Mètode Python que s’executa en canviar el valor del camp. S’utilitza per calcular automàticament el valor d’altres camps quan el camp actual canvia. |
groups
|
Llista Python d’identificadors de grups d’usuaris autoritzats a visualitzar el camp. |
widget
|
Giny alternatiu al proporcionat de forma automàtica segons el tipus del camp. Valors possibles:
|
Taula Relació d’atributs específics per l’element button en la definició d’un form
Atribut | Significat |
---|---|
type
|
Tipus de botó. Valors possibles:
|
name
|
Segons el valor de l’atribut type :
|
special
|
Únicament és operatiu en una finestra emergent (pop-up) i en cas d’utilitzar-lo en una finestra no emergent, no és operatiu. Només té un possible valor: |
confirm
|
Text de missatge de confirmació per quan es prem el botó. |
states
|
Llista d’estats, separats per comes, en els quals el botó es mostra. Si aquest atribut no apareix, el botó és sempre visible. |
icon
|
Nom d’icona. Per defecte, el botó és textual. Es pot utilitzar qualsevol de les icones existents a la subcarpeta |
default_focus
|
Valor a 1 per indicar que aquest botó ha de tenir el focus en obrir el formulari. Només hi pot haver un element amb aquest atribut a 1. |
Vistes Tree
Les vistes arbre/llista per a un objecte d’OpenObject consisteixen en la distribució de la pantalla en línies amb l’objectiu de facilitar la visualització i/o l’edició d’un conjunt de recursos de l’objecte. La majoria de les vistes arbre/llista són de només visualització, però OpenObject permet fer-les editables.
La vista arbre mostra els recursos de l’objecte seguint una estructura jeràrquica, mentre que la vista llista mostra els recursos en seqüència, sense cap jerarquia.
En OpenERP tenim una gran quantitat de vista llista com, per exemple, en accedir a l’accés directe Clients, en el qual se’ns presenta els clients en vista llista. OpenERP també inclou exemples de vista arbre; un exemple el trobem en accedir a l’opció de menú Magatzem | Productes | Productes per categoria.
Les vistes arbre/llista es distingeixen perquè en la seva declaració incorporen:
<field name="type">tree</field>
i l’element arrel de l’XML que defineix l’arquitectura de la vista és tree.
La diferència entre les vistes arbre i vistes llista en la declaració radica en l’existència, en les vistes arbre, del següent element per indicar el camp one2many que determina la jerarquia:
<field name="field_parent">camp_one2many</field>
Les vistes arbre/llista són més simples que les vistes formulari i tenen menys opcions. De manera similar a les vistes formulari, incorporen en la seva estructura elements field. La taula.5 recull els atributs que poden acompanyar l’element arrel tree.
Taula Relació d’atributs per l’element tree en la definició d’un tree
Atribut | Significat |
---|---|
colors
|
Llistes de colors definides en un diccionari Python per tal que les línies es puguin acolorir segons diferents condicions. |
editable
|
En cas d’estar definit, els valors possibles són top i bottom i permeten editar els registres directament a la llista, sense necessitat de passar a la vista formulari. Si el valor és top , els nous registres s’afegeixen al capdamunt de la llista i si el valor és bottom , els nous registres s’afegeixen al final.
|
toolbar
|
Només per a les vistes arbre. Valor a |
Exemple d'anàlisi del codi de la vista arbre Productes per categoria
El codi de la vista arbre Productes per categoria és:
<record id="product_category_tree_view" model="ir.ui.view"> <field name="name">product.category.tree</field> <field name="model">product.category</field> <field name="type">tree</field> <field name="field_parent">child_id</field> <field name="arch" type="xml"> <tree toolbar="True" string="Product Categories"> <field name="name"/> </tree> </field> </record>
Veiem que l’element field_parent informa que l’arbre es munta a partir de la jerarquia indicada pel camp child_id. Fixem-nos com és la definició de la classe product.category:
class product_category(osv.osv): ... _name = "product.category" _description = "Product Category" _columns = { ... 'parent_id': fields.many2one('product.category', 'Parent Category', select=True, ondelete='cascade'), 'child_id': fields.one2many('product.category', 'parent_id', string='Child Categories'), ...
Si modifiquem la vista product_category_tree_view eliminant l’atribut toolbar, cosa que podem aconseguir ràpidament des del client web, i tornem a demanar l’opció Productes per categoria, veurem que el desplegable per escollir el recurs de primer nivell de la jerarquia ha desaparegut i directament ens facilita la llista de tots els recursos de primer nivell.
Exemple d'anàlisi de la vista llista Productes
Quan accedim a Productes (ja sigui amb la drecera o des de l’opció de menú), apareixen els productes en vista llista. La majoria dels productes apareixen de color negre, però n’hi ha alguns en vermell i alguns en blau. Això és a causa que s’està utilitzant l’atribut colors en la definició de l’element tree.
El codi de la vista llista és:
<record id="product_product_tree_view" model="ir.ui.view"> <field name="name">product.product.tree</field> <field name="model">product.product</field> <field name="type">tree</field> <field eval="7" name="priority"/> <field name="arch" type="xml"> <tree colors="red:virtual_available<0; blue:virtual_available>=0 and state in ('draft','end','obsolete'); black:virtual_available>=0 and state not in ('draft','end','obsolete')" string="Products"> <field name="default_code"/> <field name="name"/> <field name="categ_id" invisible="1"/> <field name="variants" groups="product.group_product_variant"/> <field name="uom_id" string="UoM"/> <field name="type"/> <field name="qty_available"/> <field name="virtual_available"/> <field name="lst_price"/> <field name="price" invisible="not context.get('pricelist',False)"/> <field name="standard_price" groups="base.group_extended"/> <field name="state" groups="base.group_extended"/> <field name="company_id" groups="base.group_multi_company" invisible="1"/> </tree> </field> </record>
Observem-hi el contingut de l’atribut colors a l’element tree: es defineix el color que ha de mostrar el registre en funció dels valors dels camps virtual_available i state.
Si voleu que la llista sigui editable només heu d’afegir l’atribut editable a l’element tree, amb el valor top o bottom segons vulgueu que els nous registres s’afegeixin per dalt o per baix. Podeu comprovar-ho, de manera ràpida, modificant la vista des del client web.
Els ginys one2many i many2many emprats en les vistes form mostren la informació de la vista llista associada als objectes referenciats pels camps one2many i many2many.
L’element tree també s’utilitza per modificar l’estructura dels ginys one2many i many2many en una vista form. Per aconseguir-ho, a l’element field corresponent al camp one2many o many2many se li ha d’afegir un element fill tree, que haurà de contenir els elements fields que es vulguin visualitzar en el giny. És a dir, mentre que el funcionament automàtic dels ginys one2many i many2many mostra la vista llista associada a l’objecte referit en el camp one2many i many2many, amb l’element tree podem alterar-la i mostrar aquells camps que interessi i amb les característiques adequades (ordre, color…).
Cal tenir en compte, també, que si en un element tree explicita una columna one2many o many2many, OpenERP hi visualitzarà el nombre de recursos (registres) que en aquell moment estan referenciats.
De la mateixa manera que en un camp one2many o many2many d’una vista form, la utilització de l’element tree permet explicitar les columnes a visualitzar (exemples anteriors), ja que si no s’utilitza OpenERP mostra tots els camps dels objectes referenciats, pot interessar que l’edició en vista form de qualsevol dels recursos mostrats en un giny one2many rebi algun camp ja emplenat i no sigui editable.