2
.. i18n: Type of Fields
3
.. i18n: ==============
18
.. i18n: A boolean (true, false).
20
A boolean (true, false).
24
.. i18n: fields.boolean('Field Name' [, Optional Parameters]),
28
fields.boolean('Field Name' [, Optional Parameters]),
38
.. i18n: fields.integer('Field Name' [, Optional Parameters]),
44
fields.integer('Field Name' [, Optional Parameters]),
50
.. i18n: A floating point number.
52
A floating point number.
56
.. i18n: fields.float('Field Name' [, Optional Parameters]),
60
fields.float('Field Name' [, Optional Parameters]),
64
.. i18n: The optional parameter digits defines the precision and scale of the number. The scale being the number of digits after the decimal point whereas the precision is the total number of significant digits in the number (before and after the decimal point). If the parameter digits is not present, the number will be a double precision floating point number. Warning: these floating-point numbers are inexact (not any value can be converted to its binary representation) and this can lead to rounding errors. You should always use the digits parameter for monetary amounts.
69
.. i18n: 'rate' : fields.float('Relative Change rate', digits=(12,6) [, Optional Parameters]),
73
The optional parameter digits defines the precision and scale of the number. The scale being the number of digits after the decimal point whereas the precision is the total number of significant digits in the number (before and after the decimal point). If the parameter digits is not present, the number will be a double precision floating point number. Warning: these floating-point numbers are inexact (not any value can be converted to its binary representation) and this can lead to rounding errors. You should always use the digits parameter for monetary amounts.
78
'rate' : fields.float('Relative Change rate', digits=(12,6) [, Optional Parameters]),
84
.. i18n: A string of limited length. The required size parameter determines its size.
86
A string of limited length. The required size parameter determines its size.
90
.. i18n: fields.char('Field Name', size=n [, Optional Parameters]), # where ''n'' is an integer.
94
fields.char('Field Name', size=n [, Optional Parameters]), # where ''n'' is an integer.
100
.. i18n: 'city' : fields.char('City Name', size=30, required=True),
102
'city' : fields.char('City Name', size=30, required=True),
108
.. i18n: A text field with no limit in length.
110
A text field with no limit in length.
114
.. i18n: fields.text('Field Name' [, Optional Parameters]),
118
fields.text('Field Name' [, Optional Parameters]),
130
.. i18n: fields.date('Field Name' [, Optional Parameters]),
134
fields.date('Field Name' [, Optional Parameters]),
140
.. i18n: Allows to store a date and the time of day in the same field.
142
Allows to store a date and the time of day in the same field.
146
.. i18n: fields.datetime('Field Name' [, Optional Parameters]),
150
fields.datetime('Field Name' [, Optional Parameters]),
156
.. i18n: A binary chain
164
.. i18n: A field which allows the user to make a selection between various predefined values.
166
A field which allows the user to make a selection between various predefined values.
170
.. i18n: fields.selection((('n','Unconfirmed'), ('c','Confirmed')),
171
.. i18n: 'Field Name' [, Optional Parameters]),
175
fields.selection((('n','Unconfirmed'), ('c','Confirmed')),
176
'Field Name' [, Optional Parameters]),
180
.. i18n: Format of the selection parameter: tuple of tuples of strings of the form::
182
.. i18n: (('key_or_value', 'string_to_display'), ... )
186
Format of the selection parameter: tuple of tuples of strings of the form::
188
(('key_or_value', 'string_to_display'), ... )
194
.. i18n: Using relation fields **many2one** with **selection**. In fields definitions add::
197
.. i18n: 'my_field': fields.many2one('mymodule.relation.model', 'Title', selection=_sel_func),
200
Using relation fields **many2one** with **selection**. In fields definitions add::
203
'my_field': fields.many2one('mymodule.relation.model', 'Title', selection=_sel_func),
206
.. i18n: And then define the _sel_func like this (but before the fields definitions)::
208
.. i18n: def _sel_func(self, cr, uid, context={}):
209
.. i18n: obj = self.pool.get('mymodule.relation.model')
210
.. i18n: ids = obj.search(cr, uid, [])
211
.. i18n: res = obj.read(cr, uid, ids, ['name', 'id'], context)
212
.. i18n: res = [(r['id'], r['name']) for r in res]
216
And then define the _sel_func like this (but before the fields definitions)::
218
def _sel_func(self, cr, uid, context={}):
219
obj = self.pool.get('mymodule.relation.model')
220
ids = obj.search(cr, uid, [])
221
res = obj.read(cr, uid, ids, ['name', 'id'], context)
222
res = [(r['id'], r['name']) for r in res]
226
.. i18n: Relational Types
227
.. i18n: ----------------
236
.. i18n: A one2one field expresses a one:to:one relation between two objects. It is deprecated. Use many2one instead.
240
.. i18n: fields.one2one('other.object.name', 'Field Name')
242
A one2one field expresses a one:to:one relation between two objects. It is deprecated. Use many2one instead.
246
fields.one2one('other.object.name', 'Field Name')
252
.. i18n: Associates this object to a parent object via this Field. For example Department an Employee belongs to would Many to one. i.e Many employees will belong to a Department
254
Associates this object to a parent object via this Field. For example Department an Employee belongs to would Many to one. i.e Many employees will belong to a Department
258
.. i18n: fields.many2one('other.object.name', 'Field Name', optional parameter)
262
fields.many2one('other.object.name', 'Field Name', optional parameter)
264
.. i18n: * Optional parameters:
265
.. i18n: - ondelete: What should happen when the resource this field points to is deleted.
266
.. i18n: + Predefined value: "cascade", "set null"
267
.. i18n: + Default value: "set null"
268
.. i18n: - required: True
269
.. i18n: - readonly: True
270
.. i18n: - select: True - (creates an index on the Foreign Key field)
272
* Optional parameters:
273
- ondelete: What should happen when the resource this field points to is deleted.
274
+ Predefined value: "cascade", "set null"
275
+ Default value: "set null"
278
- select: True - (creates an index on the Foreign Key field)
284
.. i18n: 'commercial': fields.many2one('res.users', 'Commercial', ondelete='cascade'),
286
'commercial': fields.many2one('res.users', 'Commercial', ondelete='cascade'),
298
.. i18n: fields.one2many('other.object.name', 'Field relation id', 'Fieldname', optional parameter)
302
fields.one2many('other.object.name', 'Field relation id', 'Fieldname', optional parameter)
304
.. i18n: * Optional parameters:
305
.. i18n: - invisible: True/False
307
.. i18n: - readonly: True/False
309
* Optional parameters:
310
- invisible: True/False
312
- readonly: True/False
318
.. i18n: 'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
320
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
332
.. i18n: fields.many2many('other.object.name',
333
.. i18n: 'relation object',
334
.. i18n: 'other.object.id',
335
.. i18n: 'actual.object.id',
336
.. i18n: 'Field Name')
340
fields.many2many('other.object.name',
347
.. i18n: - other.object.name is the other object which belongs to the relation
348
.. i18n: - relation object is the table that makes the link
349
.. i18n: - other.object.id and actual.object.id are the fields' names used in the relation table
352
- other.object.name is the other object which belongs to the relation
353
- relation object is the table that makes the link
354
- other.object.id and actual.object.id are the fields' names used in the relation table
358
.. i18n: 'category_id':
359
.. i18n: fields.many2many(
360
.. i18n: 'res.partner.category',
361
.. i18n: 'res_partner_category_rel',
362
.. i18n: 'partner_id',
363
.. i18n: 'category_id',
364
.. i18n: 'Categories'),
370
'res.partner.category',
371
'res_partner_category_rel',
380
.. i18n: Sometimes you need to refer the relation of a relation. For example, supposing you have objects: City <- State <- Country, and you need to refer Country in a City, you can define a field as below in the City object::
382
.. i18n: 'country_id': fields.related('state_id', 'country_id', type="many2one",
383
.. i18n: relation="module.country", string="Country", store=False)
385
Sometimes you need to refer the relation of a relation. For example, supposing you have objects: City <- State <- Country, and you need to refer Country in a City, you can define a field as below in the City object::
387
'country_id': fields.related('state_id', 'country_id', type="many2one",
388
relation="module.country", string="Country", store=False)
390
.. i18n: Functional Field
391
.. i18n: ++++++++++++++++
396
.. i18n: A functional field is a field whose value is calculated by a function (rather than being stored in the database).
398
A functional field is a field whose value is calculated by a function (rather than being stored in the database).
400
.. i18n: **Parameters:** fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type="%green%float%black%", fnct_search=None, obj=None, method=False, store=True
402
**Parameters:** fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type="%green%float%black%", fnct_search=None, obj=None, method=False, store=True
408
.. i18n: * :guilabel:`type` is the field type name returned by the function. It can be any field type name except function.
409
.. i18n: * :guilabel:`store` If you want to store field in database or not. Default is False.
410
.. i18n: * :guilabel:`method` whether the field is computed by a method (of an object) or a global function
411
.. i18n: * :guilabel:`fnct` is the function or method that will compute the field value. It must have been declared before declaring the functional field.
413
* :guilabel:`type` is the field type name returned by the function. It can be any field type name except function.
414
* :guilabel:`store` If you want to store field in database or not. Default is False.
415
* :guilabel:`method` whether the field is computed by a method (of an object) or a global function
416
* :guilabel:`fnct` is the function or method that will compute the field value. It must have been declared before declaring the functional field.
418
.. i18n: If *method* is True, the signature of the method must be::
420
.. i18n: def fnct(self, cr, uid, ids, field_name, arg, context)
422
If *method* is True, the signature of the method must be::
424
def fnct(self, cr, uid, ids, field_name, arg, context)
426
.. i18n: otherwise (if it is a global function), its signature must be::
428
.. i18n: def fnct(cr, table, ids, field_name, arg, context)
430
otherwise (if it is a global function), its signature must be::
432
def fnct(cr, table, ids, field_name, arg, context)
434
.. i18n: Either way, it must return a dictionary of values of the form **{id'_1_': value'_1_', id'_2_': value'_2_',...}.**
436
Either way, it must return a dictionary of values of the form **{id'_1_': value'_1_', id'_2_': value'_2_',...}.**
438
.. i18n: The values of the returned dictionary must be of the type specified by the type argument in the field declaration.
440
The values of the returned dictionary must be of the type specified by the type argument in the field declaration.
442
.. i18n: * :guilabel:`fnct_inv` is the function or method that will allow writing values in that field.
444
* :guilabel:`fnct_inv` is the function or method that will allow writing values in that field.
446
.. i18n: If *method* is true, the signature of the method must be::
448
.. i18n: def fnct(self, cr, uid, ids, field_name, field_value, arg, context)
450
If *method* is true, the signature of the method must be::
452
def fnct(self, cr, uid, ids, field_name, field_value, arg, context)
454
.. i18n: otherwise (if it is a global function), it should be::
456
.. i18n: def fnct(cr, table, ids, field_name, field_value, arg, context)
458
otherwise (if it is a global function), it should be::
460
def fnct(cr, table, ids, field_name, field_value, arg, context)
462
.. i18n: * :guilabel:`fnct_search` allows you to define the searching behaviour on that field.
464
* :guilabel:`fnct_search` allows you to define the searching behaviour on that field.
466
.. i18n: If method is true, the signature of the method must be::
468
.. i18n: def fnct(self, cr, uid, obj, name, args)
470
If method is true, the signature of the method must be::
472
def fnct(self, cr, uid, obj, name, args)
474
.. i18n: otherwise (if it is a global function), it should be::
476
.. i18n: def fnct(cr, uid, obj, name, args)
478
otherwise (if it is a global function), it should be::
480
def fnct(cr, uid, obj, name, args)
482
.. i18n: The return value is a list countaining 3-part tuplets which are used in search funtion::
484
.. i18n: return [('id','in',[1,3,5])]
486
The return value is a list countaining 3-part tuplets which are used in search funtion::
488
return [('id','in',[1,3,5])]
490
.. i18n: :Example Of Functional Field:
492
:Example Of Functional Field:
494
.. i18n: Suppose we create a contract object which is :
496
Suppose we create a contract object which is :
498
.. i18n: .. code-block:: python
500
.. i18n: class hr_contract(osv.osv):
501
.. i18n: _name = 'hr.contract'
502
.. i18n: _description = 'Contract'
503
.. i18n: _columns = {
504
.. i18n: 'name' : fields.char('Contract Name', size=30, required=True),
505
.. i18n: 'employee_id' : fields.many2one('hr.employee', 'Employee', required=True),
506
.. i18n: 'function' : fields.many2one('res.partner.function', 'Function'),
508
.. i18n: hr_contract()
510
.. code-block:: python
512
class hr_contract(osv.osv):
513
_name = 'hr.contract'
514
_description = 'Contract'
516
'name' : fields.char('Contract Name', size=30, required=True),
517
'employee_id' : fields.many2one('hr.employee', 'Employee', required=True),
518
'function' : fields.many2one('res.partner.function', 'Function'),
522
.. i18n: If we want to add a field that retrieves the function of an employee by looking its current contract, we use a functional field. The object hr_employee is inherited this way:
524
If we want to add a field that retrieves the function of an employee by looking its current contract, we use a functional field. The object hr_employee is inherited this way:
526
.. i18n: .. code-block:: python
528
.. i18n: class hr_employee(osv.osv):
529
.. i18n: _name = "hr.employee"
530
.. i18n: _description = "Employee"
531
.. i18n: _inherit = "hr.employee"
532
.. i18n: _columns = {
533
.. i18n: 'contract_ids' : fields.one2many('hr.contract', 'employee_id', 'Contracts'),
534
.. i18n: 'function' : fields.function(_get_cur_function_id, type='many2one', obj="res.partner.function",
535
.. i18n: method=True, string='Contract Function'),
537
.. i18n: hr_employee()
539
.. code-block:: python
541
class hr_employee(osv.osv):
542
_name = "hr.employee"
543
_description = "Employee"
544
_inherit = "hr.employee"
546
'contract_ids' : fields.one2many('hr.contract', 'employee_id', 'Contracts'),
547
'function' : fields.function(_get_cur_function_id, type='many2one', obj="res.partner.function",
548
method=True, string='Contract Function'),
552
.. i18n: .. note:: three points
554
.. i18n: * :guilabel:`type` ='many2one' is because the function field must create a many2one field; function is declared as a many2one in hr_contract also.
555
.. i18n: * :guilabel:`obj` ="res.partner.function" is used to specify that the object to use for the many2one field is res.partner.function.
556
.. i18n: * We called our method :guilabel:`_get_cur_function_id` because its role is to return a dictionary whose keys are ids of employees, and whose corresponding values are ids of the function of those employees. The code of this method is:
558
.. note:: three points
560
* :guilabel:`type` ='many2one' is because the function field must create a many2one field; function is declared as a many2one in hr_contract also.
561
* :guilabel:`obj` ="res.partner.function" is used to specify that the object to use for the many2one field is res.partner.function.
562
* We called our method :guilabel:`_get_cur_function_id` because its role is to return a dictionary whose keys are ids of employees, and whose corresponding values are ids of the function of those employees. The code of this method is:
564
.. i18n: .. code-block:: python
566
.. i18n: def _get_cur_function_id(self, cr, uid, ids, field_name, arg, context):
567
.. i18n: for i in ids:
568
.. i18n: #get the id of the current function of the employee of identifier "i"
569
.. i18n: sql_req= """
570
.. i18n: SELECT f.id AS func_id
571
.. i18n: FROM hr_contract c
572
.. i18n: LEFT JOIN res_partner_function f ON (f.id = c.function)
574
.. i18n: (c.employee_id = %d)
577
.. i18n: cr.execute(sql_req)
578
.. i18n: sql_res = cr.dictfetchone()
580
.. i18n: if sql_res: #The employee has one associated contract
581
.. i18n: res[i] = sql_res['func_id']
583
.. i18n: #res[i] must be set to False and not to None because of XML:RPC
584
.. i18n: # "cannot marshal None unless allow_none is enabled"
585
.. i18n: res[i] = False
588
.. code-block:: python
590
def _get_cur_function_id(self, cr, uid, ids, field_name, arg, context):
592
#get the id of the current function of the employee of identifier "i"
594
SELECT f.id AS func_id
596
LEFT JOIN res_partner_function f ON (f.id = c.function)
602
sql_res = cr.dictfetchone()
604
if sql_res: #The employee has one associated contract
605
res[i] = sql_res['func_id']
607
#res[i] must be set to False and not to None because of XML:RPC
608
# "cannot marshal None unless allow_none is enabled"
612
.. i18n: The id of the function is retrieved using a SQL query. Note that if the query returns no result, the value of sql_res['func_id'] will be None. We force the False value in this case value because XML:RPC (communication between the server and the client) doesn't allow to transmit this value.
614
The id of the function is retrieved using a SQL query. Note that if the query returns no result, the value of sql_res['func_id'] will be None. We force the False value in this case value because XML:RPC (communication between the server and the client) doesn't allow to transmit this value.
616
.. i18n: :store={...} Enhancement:
618
:store={...} Enhancement:
620
.. i18n: It will compute the field depends on other objects.
622
It will compute the field depends on other objects.
624
.. i18n: :Syntex: store={'object_name':(function_name,['field_name1','field_name2'],priority)} It will call function function_name when any changes will be applied on field list ['field1','field2'] on object 'object_name' and output of the function will send as a parameter for main function of the field.
626
:Syntex: store={'object_name':(function_name,['field_name1','field_name2'],priority)} It will call function function_name when any changes will be applied on field list ['field1','field2'] on object 'object_name' and output of the function will send as a parameter for main function of the field.
628
.. i18n: :Example In membership module:
630
:Example In membership module:
632
.. i18n: .. code-block:: python
634
.. i18n: 'membership_state': fields.function(_membership_state, method=True, string='Current membership state', type='selection', selection=STATE,
635
.. i18n: store={'account.invoice':(_get_invoice_partner,['state'], 10),
636
.. i18n: 'membership.membership_line':(_get_partner_id,['state'], 10),
637
.. i18n: 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)}),
639
.. code-block:: python
641
'membership_state': fields.function(_membership_state, method=True, string='Current membership state', type='selection', selection=STATE,
642
store={'account.invoice':(_get_invoice_partner,['state'], 10),
643
'membership.membership_line':(_get_partner_id,['state'], 10),
644
'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)}),
646
.. i18n: Property Fields
647
.. i18n: +++++++++++++++
652
.. i18n: .. describe:: Declaring a property
654
.. describe:: Declaring a property
656
.. i18n: A property is a special field: fields.property.
658
A property is a special field: fields.property.
660
.. i18n: .. code-block:: python
662
.. i18n: class res_partner(osv.osv):
663
.. i18n: _name = "res.partner"
664
.. i18n: _inherit = "res.partner"
665
.. i18n: _columns = {
666
.. i18n: 'property_product_pricelist': fields.property(
667
.. i18n: 'product.pricelist',
668
.. i18n: type='many2one',·
669
.. i18n: relation='product.pricelist',·
670
.. i18n: string="Sale Pricelist",·
671
.. i18n: method=True,
672
.. i18n: view_load=True,
673
.. i18n: group_name="Pricelists Properties"),
676
.. code-block:: python
678
class res_partner(osv.osv):
679
_name = "res.partner"
680
_inherit = "res.partner"
682
'property_product_pricelist': fields.property(
685
relation='product.pricelist',·
686
string="Sale Pricelist",·
689
group_name="Pricelists Properties"),
692
.. i18n: Then you have to create the default value in a .XML file for this property:
694
Then you have to create the default value in a .XML file for this property:
696
.. i18n: .. code-block:: xml
698
.. i18n: <record model="ir.property" id="property_product_pricelist">
699
.. i18n: <field name="name">property_product_pricelist</field>
700
.. i18n: <field name="fields_id" search="[('model','=','res.partner'),
701
.. i18n: ('name','=','property_product_pricelist')]"/>
702
.. i18n: <field name="value" eval="'product.pricelist,'+str(list0)"/>
707
<record model="ir.property" id="property_product_pricelist">
708
<field name="name">property_product_pricelist</field>
709
<field name="fields_id" search="[('model','=','res.partner'),
710
('name','=','property_product_pricelist')]"/>
711
<field name="value" eval="'product.pricelist,'+str(list0)"/>
720
.. i18n: if the default value points to a resource from another module, you can use the ref function like this:
722
.. i18n: <field name="value" eval="'product.pricelist,'+str(ref('module.data_id'))"/>
726
if the default value points to a resource from another module, you can use the ref function like this:
728
<field name="value" eval="'product.pricelist,'+str(ref('module.data_id'))"/>
730
.. i18n: **Putting properties in forms**
732
**Putting properties in forms**
734
.. i18n: To add properties in forms, just put the <properties/> tag in your form. This will automatically add all properties fields that are related to this object. The system will add properties depending on your rights. (some people will be able to change a specific property, others won't).
736
To add properties in forms, just put the <properties/> tag in your form. This will automatically add all properties fields that are related to this object. The system will add properties depending on your rights. (some people will be able to change a specific property, others won't).
738
.. i18n: Properties are displayed by section, depending on the group_name attribute. (It is rendered in the client like a separator tag).
740
Properties are displayed by section, depending on the group_name attribute. (It is rendered in the client like a separator tag).
742
.. i18n: **How does this work ?**
744
**How does this work ?**
746
.. i18n: The fields.property class inherits from fields.function and overrides the read and write method. The type of this field is many2one, so in the form a property is represented like a many2one function.
748
The fields.property class inherits from fields.function and overrides the read and write method. The type of this field is many2one, so in the form a property is represented like a many2one function.
750
.. i18n: But the value of a property is stored in the ir.property class/table as a complete record. The stored value is a field of type reference (not many2one) because each property may point to a different object. If you edit properties values (from the administration menu), these are represented like a field of type reference.
752
But the value of a property is stored in the ir.property class/table as a complete record. The stored value is a field of type reference (not many2one) because each property may point to a different object. If you edit properties values (from the administration menu), these are represented like a field of type reference.
754
.. i18n: When you read a property, the program gives you the property attached to the instance of object you are reading. It this object has no value, the system will give you the default property.
756
When you read a property, the program gives you the property attached to the instance of object you are reading. It this object has no value, the system will give you the default property.
758
.. i18n: The definition of a property is stored in the ir.model.fields class like any other fields. In the definition of the property, you can add groups that are allowed to change to property.
760
The definition of a property is stored in the ir.model.fields class like any other fields. In the definition of the property, you can add groups that are allowed to change to property.
762
.. i18n: **Using properties or normal fields**
764
**Using properties or normal fields**
766
.. i18n: When you want to add a new feature, you will have to choose to implement it as a property or as normal field. Use a normal field when you inherit from an object and want to extend this object. Use a property when the new feature is not related to the object but to an external concept.
768
When you want to add a new feature, you will have to choose to implement it as a property or as normal field. Use a normal field when you inherit from an object and want to extend this object. Use a property when the new feature is not related to the object but to an external concept.
770
.. i18n: Here are a few tips to help you choose between a normal field or a property:
772
Here are a few tips to help you choose between a normal field or a property:
774
.. i18n: Normal fields extend the object, adding more features or data.
776
Normal fields extend the object, adding more features or data.
778
.. i18n: A property is a concept that is attached to an object and have special features:
780
A property is a concept that is attached to an object and have special features:
782
.. i18n: * Different value for the same property depending on the company
783
.. i18n: * Rights management per field
784
.. i18n: * It's a link between resources (many2one)
786
* Different value for the same property depending on the company
787
* Rights management per field
788
* It's a link between resources (many2one)
790
.. i18n: **Example 1: Account Receivable**
792
**Example 1: Account Receivable**
794
.. i18n: The default "Account Receivable" for a specific partner is implemented as a property because:
796
The default "Account Receivable" for a specific partner is implemented as a property because:
798
.. i18n: * This is a concept related to the account chart and not to the partner, so it is an account property that is visible on a partner form. Rights have to be managed on this fields for accountants, these are not the same rights that are applied to partner objects. So you have specific rights just for this field of the partner form: only accountants may change the account receivable of a partner.
800
.. i18n: * This is a multi-company field: the same partner may have different account receivable values depending on the company the user belongs to. In a multi-company system, there is one account chart per company. The account receivable of a partner depends on the company it placed the sale order.
802
.. i18n: * The default account receivable is the same for all partners and is configured from the general property menu (in administration).
804
* This is a concept related to the account chart and not to the partner, so it is an account property that is visible on a partner form. Rights have to be managed on this fields for accountants, these are not the same rights that are applied to partner objects. So you have specific rights just for this field of the partner form: only accountants may change the account receivable of a partner.
806
* This is a multi-company field: the same partner may have different account receivable values depending on the company the user belongs to. In a multi-company system, there is one account chart per company. The account receivable of a partner depends on the company it placed the sale order.
808
* The default account receivable is the same for all partners and is configured from the general property menu (in administration).
811
.. i18n: One interesting thing is that properties avoid "spaghetti" code. The account module depends on the partner (base) module. But you can install the partner (base) module without the accounting module. If you add a field that points to an account in the partner object, both objects will depend on each other. It's much more difficult to maintain and code (for instance, try to remove a table when both tables are pointing to each others.)
814
One interesting thing is that properties avoid "spaghetti" code. The account module depends on the partner (base) module. But you can install the partner (base) module without the accounting module. If you add a field that points to an account in the partner object, both objects will depend on each other. It's much more difficult to maintain and code (for instance, try to remove a table when both tables are pointing to each others.)
816
.. i18n: **Example 2: Product Times**
818
**Example 2: Product Times**
820
.. i18n: The product expiry module implements all delays related to products: removal date, product usetime, ... This module is very useful for food industries.
822
The product expiry module implements all delays related to products: removal date, product usetime, ... This module is very useful for food industries.
824
.. i18n: This module inherits from the product.product object and adds new fields to it:
826
This module inherits from the product.product object and adds new fields to it:
828
.. i18n: .. code-block:: python
830
.. i18n: class product_product(osv.osv):
832
.. i18n: _inherit = 'product.product'
833
.. i18n: _name = 'product.product'
834
.. i18n: _columns = {
836
.. i18n: 'life_time': fields.integer('Product lifetime'),
837
.. i18n: 'use_time': fields.integer('Product usetime'),
838
.. i18n: 'removal_time': fields.integer('Product removal time'),
839
.. i18n: 'alert_time': fields.integer('Product alert time'),
842
.. i18n: product_product()
844
.. code-block:: python
846
class product_product(osv.osv):
848
_inherit = 'product.product'
849
_name = 'product.product'
852
'life_time': fields.integer('Product lifetime'),
853
'use_time': fields.integer('Product usetime'),
854
'removal_time': fields.integer('Product removal time'),
855
'alert_time': fields.integer('Product alert time'),
864
.. i18n: This module adds simple fields to the product.product object. We did not use properties because:
866
This module adds simple fields to the product.product object. We did not use properties because:
868
.. i18n: * We extend a product, the life_time field is a concept related to a product, not to another object.
869
.. i18n: * We do not need a right management per field, the different delays are managed by the same people that manage all products.
871
* We extend a product, the life_time field is a concept related to a product, not to another object.
872
* We do not need a right management per field, the different delays are managed by the same people that manage all products.