14
.. i18n: 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.::
16
.. i18n: sale = self.browse(cr, uid, ID)
18
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.::
20
sale = self.browse(cr, uid, ID)
22
.. i18n: (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)
24
(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)
26
.. i18n: 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 Open ERP::
28
.. i18n: country_name = sale.partner_id.address[0].country_id.name
30
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 Open ERP::
32
country_name = sale.partner_id.address[0].country_id.name
34
.. i18n: 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)
36
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)
38
.. i18n: .. code-block:: python
40
.. i18n: cr.execute('select partner_id from sale_order where id=%d', (ID,))
41
.. i18n: partner_id = cr.fetchone()[0]
42
.. i18n: cr.execute('select country_id from res_partner_address where partner_id=%d', (partner_id,))
43
.. i18n: country_id = cr.fetchone()[0]
44
.. i18n: cr.execute('select name from res_country where id=%d', (country_id,))
45
.. i18n: del partner_id
46
.. i18n: del country_id
47
.. i18n: country_name = cr.fetchone()[0]
49
.. code-block:: python
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,))
58
country_name = cr.fetchone()[0]
60
.. i18n: 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:
62
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:
64
.. i18n: * Maybe some parts are in others functions
65
.. i18n: * There may be a loop in different elements
66
.. i18n: * You have to use intermediate variables like country_id
68
* Maybe some parts are in others functions
69
* There may be a loop in different elements
70
* You have to use intermediate variables like country_id
72
.. i18n: The first operation as an object call is much better for several reasons:
74
The first operation as an object call is much better for several reasons:
76
.. i18n: * It uses objects facilities and works with modules inheritances, overload, ...
77
.. i18n: * It's simpler, more explicit and uses less code
78
.. i18n: * It's much more efficient as you will see in the following examples
79
.. i18n: * Some fields do not directly correspond to a SQL field (e.g.: function fields in Python)
81
* It uses objects facilities and works with modules inheritances, overload, ...
82
* It's simpler, more explicit and uses less code
83
* It's much more efficient as you will see in the following examples
84
* Some fields do not directly correspond to a SQL field (e.g.: function fields in Python)
86
.. i18n: Example 2 - Prefetching
87
.. i18n: -----------------------
89
Example 2 - Prefetching
90
-----------------------
92
.. i18n: 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::
94
.. i18n: partner_name = sale.partner_id.name
96
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::
98
partner_name = sale.partner_id.name
100
.. i18n: And this will not generate any SQL query as it has been prefetched by the object relational mapping engine of Open ERP.
102
And this will not generate any SQL query as it has been prefetched by the object relational mapping engine of Open ERP.
104
.. i18n: Loops and special fields
105
.. i18n: ------------------------
107
Loops and special fields
108
------------------------
110
.. i18n: Suppose now that you want to compute the totals of 10 sales order by countries. You can do this in Open ERP within a Open ERP object:
112
Suppose now that you want to compute the totals of 10 sales order by countries. You can do this in Open ERP within a Open ERP object:
114
.. i18n: .. code-block:: python
116
.. i18n: def get_totals(self, cr, uid, ids):
117
.. i18n: countries = {}
118
.. i18n: for sale in self.browse(cr, uid, ids):
119
.. i18n: country = sale.partner_invoice_id.country
120
.. i18n: countries.setdefault(country, 0.0)
121
.. i18n: countries[country] += sale.amount_untaxed
122
.. i18n: return countries
124
.. code-block:: python
126
def get_totals(self, cr, uid, ids):
128
for sale in self.browse(cr, uid, ids):
129
country = sale.partner_invoice_id.country
130
countries.setdefault(country, 0.0)
131
countries[country] += sale.amount_untaxed
134
.. i18n: And, to print them as a good way, you can add this on your object:
136
And, to print them as a good way, you can add this on your object:
138
.. i18n: .. code-block:: python
140
.. i18n: def print_totals(self, cr, uid, ids):
141
.. i18n: result = self.get_totals(cr, uid, ids)
142
.. i18n: for country in result.keys():
143
.. i18n: print '[%s] %s: %.2f' (country.code, country.name, result[country])
145
.. code-block:: python
147
def print_totals(self, cr, uid, ids):
148
result = self.get_totals(cr, uid, ids)
149
for country in result.keys():
150
print '[%s] %s: %.2f' (country.code, country.name, result[country])
152
.. i18n: The 2 functions will generate 4 SQL queries in total ! This is due to the SQL engine of Open ERP that does prefetching, works on lists and uses caching methods. The 3 queries are:
154
The 2 functions will generate 4 SQL queries in total ! This is due to the SQL engine of Open ERP that does prefetching, works on lists and uses caching methods. The 3 queries are:
156
.. i18n: 1. Reading the sale.order to get ID's of the partner's address
157
.. i18n: 2. Reading the partner's address for the countries
158
.. i18n: 3. Calling the amount_untaxed function that will compute a total of the sale order lines
159
.. i18n: 4. Reading the countries info (code and name)
161
1. Reading the sale.order to get ID's of the partner's address
162
2. Reading the partner's address for the countries
163
3. Calling the amount_untaxed function that will compute a total of the sale order lines
164
4. Reading the countries info (code and name)
166
.. i18n: That's great because if you run this code on 1000 sales orders, you have the guarantee to only have 4 SQL queries.
168
That's great because if you run this code on 1000 sales orders, you have the guarantee to only have 4 SQL queries.
174
.. i18n: * IDS is the list of the 10 ID's: [12,15,18,34, ...,99]
175
.. i18n: * The arguments of a function are always the same:
176
.. i18n: - cr: the cursor database (from psycopg)
177
.. i18n: - uid: the user id (for security checks)
178
.. i18n: * 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.
180
* IDS is the list of the 10 ID's: [12,15,18,34, ...,99]
181
* The arguments of a function are always the same:
182
- cr: the cursor database (from psycopg)
183
- uid: the user id (for security checks)
184
* 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.
186
.. i18n: A complete example
187
.. i18n: ------------------
192
.. i18n: Here is a complete example, from the Open ERP official distribution, of the function that does bill of material explosion and computation of associated routings:
194
Here is a complete example, from the Open ERP official distribution, of the function that does bill of material explosion and computation of associated routings:
196
.. i18n: .. code-block:: python
198
.. i18n: class mrp_bom(osv.osv):
200
.. i18n: def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
201
.. i18n: bom_result = False
202
.. i18n: # Why searching on `BoM without parent ?
203
.. i18n: cr.execute('select id from mrp_bom where product_id=%d and bom_id is null
204
.. i18n: order by sequence', (product_id,))
205
.. i18n: ids = map(lambda x: x[0], cr.fetchall())
206
.. i18n: max_prop = 0
207
.. i18n: result = False
208
.. i18n: for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
210
.. i18n: for prop_id in bom.property_ids:
211
.. i18n: if prop_id.id in properties:
213
.. i18n: if (prop>max_prop) or ((max_prop==0) and not result):
214
.. i18n: result = bom.id
215
.. i18n: return result
217
.. i18n: def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
218
.. i18n: factor = factor / (bom.product_efficiency or 1.0)
219
.. i18n: factor = rounding(factor, bom.product_rounding)
220
.. i18n: if factor<bom.product_rounding:
221
.. i18n: factor = bom.product_rounding
223
.. i18n: result2 = []
224
.. i18n: if bom.type=='phantom' and not bom.bom_lines:
225
.. i18n: newbom = self._bom_find(cr, uid, bom.product_id.id,
226
.. i18n: bom.product_uom.id, properties)
228
.. i18n: res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0],
229
.. i18n: factor*bom.product_qty, properties, addthis=True, level=level+10)
230
.. i18n: result = result + res[0]
231
.. i18n: result2 = result2 + res[1]
233
.. i18n: return [],[]
235
.. i18n: if addthis and not bom.bom_lines:
236
.. i18n: result.append(
238
.. i18n: 'name': bom.product_id.name,
239
.. i18n: 'product_id': bom.product_id.id,
240
.. i18n: 'product_qty': bom.product_qty * factor,
241
.. i18n: 'product_uom': bom.product_uom.id,
243
.. i18n: if bom.routing_id:
244
.. i18n: for wc_use in bom.routing_id.workcenter_lines:
245
.. i18n: wc = wc_use.workcenter_id
246
.. i18n: cycle = factor * wc_use.cycle_nbr
247
.. i18n: result2.append({
248
.. i18n: 'name': bom.routing_id.name,
249
.. i18n: 'workcenter_id': wc.id,
250
.. i18n: 'sequence': level,
251
.. i18n: 'cycle': cycle,
252
.. i18n: 'hour': wc_use.hour_nbr + (
253
.. i18n: wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
254
.. i18n: (wc.time_efficiency or 1
256
.. i18n: for bom2 in bom.bom_lines:
257
.. i18n: res = self._bom_explode(cr, uid, bom2, factor, properties,
258
.. i18n: addthis=True, level=level+10)
259
.. i18n: result = result + res[0]
260
.. i18n: result2 = result2 + res[1]
261
.. i18n: return result, result2
263
.. code-block:: python
265
class mrp_bom(osv.osv):
267
def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
269
# Why searching on `BoM without parent ?
270
cr.execute('select id from mrp_bom where product_id=%d and bom_id is null
271
order by sequence', (product_id,))
272
ids = map(lambda x: x[0], cr.fetchall())
275
for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
277
for prop_id in bom.property_ids:
278
if prop_id.id in properties:
280
if (prop>max_prop) or ((max_prop==0) and not result):
284
def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
285
factor = factor / (bom.product_efficiency or 1.0)
286
factor = rounding(factor, bom.product_rounding)
287
if factor<bom.product_rounding:
288
factor = bom.product_rounding
291
if bom.type=='phantom' and not bom.bom_lines:
292
newbom = self._bom_find(cr, uid, bom.product_id.id,
293
bom.product_uom.id, properties)
295
res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0],
296
factor*bom.product_qty, properties, addthis=True, level=level+10)
297
result = result + res[0]
298
result2 = result2 + res[1]
302
if addthis and not bom.bom_lines:
305
'name': bom.product_id.name,
306
'product_id': bom.product_id.id,
307
'product_qty': bom.product_qty * factor,
308
'product_uom': bom.product_uom.id,
311
for wc_use in bom.routing_id.workcenter_lines:
312
wc = wc_use.workcenter_id
313
cycle = factor * wc_use.cycle_nbr
315
'name': bom.routing_id.name,
316
'workcenter_id': wc.id,
319
'hour': wc_use.hour_nbr + (
320
wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
321
(wc.time_efficiency or 1
323
for bom2 in bom.bom_lines:
324
res = self._bom_explode(cr, uid, bom2, factor, properties,
325
addthis=True, level=level+10)
326
result = result + res[0]
327
result2 = result2 + res[1]
328
return result, result2