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:
177
.. i18n: - cr: the cursor database (from psycopg)
178
.. i18n: - uid: the user id (for security checks)
179
.. 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.
181
* IDS is the list of the 10 ID's: [12,15,18,34, ...,99]
182
* The arguments of a function are always the same:
184
- cr: the cursor database (from psycopg)
185
- uid: the user id (for security checks)
186
* 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.
188
.. i18n: A complete example
189
.. i18n: ------------------
194
.. 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:
196
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:
198
.. i18n: .. code-block:: python
200
.. i18n: class mrp_bom(osv.osv):
202
.. i18n: def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
203
.. i18n: bom_result = False
204
.. i18n: # Why searching on BoM without parent ?
205
.. i18n: cr.execute('select id from mrp_bom where product_id=%d and bom_id is null
206
.. i18n: order by sequence', (product_id,))
207
.. i18n: ids = map(lambda x: x[0], cr.fetchall())
208
.. i18n: max_prop = 0
209
.. i18n: result = False
210
.. i18n: for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
212
.. i18n: for prop_id in bom.property_ids:
213
.. i18n: if prop_id.id in properties:
215
.. i18n: if (prop>max_prop) or ((max_prop==0) and not result):
216
.. i18n: result = bom.id
217
.. i18n: max_prop = prop
218
.. i18n: return result
220
.. i18n: def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
221
.. i18n: factor = factor / (bom.product_efficiency or 1.0)
222
.. i18n: factor = rounding(factor, bom.product_rounding)
223
.. i18n: if factor<bom.product_rounding:
224
.. i18n: factor = bom.product_rounding
226
.. i18n: result2 = []
227
.. i18n: phantom = False
228
.. i18n: if bom.type=='phantom' and not bom.bom_lines:
229
.. i18n: newbom = self._bom_find(cr, uid, bom.product_id.id,
230
.. i18n: bom.product_uom.id, properties)
232
.. i18n: res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0],
233
.. i18n: factor*bom.product_qty, properties, addthis=True, level=level+10)
234
.. i18n: result = result + res[0]
235
.. i18n: result2 = result2 + res[1]
236
.. i18n: phantom = True
238
.. i18n: phantom = False
239
.. i18n: if not phantom:
240
.. i18n: if addthis and not bom.bom_lines:
241
.. i18n: result.append(
243
.. i18n: 'name': bom.product_id.name,
244
.. i18n: 'product_id': bom.product_id.id,
245
.. i18n: 'product_qty': bom.product_qty * factor,
246
.. i18n: 'product_uom': bom.product_uom.id,
247
.. i18n: 'product_uos_qty': bom.product_uos and
248
.. i18n: bom.product_uos_qty * factor or False,
249
.. i18n: 'product_uos': bom.product_uos and bom.product_uos.id or False,
251
.. i18n: if bom.routing_id:
252
.. i18n: for wc_use in bom.routing_id.workcenter_lines:
253
.. i18n: wc = wc_use.workcenter_id
254
.. i18n: d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
255
.. i18n: mult = (d + (m and 1.0 or 0.0))
256
.. i18n: cycle = mult * wc_use.cycle_nbr
257
.. i18n: result2.append({
258
.. i18n: 'name': bom.routing_id.name,
259
.. i18n: 'workcenter_id': wc.id,
260
.. i18n: 'sequence': level+(wc_use.sequence or 0),
261
.. i18n: 'cycle': cycle,
262
.. i18n: 'hour': float(wc_use.hour_nbr*mult +
263
.. i18n: (wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
264
.. i18n: (wc.time_efficiency or 1.0)),
266
.. i18n: for bom2 in bom.bom_lines:
267
.. i18n: res = self._bom_explode(cr, uid, bom2, factor, properties,
268
.. i18n: addthis=True, level=level+10)
269
.. i18n: result = result + res[0]
270
.. i18n: result2 = result2 + res[1]
271
.. i18n: return result, result2
273
.. code-block:: python
275
class mrp_bom(osv.osv):
277
def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
279
# Why searching on BoM without parent ?
280
cr.execute('select id from mrp_bom where product_id=%d and bom_id is null
281
order by sequence', (product_id,))
282
ids = map(lambda x: x[0], cr.fetchall())
285
for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
287
for prop_id in bom.property_ids:
288
if prop_id.id in properties:
290
if (prop>max_prop) or ((max_prop==0) and not result):
295
def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
296
factor = factor / (bom.product_efficiency or 1.0)
297
factor = rounding(factor, bom.product_rounding)
298
if factor<bom.product_rounding:
299
factor = bom.product_rounding
303
if bom.type=='phantom' and not bom.bom_lines:
304
newbom = self._bom_find(cr, uid, bom.product_id.id,
305
bom.product_uom.id, properties)
307
res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0],
308
factor*bom.product_qty, properties, addthis=True, level=level+10)
309
result = result + res[0]
310
result2 = result2 + res[1]
315
if addthis and not bom.bom_lines:
318
'name': bom.product_id.name,
319
'product_id': bom.product_id.id,
320
'product_qty': bom.product_qty * factor,
321
'product_uom': bom.product_uom.id,
322
'product_uos_qty': bom.product_uos and
323
bom.product_uos_qty * factor or False,
324
'product_uos': bom.product_uos and bom.product_uos.id or False,
327
for wc_use in bom.routing_id.workcenter_lines:
328
wc = wc_use.workcenter_id
329
d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
330
mult = (d + (m and 1.0 or 0.0))
331
cycle = mult * wc_use.cycle_nbr
333
'name': bom.routing_id.name,
334
'workcenter_id': wc.id,
335
'sequence': level+(wc_use.sequence or 0),
337
'hour': float(wc_use.hour_nbr*mult +
338
(wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
339
(wc.time_efficiency or 1.0)),
341
for bom2 in bom.bom_lines:
342
res = self._bom_explode(cr, uid, bom2, factor, properties,
343
addthis=True, level=level+10)
344
result = result + res[0]
345
result2 = result2 + res[1]
346
return result, result2