@@ -6,12 +6,6 @@ In this chapter, we'll add business logic to the models to automate the processe
6
6
and turn it into a dynamic and useful tool. This will involve defining actions, constraints,
7
7
automatic computations, and other model methods.
8
8
9
- .. todo: explain the env (self.env.uid, self.env.user, self.env.ref(xml_id), self.env[model_name])
10
- .. todo: explain magic commands
11
- .. todo: 6,0,0 to associate tags to properties in data
12
- .. todo: create (create offer -> offer received state) and write methods
13
- .. todo: auto-update property state based on received offers state (write)
14
-
15
9
.. _tutorials/server_framework_101/computed_fields :
16
10
17
11
Automate field computations
@@ -95,6 +89,7 @@ that computed fields remain consistent.
95
89
.. seealso ::
96
90
- :ref: `Reference documentation for computed fields <reference/fields/compute >`
97
91
- :ref: `Reference documentation for recordsets <reference/orm/recordsets >`
92
+ - Reference documentation for the :meth: `@api.depends() <odoo.api.depends> ` decorator
98
93
- :ref: `Coding guidelines on naming and ordering the members of model classes
99
94
<contributing/coding_guidelines/model_members>`
100
95
@@ -109,9 +104,9 @@ Let's implement them.
109
104
- **Best Offer ** (`real.estate.property `): The maximum amount of all offers.
110
105
111
106
.. tip ::
112
- - Import the `odoo.tools.date_utils ` package to simplify operations on `Date ` fields.
113
107
- Use the :meth: `mapped <odoo.models.Model.mapped> ` method to extract a recordset's field
114
108
values into a list.
109
+ - Import the `odoo.tools.date_utils ` package to simplify operations on `Date ` fields.
115
110
116
111
.. spoiler :: Solution
117
112
@@ -178,6 +173,50 @@ Let's implement them.
178
173
[...]
179
174
</record >
180
175
176
+ .. code-block :: python
177
+ :caption: `real_estate_offer.py`
178
+ :emphasize- lines: 1 - 2 ,9 ,12 - 15
179
+
180
+ from odoo import api, fields, models
181
+ from odoo.tools import date_utils
182
+
183
+ class RealEstateOffer (models .Model ):
184
+ [... ]
185
+ validity = fields.Integer(
186
+ string = " Validity" , help = " The number of days before the offer expires." , default = 7
187
+ )
188
+ expiry_date = fields.Date(string = " Expiry Date" , compute = ' _compute_expiry_date' )
189
+ [... ]
190
+
191
+ @api.depends (' date' , ' validity' )
192
+ def _compute_expiry_date (self ):
193
+ for offer in self :
194
+ offer.expiry_date = date_utils.add(offer.date, days = offer.validity)
195
+
196
+ .. code-block :: xml
197
+ :caption: `real_estate_offer_views.xml`
198
+ :emphasize-lines: 5,16
199
+
200
+ <record id =" real_estate.offer_list" model =" ir.ui.view" >
201
+ [...]
202
+ <list >
203
+ [...]
204
+ <field name =" expiry_date" />
205
+ <field name =" state" />
206
+ </list >
207
+ [...]
208
+ </record >
209
+
210
+ <record id =" real_estate.offer_form" model =" ir.ui.view" >
211
+ [...]
212
+ <group >
213
+ [...]
214
+ <field name =" validity" />
215
+ <field name =" expiry_date" />
216
+ </group >
217
+ [...]
218
+ </record >
219
+
181
220
.. _tutorials/server_framework_101/inverse_methods :
182
221
183
222
Make computed fields editable
@@ -193,10 +232,43 @@ To make a computed field editable, a Python method must be defined and linked to
193
232
declaration using the `inverse ` argument. This method specifies how updates to the computed field
194
233
should be applied to its dependencies.
195
234
196
- .. seealso ::
197
- :ref: `Reference documentation for related fields <reference/fields/related >`
235
+ .. example ::
236
+ In the example below, an inverse method is added to the `margin ` field.
237
+
238
+ .. code-block :: python
239
+
240
+ margin = fields.Float(
241
+ string = " Profit Margin" , compute = ' _compute_margin' , inverse = ' _inverse_margin'
242
+ )
243
+
244
+ def _inverse_margin (self ):
245
+ for product in self :
246
+ # As the cost is fixed, the sales price is increased to match the desired margin.
247
+ product.price = product.cost + product.margin
248
+
249
+ Now that we have seen how inverse methods make computed fields editable, let's put this concept in
250
+ practice.
251
+
252
+ .. exercise ::
253
+ Make the Expiry Date field editable on real estate offers.
254
+
255
+ .. tip ::
256
+ You'll need to save the property form view to trigger the computation.
257
+
258
+ .. spoiler :: Solution
259
+
260
+ .. code-block :: python
261
+ :caption: `real_estate_offer.py`
262
+ :emphasize- lines: 1 - 3 ,6 - 8
198
263
199
- .. todo: inverse: offer deadline
264
+ expiry_date = fields.Date(
265
+ string = " Expiry Date" , compute = ' _compute_expiry_date' , inverse = ' _inverse_expiry_date'
266
+ )
267
+ [... ]
268
+
269
+ def _inverse_expiry_date (self ):
270
+ for offer in self :
271
+ offer.validity = date_utils.relativedelta(dt1 = offer.expiry_date, dt2 = offer.date).days
200
272
201
273
.. _tutorials/server_framework_101/store_computed_fields :
202
274
@@ -221,9 +293,6 @@ large number of records.
221
293
.. example ::
222
294
store `margin `
223
295
224
- .. seealso ::
225
- Reference documentation for the :meth: `@api.depends() <odoo.api.depends> ` decorator
226
-
227
296
.. _tutorials/server_framework_101/search_methods :
228
297
229
298
Search computed fields
@@ -263,6 +332,9 @@ domain must be constructed using stored fields only.
263
332
else :
264
333
raise NotImplementedError ()
265
334
335
+ .. todo: compute Stalled based on `today > availability date` -> search filter for stalled (below availability date)
336
+ .. todo: compute Priority (star) == offer expires in <= 2 days -> search filter + groupby priority (separate group below for sale)
337
+
266
338
.. _tutorials/server_framework_101/related_fields :
267
339
268
340
Simplify related record access
@@ -281,7 +353,8 @@ argument, just like regular computed fields.
281
353
.. seealso ::
282
354
:ref: `Reference documentation for related fields <reference/fields/related >`
283
355
284
- .. todo: related fields (buyer's phone)
356
+ .. todo: related buyer's phone
357
+ .. todo: related address's street depends=[partner_id]
285
358
286
359
.. _tutorials/server_framework_101/onchanges :
287
360
@@ -301,14 +374,23 @@ the :code:`@api.onchange()` decorator. These methods are triggered when the spec
301
374
are altered. They operate on the in-memory representation of a single-record recordset received
302
375
through `self `. If field values are modified, the changes are automatically reflected in the UI.
303
376
377
+ .. todo: explain the env (self.env.uid, self.env.user, self.env.ref(xml_id), self.env[model_name])
378
+
304
379
.. seealso ::
305
380
- Reference documentation for the :meth: `@api.onchange() <odoo.api.onchange> ` decorator
306
381
- Reference documentation for the :class: `UserError <odoo.exceptions.UserError> ` exception
307
382
383
+ .. todo: tip ref translation
384
+
308
385
.. todo: raise UserError + translation
309
- .. todo: if garden checked -> show total area
310
- .. todo: mention that the method is public so it can be called directly by the client.
311
- .. todo: always return something in public methods as they are part of the :ref:external API and can be called through XML-RPC
386
+ .. todo: note: mention that the method is public so it can be called directly by the client.
387
+ always return something in public methods as they are part of the :ref:external API and can be called through XML-RPC
388
+
389
+ .. exercise ::
390
+ tmp
391
+
392
+ .. todo: if garden unchecked -> set garden area to zero
393
+ .. todo: if write in garden area -> set garden checked
312
394
313
395
.. _tutorials/server_framework_101/constraints :
314
396
@@ -347,8 +429,9 @@ expression to validate, and the error message to display if the constraint is vi
347
429
- `Reference documentation for PostgreSQL's constraints
348
430
<https://www.postgresql.org/docs/current/ddl-constraints.html> `_
349
431
350
- .. todo: price more than zero
351
- .. todo: unique tag constraint
432
+ .. todo: property price strictly positive
433
+ .. todo: offer amount strictly positive
434
+ .. todo: unique tag
352
435
353
436
.. _tutorials/server_framework_101/python_constraints :
354
437
@@ -371,7 +454,10 @@ the constraint is violated.
371
454
- Reference documentation for the :class: `ValidationError <odoo.exceptions.ValidationError> `
372
455
exception
373
456
457
+ .. todo: the offer amount must be at least 80% of the sales price
458
+ .. todo: the availability date must be in less than 3 months
374
459
.. todo: accept only one offer
460
+ .. todo: new offers of given user must be more than offers (tip: filtered)
375
461
376
462
.. _tutorials/server_framework_101/defaults :
377
463
@@ -388,9 +474,9 @@ as a model method or a lambda function. In both cases, the `self` argument provi
388
474
environment but does not represent the current record, as no record exists yet during the creation
389
475
process.
390
476
477
+ .. todo : real.estate.offer.amount ::default -> property.selling_price
391
478
.. todo: salesperson_id = fields.Many2one(default=lambda self: self.env.user)
392
479
.. todo: availability_date = fields.Date(default=lambda self: date_utils.add(fields.Date.today(), months=2))
393
- .. todo : real.estate.offer.amount ::default -> property.selling_price (add related?)
394
480
.. todo: real.estate.tag.color -> default=_default_color ; def _default_color(self): return random.randint(1, 11) (check if lambda works)
395
481
.. todo: copy=False on some fields
396
482
@@ -404,11 +490,8 @@ buttons can be of type **action**, defined in XML, or **object**, implemented in
404
490
Together, these types of buttons facilitate the integration of user interactions with business
405
491
logic.
406
492
407
- .. todo: "assign myself as salesperson" action
408
- .. todo: "view best offer" statbutton
409
- .. todo: accept/refuse offer buttons
410
- .. todo: accepting offer refuses others
411
- .. todo: action name=...
493
+ .. todo: def create of offer -> write state of the property to offer received
494
+ .. todo: def unlink: _unlink_if_state_is_valid (new or cancelled)
412
495
413
496
.. _tutorials/server_framework_101/action_type_actions :
414
497
@@ -430,6 +513,8 @@ attribute should reference the XML ID of the action to execute.
430
513
<reference/view_architectures/form/button>` and :ref: `headers
431
514
<reference/view_architectures/form/header>` in form views.
432
515
516
+ .. todo: "view offers" statbutton with count + remove notebook page of offers
517
+
433
518
.. _tutorials/server_framework_101/object_type_actions :
434
519
435
520
Model-defined actions
@@ -444,6 +529,10 @@ To link a button to a model-defined action, its `type` attribute must be set to
444
529
method receives the current recordset through `self ` and should return a dictionary acting as an
445
530
action descriptor.
446
531
532
+ .. todo: accept/refuse offer buttons -> auto refuse others when accepting (write)
533
+ .. todo: multi-checkbox refuse offers in bulk
534
+ .. todo: "assign myself as salesperson" action
535
+
447
536
----
448
537
449
538
.. todo: add incentive for chapter 6
0 commit comments