I created a function fields that has this declaration :
'purchase_id': fields.function(_get_purchase_id, fnct_inv=_set_purchase_id, method=True, type="many2one", relation="purchase.order", string="Bon de commande"),
The problem is that the fnct_inv is not triggered and the field purchase_id remains empty.
Here is the definition of the method _set_purchase_id :
def _set_purchase_id(self, cr, uid, ids, field_name, field_value, arg, context):
query = """
SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
WHERE p.id in %s and p.id = m.picking_id and m.purchase_line_id = pol.id and pol.order_id = po.id
GROUP BY picking_id, po.id
"""
cr.execute(query, (tuple(ids), ))
picks = cr.fetchall()
_logger.info("============================================ %s " % ids)
for pick_id, po_id in picks:
self.write(cr, uid, [pick_id], {'purchase_id': [(4, po_id)]}, context=context)
I someone can help me? Or is there another solution? Thank you
The purpose of "inverse" methods in Odoo is not to set the field it is defined on, but to set all necessary fields for the computation of that field, so that the computation will compute the value a user was giving Odoo for that computed field.
Let me give you an example, because i don't want to work on a ready solution for your problem.
If you have a computed field days_since_last_action on a model which is computed by a date field (date_last_action) on the same model, the inverse method won't set days_since_last_action. Instead you have to take the new days_since_last_action value and reverse it to a new date.
That means for
compute method is days_since_last_action = today - date_last_action
inverse method is date_last_action = today - new value of days_since_last_action
In odoo8 we use Computed fields.
purchase_id = fields.Many2one('purchase.order','Bon de commande',compute='_set_purchase_id')
def _set_purchase_id(self, cr, uid, ids, field_name, field_value, arg, context):
query = """
SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
WHERE p.id in %s and p.id = m.picking_id and m.purchase_line_id = pol.id and pol.order_id = po.id
GROUP BY picking_id, po.id
"""
cr.execute(query, (tuple(ids), ))
picks = cr.fetchall()
_logger.info("============================================ %s " % ids)
for pick_id, po_id in picks:
self.write(cr, uid, [pick_id], {'purchase_id': [(4, po_id)]}, context=context)
For more you can refer : https://www.odoo.com/documentation/8.0/howtos/backend.html#computed-fields-and-default-values
Related
There is this field in purchase.order :
'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of receipts that have been generated for this purchase order.")
It seems that it is an one2many field, so it is logic that it has counter part in stock.picking, but no, it hasn't counter part.
The code in _get_picking_ids method looks like :
def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None):
res = {}
for po_id in ids:
res[po_id] = []
query = """
SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
WHERE po.id in %s and po.id = pol.order_id and pol.id = m.purchase_line_id and m.picking_id = p.id
GROUP BY picking_id, po.id
"""
cr.execute(query, (tuple(ids), ))
picks = cr.fetchall()
for pick_id, po_id in picks:
res[po_id].append(pick_id)
return res
Is someone can explain me why? And how to link purchase.order and stock.picking without adding extra field? (because the database is already in prod since 2k15).
The field is computed so there is no need of a counter part like in "real" one2many fields. But you can try to reverse the field picking_ids from purchase.order on stock.picking.
def _get_purchase_order_ids(self, cr, uid, ids, field_names, args, context=None):
res = {}
for pick_id in ids:
res[pick_id] = []
query = """
SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
WHERE p.id in %s and p.id = m.picking_id and m.purchase_line_id = pol.id and pol.order_id = po.id
GROUP BY picking_id, po.id
"""
cr.execute(query, (tuple(ids), ))
picks = cr.fetchall()
for pick_id, po_id in picks:
res[pick_id].append(po_id)
return res
'purchase_order_ids': fields.function(
_get_purchase_order_ids, method=True, type='one2many',
relation='purchase.order', string='Purchase Order List')
This is only a fast example and not tested.
You can search on Odoo were on the purchase_stock module has the linked with "stock.picking" to "purchase.order" field name purchase_id
Thanks
I'm working on migrating an old module from OpenERP 7 to Odoo 12. I'm stuck in this method called _prepare_purchase_order_line, you can find it in model purchase.requisition.
Here is its code :
def make_purchase_order(self, cr, uid, ids, partner_id, context=None):
"""
Create New RFQ for Supplier
"""
context = dict(context or {})
assert partner_id, 'Supplier should be specified'
purchase_order = self.pool.get('purchase.order')
purchase_order_line = self.pool.get('purchase.order.line')
res_partner = self.pool.get('res.partner')
supplier = res_partner.browse(cr, uid, partner_id, context=context)
res = {}
for requisition in self.browse(cr, uid, ids, context=context):
if not requisition.multiple_rfq_per_supplier and supplier.id in filter(lambda x: x, [rfq.state != 'cancel' and rfq.partner_id.id or None for rfq in requisition.purchase_ids]):
raise osv.except_osv(_('Warning!'), _('You have already one %s purchase order for this partner, you must cancel this purchase order to create a new quotation.') % rfq.state)
context.update({'mail_create_nolog': True})
purchase_id = purchase_order.create(cr, uid, self._prepare_purchase_order(cr, uid, requisition, supplier, context=context), context=context)
purchase_order.message_post(cr, uid, [purchase_id], body=_("RFQ created"), context=context)
res[requisition.id] = purchase_id
for line in requisition.line_ids:
purchase_order_line.create(cr, uid, self._prepare_purchase_order_line(cr, uid, requisition, line, purchase_id, supplier, context=context), context=context)
return res
I want to know what is the equivalent of this method in Odoo 12.
Can you help me?
I can see this method exist with the same name in odoo 12 but it is in purchase.requisition.line model.
#api.multi
def _prepare_purchase_order_line(self, name, product_qty=0.0, price_unit=0.0, taxes_ids=False):
self.ensure_one()
requisition = self.requisition_id
if requisition.schedule_date:
date_planned = datetime.combine(requisition.schedule_date, time.min)
else:
date_planned = datetime.now()
return {
'name': name,
'product_id': self.product_id.id,
'product_uom': self.product_id.uom_po_id.id,
'product_qty': product_qty,
'price_unit': price_unit,
'taxes_id': [(6, 0, taxes_ids)],
'date_planned': date_planned,
'account_analytic_id': self.account_analytic_id.id,
'analytic_tag_ids': self.analytic_tag_ids.ids,
'move_dest_ids': self.move_dest_id and [(4, self.move_dest_id.id)] or []
}
I have this function that calculates qty_incoming, but there is an outgoing_qty field that I want to calculate with the same function and not to create separate function for its calculation. how can I do this?
_columns = {
'
'incoming_qty': fields.function(_product_inc_out_qty, type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
string='Incoming'
),
'outgoing_qty': fields.function(_product_inc_out_qty, type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
string='Outgoing'
),
}
function:
def _product_inc_out_qty(self, cr, uid, ids, field_names=None, arg=False, context=None):
if context is None:
context = {}
res = {}
for move_id in ids:
move = self.browse(cr, uid, move_id, context=context)
res[move.id] = move.product_id.incoming_qty or 0.0
return res
if I do something like this, then I get error TypeError: float() argument must be a string or a number
def _product_inc_out_qty(self, cr, uid, ids, field_names=None, arg=False, context=None):
if context is None:
context = {}
res = {}
vals = {
'outgoing_qty': 0.0,
'incoming_qty': 0.0,
}
for move_id in ids:
move = self.browse(cr, uid, move_id, context=context)
vals['outgoing_qty'] = move.product_id.qty_available or 0.0
vals['incoming_qty'] = move.product_id.incoming_qty or 0.0
res[move.id] = vals
return res
Multiple fields can be computed at the same time by the same method, just use the same method on all fields and set all of them:
discount_value = fields.Float(compute='_apply_discount')
total = fields.Float(compute='_apply_discount')
#depends('value', 'discount')
def _apply_discount(self):
for record in self:
# compute actual discount from discount percentage
discount = record.value * record.discount
record.discount_value = discount
record.total = record.value - discount
You can find an example in old api at sale_order
The problem in my code was that in old API if you want to return values for more than 1 field you need to add multi="any_string" to your field
So my fields should look like this
'incoming_qty': fields.function(_product_inc_out_qty, type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
multi='all',
string='Incoming'
),
When creating a new purchase order I want to remove the product_name under the product_id so for that I did this function:
class snc_product(osv.osv):
_name='product.product'
_inherit='product.product'
def name_get(self, cr, uid, ids, context=None):
return_val = super(snc_product, self).name_get(cr, uid, ids, context=context)
res = []
def _name_get(d):
code = d.get('code','')
if d.get('variants'):
code = code + ' - %s' % (d['variants'],)
return (d['id'], code)
for product in self.browse(cr, uid, ids, context=context):
res.append((product.id, (product.code)))
return res or return_val
The problem now is even under description I'm getting the default_code instead of the name.
http://imgur.com/afLNQMS
How could I fix this problem?
Seems like you redefined also the name_get() method of the purchase.order.line model. The second column, named 'Description' is showing the name field ot the purchase.order.line model. That's why I suppose you redefined it.
Your solution is working for me - I have the product code in the first column and the description in the second. Only one thing - you don't need this internal _name_get() method as you don't use it.
Here is the code that worked for me:
from openerp.osv import osv, fields
class snc_product(osv.osv):
_name = 'product.product'
_inherit = 'product.product'
def name_get(self, cr, uid, ids, context=None):
return_val = super(snc_product, self).name_get(cr, uid, ids,
context=context)
res = []
for product in self.browse(cr, uid, ids, context=context):
res.append((product.id, (product.code)))
return res or return_val
snc_product()
How to get the field which is present in sale.order in invoice report. So invoice report use the model account.invoice, if i add a function in report.py it allow only self.cr,self.uid because we are not using osv.memory. So how to get the value of cust_ref_value from sale.order to invoice report.
We can track from the Source Document
In report.py we need to make a function and pass origin in it.
def __init__(self, cr, uid, name, context):
super(your_report_calss_name, self).__init__(cr, uid, name, context)
self.localcontext.update({
'time': time,
'get_cust_ref_val': self._get_cust_ref_val,
})
This method will check like origin is SO or PO or OUT/###:SO### or IN/###:PO### so following case be here
def _get_cust_ref_val(self, origin)
if 'SO' in origin:
if 'OUT/' in origin:
so_name = str(origin).split(':')[1]
sale_id = self.pool.get('sale.order').search(self.cr, self.uid, [('name', '=', so_name)]
if sale_id:
sale = self.pool.get('sale.order').browse(self.cr, self.uid, sale_id)
return sale.cust_ref_value
else:
sale_id = self.pool.get('sale.order').search(self.cr, self.uid, [('name', '=', origin)]
if sale_id:
sale = self.pool.get('sale.order').browse(self.cr, self.uid, sale_id)
return sale.cust_ref_value
else:
return ''
and from the rml side
[[ get_cust_ref_val(inv.origin) ]]