[DOC] [ADD] Added architecture chapter.
[odoo/odoo.git] / doc / 02_architecture.rst
1 ========================================
2 Architecture
3 ========================================
4
5 MVC architecture
6 ================
7
8 According to `Wikipedia <http://en.wikipedia.org/wiki/Model-view-controller>`_, "a Model-view-controller (MVC) is an architectural pattern used in software engineering". In complex computer applications presenting lots of data to the user, one often wishes to separate data (model) and user interface (view) concerns. Changes to the user interface does therefore not impact data management, and data can be reorganized without changing the user interface. The model-view-controller solves this problem by decoupling data access and business logic from data presentation and user interaction, by introducing an intermediate component: the controller.
9
10 .. figure::  images/MVCDiagram.png
11    :scale: 100
12    :align: center
13
14    MVC Diagram
15
16 For example in the diagram above, the solid lines for the arrows starting from the controller and going to both the view and the model mean that the controller has a complete access to both the view and the model. The dashed line for the arrow going from the view to the controller means that the view has a limited access to the controller. The reasons of this design are :
17
18     * From **View** to **Model** : the model sends notification to the view when its data has been modified in order the view to redraw its content. The model doesn't need to know the inner workings of the view to perform this operation. However, the view needs to access the internal parts of the model.
19     * From **View** to **Controller** : the reason why the view has limited access to the controller is because the dependencies from the view to the controller need to be minimal: the controller can be replaced at any moment. 
20
21 MVC Model in OpenERP
22 --------------------
23
24 In OpenERP, we can apply this model-view-controller semantic with
25
26     * model : The PostgreSQL tables.
27     * view : views are defined in XML files in OpenERP.
28     * controller : The objects of OpenERP. 
29
30
31 MVCSQL
32 ------
33
34 Example 1
35 +++++++++
36
37 Suppose sale is a variable on a record of the sale.order object related to the 'sale_order' table. You can acquire such a variable doing this.::
38
39     sale = self.browse(cr, uid, ID)
40
41 (where cr is the current row, from the database cursor, uid is the current user's ID for security checks, and ID is the sale order's ID or list of IDs if we want more than one)
42
43 Suppose you want to get: the country name of the first contact of a partner related to the ID sale order. You can do the following in OpenERP::
44
45     country_name = sale.partner_id.address[0].country_id.name
46
47 If you want to write the same thing in traditional SQL development, it will be in python: (we suppose cr is the cursor on the database, with psycopg)
48
49 .. code-block:: python
50
51     cr.execute('select partner_id from sale_order where id=%d', (ID,))
52     partner_id = cr.fetchone()[0]
53     cr.execute('select country_id from res_partner_address where partner_id=%d', (partner_id,))
54     country_id = cr.fetchone()[0]
55     cr.execute('select name from res_country where id=%d', (country_id,))
56     del partner_id
57     del country_id
58     country_name = cr.fetchone()[0]
59
60 Of course you can do better if you develop smartly in SQL, using joins or subqueries. But you have to be smart and most of the time you will not be able to make such improvements:
61
62     * Maybe some parts are in others functions
63     * There may be a loop in different elements
64     * You have to use intermediate variables like country_id
65
66 The first operation as an object call is much better for several reasons:
67
68     * It uses objects facilities and works with modules inheritances, overload, ...
69     * It's simpler, more explicit and uses less code
70     * It's much more efficient as you will see in the following examples
71     * Some fields do not directly correspond to a SQL field (e.g.: function fields in Python)
72
73
74 Prefetching
75 +++++++++++
76
77 Suppose that later in the code, in another function, you want to access the name of the partner associated to your sale order. You can use this::
78
79     partner_name = sale.partner_id.name
80
81 And this will not generate any SQL query as it has been prefetched by the object relational mapping engine of OpenERP.
82
83
84 Loops and special fields
85 ++++++++++++++++++++++++
86
87 Suppose now that you want to compute the totals of 10 sales order by countries. You can do this in OpenERP within a OpenERP object:
88
89 .. code-block:: python
90
91     def get_totals(self, cr, uid, ids):
92        countries = {}
93        for sale in self.browse(cr, uid, ids):
94           country = sale.partner_invoice_id.country
95           countries.setdefault(country, 0.0)
96           countries[country] += sale.amount_untaxed
97        return countries
98
99 And, to print them as a good way, you can add this on your object:
100
101 .. code-block:: python
102
103     def print_totals(self, cr, uid, ids):
104        result = self.get_totals(cr, uid, ids)
105        for country in result.keys():
106           print '[%s] %s: %.2f' (country.code, country.name, result[country])
107
108 The 2 functions will generate 4 SQL queries in total ! This is due to the SQL engine of OpenERP that does prefetching, works on lists and uses caching methods. The 3 queries are:
109
110    1. Reading the sale.order to get ID's of the partner's address
111    2. Reading the partner's address for the countries
112    3. Calling the amount_untaxed function that will compute a total of the sale order lines
113    4. Reading the countries info (code and name)
114
115 That's great because if you run this code on 1000 sales orders, you have the guarantee to only have 4 SQL queries.
116
117 Notes:
118
119     * IDS is the list of the 10 ID's: [12,15,18,34, ...,99]
120     * The arguments of a function are always the same:
121
122           - cr: the cursor database (from psycopg)
123           - uid: the user id (for security checks)
124     * If you run this code on 5000 sales orders, you may have 8 SQL queries because as SQL queries are not allowed to take too much memory, it may have to do two separate readings.
125
126
127 Complex example
128 +++++++++++++++
129
130 Here is a complete example, from the OpenERP official distribution, of the function that does bill of material explosion and computation of associated routings:
131
132 .. code-block:: python
133
134     class mrp_bom(osv.osv):
135         ...
136         def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
137             bom_result = False
138             # Why searching on BoM without parent ?
139             cr.execute('select id from mrp_bom where product_id=%d and bom_id is null
140                           order by sequence', (product_id,))
141             ids = map(lambda x: x[0], cr.fetchall())
142             max_prop = 0
143             result = False
144             for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
145                 prop = 0
146                 for prop_id in bom.property_ids:
147                     if prop_id.id in properties:
148                         prop+=1
149                 if (prop>max_prop) or ((max_prop==0) and not result):
150                     result = bom.id
151                     max_prop = prop
152             return result
153
154             def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
155                 factor = factor / (bom.product_efficiency or 1.0)
156                 factor = rounding(factor, bom.product_rounding)
157                 if factor<bom.product_rounding:
158                     factor = bom.product_rounding
159                 result = []
160                 result2 = []
161                 phantom = False
162                 if bom.type=='phantom' and not bom.bom_lines:
163                     newbom = self._bom_find(cr, uid, bom.product_id.id,
164                                             bom.product_uom.id, properties)
165                     if newbom:
166                         res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0],
167                               factor*bom.product_qty, properties, addthis=True, level=level+10)
168                         result = result + res[0]
169                         result2 = result2 + res[1]
170                         phantom = True
171                     else:
172                         phantom = False
173                 if not phantom:
174                     if addthis and not bom.bom_lines:
175                         result.append(
176                         {
177                             'name': bom.product_id.name,
178                             'product_id': bom.product_id.id,
179                             'product_qty': bom.product_qty * factor,
180                             'product_uom': bom.product_uom.id,
181                             'product_uos_qty': bom.product_uos and 
182                                                bom.product_uos_qty * factor or False,
183                             'product_uos': bom.product_uos and bom.product_uos.id or False,
184                         })
185                     if bom.routing_id:
186                         for wc_use in bom.routing_id.workcenter_lines:
187                             wc = wc_use.workcenter_id
188                             d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
189                             mult = (d + (m and 1.0 or 0.0))
190                             cycle = mult * wc_use.cycle_nbr
191                             result2.append({
192                                 'name': bom.routing_id.name,
193                                 'workcenter_id': wc.id,
194                                 'sequence': level+(wc_use.sequence or 0),
195                                 'cycle': cycle,
196                                 'hour': float(wc_use.hour_nbr*mult +
197                                               (wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
198                                                (wc.time_efficiency or 1.0)),
199                             })
200                     for bom2 in bom.bom_lines:
201                          res = self._bom_explode(cr, uid, bom2, factor, properties,
202                                                      addthis=True, level=level+10)
203                          result = result + res[0]
204                          result2 = result2 + res[1]
205                 return result, result2
206
207
208 Technical architecture
209 ======================
210
211 OpenERP is a `multitenant <http://en.wikipedia.org/wiki/Multitenancy>`_,
212 `three-tier architecture
213 <http://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture>`_.
214 The application tier itself is written as a core, multiple additional
215 modules can be installed to create a particular configuration of
216 OpenERP.
217
218 The core of OpenERP and its modules are written in `Python
219 <http://python.org/>`_. The functionality of a module is exposed through
220 XML-RPC (and/or NET-RPC depending on the server's configuration)[#]. Modules
221 typically make use of OpenERP's ORM to persist their data in a relational
222 database (PostgreSQL). Modules can insert data in the database during
223 installation by providing XML, CSV, or YML files.
224
225 .. figure:: images/client_server.png
226    :scale: 85
227    :align: center
228
229 .. [#] JSON-RPC is planned for OpenERP v6.1.
230
231 The OpenERP server
232 ------------------
233
234 OpenERP provides an application server on which specific business applications
235 can be built. It is also a complete development framework, offering a range of
236 features to write those applications. The salient features are a flexible ORM,
237 a MVC architecture, extensible data models and views, different report engines,
238 all tied together in a coherent, network-accessible framework.
239
240 From a developer perspective, the server acts both as a library which brings
241 the above benefits while hiding the low-level, nitty-gritty details, and as a
242 simple way to install, configure and run the written applications.
243
244 Modules
245 -------
246
247 By itself, the OpenERP server is not very useful. For any enterprise, the value
248 of OpenERP lies in its different modules. It is the role of the modules to
249 implement any business needs. The server is only the necessary machinery to run
250 the modules. A lot of modules already exist. Any official OpenERP release
251 includes about 170 of them, and hundreds of modules are available through the
252 community. Examples of modules are Account, CRM, HR, Marketing, MRP, Sale, etc.
253
254 A module is usually composed of data models, together with some initial data,
255 views definitions (i.e. how data from specific data models should be displayed
256 to the user), wizards (specialized screens to help the user for specific
257 interactions), workflows definitions, and reports.
258
259 Clients
260 -------
261
262 Clients can communicate with an OpenERP server using XML-RPC. A custom, faster
263 protocol called NET-RPC is also provided but will shortly disappear, replaced
264 by JSON-RPC. XML-RPC, as JSON-RPC in the future, makes it possible to write
265 clients for OpenERP in a variety of programming languages. OpenERP S.A.
266 develops two different clients: a desktop client, written with the widely used
267 `GTK+ <http://www.gtk.org/>`_ graphical toolkit, and a web client that should
268 run in any modern web browser.
269
270 As the logic of OpenERP should entirely reside on the server, the client is
271 conceptually very simple; it issues a request to the server and display the result
272 (e.g. a list of customers) in different manners (as forms, lists, calendars,
273 ...). Upon user actions, it will send modified data to the server.
274
275 Relational database server and ORM
276 ----------------------------------
277
278 The data tier of OpenERP is provided by a PostgreSQL relational database. While
279 direct SQL queries can be executed from OpenERP modules, most database access
280 to the relational database is done through the `Object-Relational Mapping
281 <http://en.wikipedia.org/wiki/Object-relational_mapping>`_.
282
283 The ORM is one of the salient features mentioned above. The data models are
284 described in Python and OpenERP creates the underlying database tables. All the
285 benefits of RDBMS (unique constraints, relational integrity, efficient
286 querying, ...) are used when possible and completed by Python flexibility. For
287 instance, arbitrary constraints written in Python can be added to any model.
288 Different modular extensibility mechanisms are also afforded by OpenERP[#].
289
290 .. [#] It is important to understand the ORM responsibility before attempting to by-pass it and access directly the underlying database via raw SQL queries.  When using the ORM, OpenERP can make sure the data remains free of any corruption.  For instance, a module can react to data creation in a particular table. This reaction can only happen if the ORM is used to create that data.
291
292 Models
293 ------
294
295 To define data models and otherwise pursue any work with the associated data,
296 OpenERP as many ORMs uses the concept of 'model'. A model is the authoritative
297 specification of how some data are structured, constrained, and manipulated. In
298 practice, a model is written as a Python class. The class encapsulates anything
299 there is to know about the model: the different fields composing the model,
300 default values to be used when creating new records, constraints, and so on. It
301 also holds the dynamic aspect of the data it controls: methods on the class can
302 be written to implement any business needs (for instance, what to do upon user
303 action, or upon workflow transitions).
304
305 There are two different models. One is simply called 'model', and the second is
306 called 'transient model'. The two models provide the same capabilities with a
307 single difference: transient models are automatically cleared from the
308 database (they can be cleaned when some limit on the number of records is
309 reached, or when they are untouched for some time).
310
311 To describe the data model per se, OpenERP offers a range of different kind of
312 fields. There are basic fields such as integer, or text fields. There are
313 relational fields to implement one-to-many, many-to-one, and many-to-many
314 relationships. There are so-called function fields, which are dynamically
315 computed and are not necessarily available in database, and more.
316
317 Access to data is controlled by OpenERP and configured by different mechanisms.
318 This ensures that different users can have read and/or write access to only the
319 relevant data. Access can be controlled with respect to user groups and rules
320 based on the value of the data themselves.
321
322 Modules
323 -------
324
325 OpenERP supports a modular approach both from a development perspective and a
326 deployment point of view. In essence, a module groups everything related to a
327 single concern in one meaningful entity. It is comprised of models, views,
328 workflows, and wizards.
329
330 Services and WSGI
331 -----------------
332
333 Everything in OpenERP, and models methods in particular, are exposed via the
334 network and a security layer. Access to the data model is in fact a 'service'
335 and it is possible to expose new services. For instance, a WebDAV service and a
336 FTP service are available.
337
338 While not mandatory, the services can make use of the `WSGI
339 <http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface>`_ stack.
340 WSGI is a standard solution in the Python ecosystem to write HTTP servers,
341 applications, and middleware which can be used in a mix-and-match fashion.
342 By using WSGI, it is possible to run OpenERP in any WSGI-compliant server, but
343 also to use OpenERP to host a WSGI application.
344
345 A striking example of this possibility is the OpenERP Web project. OpenERP Web
346 is the server-side counter part to the web clients. It is OpenERP Web which
347 provides the web pages to the browser and manages web sessions. OpenERP Web is
348 a WSGI-compliant application. As such, it can be run as a stand-alone HTTP
349 server or embedded inside OpenERP.
350
351 XML-RPC, JSON-RPC
352 -----------------
353
354 The access to the models makes also use of the WSGI stack. This can be done
355 using the XML-RPC protocol, and JSON-RPC will be added soon.
356
357
358 Explanation of modules:
359
360 **Server - Base distribution**
361
362 We use a distributed communication mechanism inside the OpenERP server. Our engine supports most commonly distributed patterns: request/reply, publish/subscribe, monitoring, triggers/callback, ...
363
364 Different business objects can be in different computers or the same objects can be on multiple computers to perform load-balancing.
365
366 **Server - Object Relational Mapping (ORM)**
367
368 This layer provides additional object functionality on top of PostgreSQL:
369
370     * Consistency: powerful validity checks,
371     * Work with objects (methods, references, ...)
372     * Row-level security (per user/group/role)
373     * Complex actions on a group of resources
374     * Inheritance 
375
376 **Server - Web-Services**
377
378 The web-service module offer a common interface for all web-services
379
380     * SOAP
381     * XML-RPC
382     * NET-RPC 
383
384 Business objects can also be accessed via the distributed object mechanism. They can all be modified via the client interface with contextual views.
385
386 **Server - Workflow Engine**
387
388 Workflows are graphs represented by business objects that describe the dynamics of the company. Workflows are also used to track processes that evolve over time.
389
390 An example of workflow used in OpenERP:
391
392 A sales order generates an invoice and a shipping order
393
394 **Server - Report Engine**
395
396 Reports in OpenERP can be rendered in different ways:
397
398     * Custom reports: those reports can be directly created via the client interface, no programming required. Those reports are represented by business objects (ir.report.custom)
399     * High quality personalized reports using openreport: no programming required but you have to write 2 small XML files:
400
401           - a template which indicates the data you plan to report
402           - an XSL:RML stylesheet 
403     * Hard coded reports
404     * OpenOffice Writer templates 
405
406 Nearly all reports are produced in PDF.
407
408 **Server - Business Objects**
409
410 Almost everything is a business object in OpenERP, they describe all data of the program (workflows, invoices, users, customized reports, ...). Business objects are described using the ORM module. They are persistent and can have multiple views (described by the user or automatically calculated).
411
412 Business objects are structured in the /module directory.
413
414 **Client - Wizards**
415
416 Wizards are graphs of actions/windows that the user can perform during a session.
417
418 **Client - Widgets**
419
420 Widgets are probably, although the origin of the term seems to be very difficult to trace, "WIndow gaDGETS" in the IT world, which mean they are gadgets before anything, which implement elementary features through a portable visual tool.
421
422 All common widgets are supported:
423
424     * entries
425     * textboxes
426     * floating point numbers
427     * dates (with calendar)
428     * checkboxes
429     * ... 
430
431 And also all special widgets:
432
433     * buttons that call actions
434     * references widgets
435
436           - one2one
437
438           - many2one
439
440           - many2many
441
442           - one2many in list
443
444           - ... 
445
446 Widget have different appearances in different views. For example, the date widget in the search dialog represents two normal dates for a range of date (from...to...).
447
448 Some widgets may have different representations depending on the context. For example, the one2many widget can be represented as a form with multiple pages or a multi-columns list.
449
450 Events on the widgets module are processed with a callback mechanism. A callback mechanism is a process whereby an element defines the type of events he can handle and which methods should be called when this event is triggered. Once the event is triggered, the system knows that the event is bound to a specific method, and calls that method back. Hence callback. 
451
452
453 Module Integrations
454 ===================
455
456 The are many different modules available for OpenERP and suited for different business models. Nearly all of these are optional (except ModulesAdminBase), making it easy to customize OpenERP to serve specific business needs. All the modules are in a directory named addons/ on the server. You simply need to copy or delete a module directory in order to either install or delete the module on the OpenERP platform.
457
458 Some modules depend on other modules. See the file addons/module/__openerp__.py for more information on the dependencies.
459
460 Here is an example of __openerp__.py:
461
462 .. code-block:: python
463
464         {
465             "name" : "Open TERP Accounting",
466             "version" : "1.0",
467             "author" : "Bob Gates - Not So Tiny",
468             "website" : "http://www.openerp.com/",
469             "category" : "Generic Modules/Others",
470             "depends" : ["base"],
471             "description" : """A
472             Multiline
473             Description
474             """,
475             "init_xml" : ["account_workflow.xml", "account_data.xml", "account_demo.xml"],
476             "demo_xml" : ["account_demo.xml"],
477             "update_xml" : ["account_view.xml", "account_report.xml", "account_wizard.xml"],
478             "active": False,
479             "installable": True
480         }
481
482 When initializing a module, the files in the init_xml list are evaluated in turn and then the files in the update_xml list are evaluated. When updating a module, only the files from the **update_xml** list are evaluated. 
483
484
485 Inheritance
486 ===========
487
488 Traditional Inheritance
489 -----------------------
490
491 Introduction
492 ++++++++++++
493
494 Objects may be inherited in some custom or specific modules. It is better to inherit an object to add/modify some fields.
495
496 It is done with::
497
498         _inherit='object.name'
499         
500 Extension of an object
501 ++++++++++++++++++++++
502
503 There are two possible ways to do this kind of inheritance. Both ways result in a new class of data, which holds parent fields and behaviour as well as additional fields and behaviour, but they differ in heavy programatical consequences. 
504
505 While Example 1 creates a new subclass "custom_material" that may be "seen" or "used" by any view or tree which handles "network.material", this will not be the case for Example 2. 
506
507 This is due to the table (other.material) the new subclass is operating on, which will never be recognized by previous "network.material" views or trees.
508
509 Example 1::
510
511         class custom_material(osv.osv):
512                 _name = 'network.material'
513                 _inherit = 'network.material'
514                 _columns = {
515                         'manuf_warranty': fields.boolean('Manufacturer warranty?'),
516                 }
517                 _defaults = {
518                         'manuf_warranty': lambda *a: False,
519                }
520         custom_material()
521
522 .. tip:: Notice
523         
524         _name == _inherit
525
526 In this example, the 'custom_material' will add a new field 'manuf_warranty' to the object 'network.material'. New instances of this class will be visible by views or trees operating on the superclasses table 'network.material'.
527
528 This inheritancy is usually called "class inheritance" in Object oriented design. The child inherits data (fields) and behavior (functions) of his parent.
529
530
531 Example 2::
532
533         class other_material(osv.osv):
534                 _name = 'other.material'
535                 _inherit = 'network.material'
536                 _columns = {
537                         'manuf_warranty': fields.boolean('Manufacturer warranty?'),
538                 }
539                 _defaults = {
540                         'manuf_warranty': lambda *a: False,
541                }
542         other_material()
543
544 .. tip:: Notice
545
546         _name != _inherit
547
548 In this example, the 'other_material' will hold all fields specified by 'network.material' and it will additionally hold a new field 'manuf_warranty'. All those fields will be part of the table 'other.material'. New instances of this class will therefore never been seen by views or trees operating on the superclasses table 'network.material'.
549
550 This type of inheritancy is known as "inheritance by prototyping" (e.g. Javascript), because the newly created subclass "copies" all fields from the specified superclass (prototype). The child inherits data (fields) and behavior (functions) of his parent. 
551
552 Inheritance by Delegation
553 -------------------------
554
555  **Syntax :**::
556
557          class tiny_object(osv.osv)
558              _name = 'tiny.object'
559              _table = 'tiny_object'
560              _inherits = { 'tiny.object_a' : 'name_col_a', 'tiny.object_b' : 'name_col_b',
561                         ..., 'tiny.object_n' : 'name_col_n' }
562              (...)    
563
564
565 The object 'tiny.object' inherits from all the columns and all the methods from the n objects 'tiny.object_a', ..., 'tiny.object_n'.
566
567 To inherit from multiple tables, the technique consists in adding one column to the table tiny_object per inherited object. This column will store a foreign key (an id from another table). The values *'name_col_a' 'name_col_b' ... 'name_col_n'* are of type string and determine the title of the columns in which the foreign keys from 'tiny.object_a', ..., 'tiny.object_n' are stored.
568
569 This inheritance mechanism is usually called " *instance inheritance* "  or  " *value inheritance* ". A resource (instance) has the VALUES of its parents.