I have this method:
#api.multi
def create_printy(self):
copy_record = self.env['stock.picking']
for record in self:
order_lines = []
for rec in record.order_lines:
order_lines.append(
(0,0,
{
'product_id': rec.isbn.id,
'product_qty': rec.qty,
}
))
sp_types = self.env['stock.picking.type'].search([
('code', '=', 'outgoing')
])
if len(sp_types) > 0:
copy_record.create({
'origin': record.name,
'picking_type_id': sp_types[0].id,
'move_lines': order_lines,
'move_type': 'direct',
'priority': '1',
'company_id': record.company_id.id,
})
I'm trying to create a stock.picking from another model.
But, with this method, I have problems for move_lines which on stock.picking is related to stock.move.
Right now, it throws me this:
Integrity Error
The operation cannot be completed, probably due to the following:
- deletion: you may be trying to delete a record while other records still reference it
- creation/update: a mandatory field is not correctly set
[object with reference: Product Unit of Measure - product.uom]
I know there are a few stock.move required fields which aren't present in my order_lines field.
So, my question is, how can I pass for example product.uom or date_expected which are required from my model?
Is there some similar way as it is done with picking_type_id in my example. for One2many field?
You could take a look of what happens when you do not fill these fields in the interface (and look what they get by default). For example, in stock.move Odoo takes the product_uom from product_id field (through an onchange method). And the date_expected is filled in by default with the current date. So:
#api.multi
def create_printy(self):
copy_record = self.env['stock.picking']
for record in self:
order_lines = []
for rec in record.order_lines:
order_lines.append(
(0,0,
{
'product_id': rec.isbn.id,
'product_uom': rec.isbn.uom_id.id,
'date_expected': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_qty': rec.qty,
}
))
sp_types = self.env['stock.picking.type'].search([
('code', '=', 'outgoing')
])
if len(sp_types) > 0:
copy_record.create({
'origin': record.name,
'picking_type_id': sp_types[0].id,
'move_lines': order_lines,
'move_type': 'direct',
'priority': '1',
'company_id': record.company_id.id,
})
For date_expected you will have to import in your .py file (out of your classes) the following:
import time
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
Related
I have created a custom module, in this module, I have a button. The button should create a delivery order in the inventory module ( just like the confirm button in the Sales module). But when I click it, It gives me the error in the pic, and this is my code.
def delivery_order(self):
delivery = self.env['stock.picking'].create({
# 'type': 'out_invoice',
'state': 'assigned',
'picking_type_id': 1,
'partner_id': self.partner_id.id,
'location_id': 1,
'location_dest_id': 1,
'origin': self.name,
'move_ids_without_package': [(0, 0, {
'product_id': self.product_id.id,
'product_uom_qty': self.selected_weight,
'picking_type_id': 1,
})]})
return delivery
You should add name field in the create method because it is a mandatory field, which causes the error.
can't figure out how to add a line to sale.subscription using the create function from another model
subscription_pak = self.env['product.template'].search([('name', '=', pak_name),('recurring_invoice', '=', True)], limit=1)
subscription_info = {
'partner_id': vals['name'],
}
add_subscription = self.env['sale.subscription'].create(subscription_info)
print('ssssss',subscription_pak)
#works
add_subscription_pak = {
'product_id': subscription_pak.id,
'partner_id': add_subscription.id,
}
link_user_to_subscription = self.env['sale.subscription.line'].create(add_subscription_pak)
I am creating the subscription but can't find the field to use add product to the lines
can you please help
Thanks to odoo Mates on YouTube, this video his from odoo Mates channel How To Update One2many Field From OnChange Of Field in Odoo and #bigbear3001
this is what worked for me
supsctiption_pak = self.env['product.product'].search([('name', '=', pak_name),('recurring_invoice', '=', True)], limit=1)
supsctiption_info = {
'partner_id': vals['name'],
}
add_supsctiption = self.env['sale.subscription'].create(supsctiption_info)
supsctiption_to_pak = self.env['sale.subscription'].search([('partner_id', '=', vals['name'])], limit=1)
add_supsctiption_pak = {
'product_id': supsctiption_pak.product_variant_id.id,
'uom_id': supsctiption_pak.uom_id.id,
'price_unit': supsctiption_pak.list_price,
}
supsctiption_to_pak.write({'recurring_invoice_line_ids':[(5, 0, 0),(0,0,add_supsctiption_pak)]})
your subscription_pak is of type product.template (Product Template) but the product_id field of sale.subscription.line requires a product.product (Product (Variant)) (can't link to it as it's Odoo Enterprise)
so this should work (if you only have one variant on the Product):
...
add_subscription_pak = {
'product_id': subscription_pak.product_variant_id.id,
'partner_id': add_subscription.id,
}
...
for multiple variants:
...
add_subscription_pak = {
'product_id': subscription_pak.product_variant_ids.filter(lambda pv: pv.attribute == 'value')[0].id,
'partner_id': add_subscription.id,
}
...
I am currently using the native module of odoo [Sales] -sales orders and I want to pass the registration of a One2many field at the moment of pressing the button that redirects me to another new formula that I have created and has a field One2Mnay.
example:
this is the native module of odo where I am registering my sales orders:
I have created a new botton that is red says [registrar cotizacion] ,
called method: [open_budget] which allows to open a new form that I created and has a field one2many :
My models :
Models of new form :
class BudgetTwo(models.Model):
_name = 'budget.two'
name = fields.Char(string ='Nombre', copy=False, index=True ,default ="Nuevo")
partner_id =fields.Many2one('res.partner' ,string ='Cliente', copy=False, index=True,required=True)
deliver_date = fields.Date(string ='Fecha de Entrega')
expiration_date = fields.Date(string ='Fecha de expiración')
pay_place =fields.Many2one('account.payment.term' ,string='Plazo de Pago')
order_line = fields.One2many('budget.table','budget_id' ,string = 'Pedidos' )
total = fields.Float(string = 'Total:' ,compute="_total")
btn_d = fields.Boolean(default = False)
Inheritance of the odoo's own module:
class InheritOrder(models.Model):
_inherit = 'sale.order'
#api.multi
def open_budget(self):
if self.order_line:
for element in self.order_line:
data = element.product_id.id
else:print('none')
print(data)
return {
'name': ('Payments'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'budget.two',
'view_id': False,
'type': 'ir.actions.act_window',
'context': {
'default_partner_id': self.partner_id.id,
'default_order_line.product_id': data,
'default_order_line.product_id': data,
},
}
the native module of odoo has a field One2many that are the orders, when I press the new botton loogre redirect to the new form that I created and created a dictionary which has a key called context I can pass the records that I have in the fields but the record that I have in the one2many values are not passed.
Finally :
'context': {
'default_partner_id': self.partner_id.id,
'default_order_line.product_id': data,
'default_order_line.product_id': data,
},
desauld_pernert_id if the record passes but default_order_line.product_id does not pass the record that is the on2many, as you can see:
You can try like this
class InheritOrder(models.Model):
_inherit = 'sale.order'
#api.multi
def open_budget(self):
ctx = dict()
if self.order_line:
products={}
for line in self.order_line:
# You can get whatever field from sale order line
products.update({'product_id': line.product_id.id})
product_line.append((0, 0, products))
ctx.update({
'default_order_line': product_line,
'default_partner_id': self.partner_id.id,
})
return {
'name': ('Payments'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'budget.two',
'view_id': False,
'type': 'ir.actions.act_window',
'context': ctx,
}
There is a special syntax to pass values to x2many fields.
If you want to create new lines you should use:
'context': {
'default_partner_id': self.partner_id.id,
'default_order_line.product_id': [(0, 0, {'field_name': its value,..}),
...,
(0, 0, {'field_name': its value, ..})
]
},
THE CODE
I have the following transient models:
class MoveLotsManager(models.TransientModel):
_name = 'move.lots.manager'
product_lots_available = fields.One2many(
comodel_name='move.product.lot.available',
inverse_name='manager_id',
string='Available lots',
)
class MoveProductLotAvailable(models.TransientModel):
_name = 'move.product.lot.available'
manager_id = fields.Many2one(
comodel_name='move.lots.manager',
string='Lots Manager',
)
name = fields.Char(
string='Name',
)
#api.one
#api.onchange('name')
def onchange_name(self):
# LOGGER 4
_logger.info(self.manager_id)
# LOGGER 5
_logger.info(self.manager_id.id)
As you can see, they both are connected through a 1:N relationship. I open the transient models' views this way:
#api.multi
def open_move_lots_manager_wizard(self):
self.ensure_one()
wizard_id = self.env.ref(
'my_module.view_move_lots_manager_wizard').id
default_lots = [(
(0, 0, {
'name': 'My lot',
})
)]
lots_manager = self.env['move.lots.manager'].create({
'product_lots_available': default_lots,
})
# LOGGER 1
_logger.info(lots_manager)
# LOGGER 2
_logger.info(lots_manager.id)
# LOGGER 3
_logger.info(lots_manager.product_lots_available.mapped('manager_id'))
return {
'name': _('Lots manager'),
'view_type': 'form',
'view_mode': 'form',
'view_id': False,
'res_id': lots_manager.id,
'views': [(wizard_id, 'form'), ],
'res_model': 'move.lots.manager',
'type': 'ir.actions.act_window',
'target': 'new',
'flags': {
'form': {
'action_buttons': False,
},
},
}
MY PURPOSE
In the onchange_name method of the model move.product.lot.available, I want to access to its related manager (and their fields).
THE EXPECTED BEHAVIOUR
Imagine the move.lots.manager I've just created has the ID 11.
LOGGER 1: move.lots.manager(11,)
LOGGER 2: 11
LOGGER 3: move.lots.manager(11,)
LOGGER 4: move.lots.manager(11,)
LOGGER 5: 11
THE CURRENT BEHAVIOUR
LOGGER 1: move.lots.manager(11,)
LOGGER 2: 11
LOGGER 3: move.lots.manager(11,)
LOGGER 4: move.lots.manager(<openerp.models.NewId object at 0x7fd1a60cb850>,)
LOGGER 5: <openerp.models.NewId object at 0x7fd1a60cb850>
MY QUESTION
I know that transient models aren't stored in a permanent way, they're removed after a while, according to the system parameters of the database. But, while they're in the database, they do have an ID. And I can get it as you see, in fact, when I have the transient model form opened (with the developer mode activated) and I click on View Metadata option, I can see ID: 11... So why can't I get it from the "child" transient model?
Take a look into onchange() line 4971 and following. Odoo is creating a new record in cache/memory and will later, after the onchange is done, update the own cache with the provided data.
If you really need the ID or other fields use <record>._origin.<field>.
EDIT: Don't use api.one decorator on onchange methods. They are triggered on singleton records anyway.
I have a field amount on the Journal Entries (account move form) and I need to define an onChange event which automatically inserts the lines once I fill in the amount. But I am not sure how.
Yesterday I had to do something similar to your requirement. I had a salo order(m2o) field in purchase ...on_change of sale_order I had to fill the purchase order lines ...see hope it can help you.
class purchase_order(osv.osv):
_inherit = 'purchase.order'
_columns = {
'sale_order':fields.many2one('sale.order','Sale Order'),
'purchase_type':
fields.selection([
('order','Purchase Order'),
('job','Job Order')
],
'Purchase Type',
required=True,
states={
'confirmed':[('readonly',True)],
'approved':[('readonly',True)],
},
select=True,
help="Define type of purchase order.",
),
}
def onchange_saleorder(self,cr,uid,ids,order,context=None):
res={}
lis=[]
sorder_id=self.pool.get('sale.order').browse(cr,uid,order)
for line in sorder_id.order_line:
print "uom",line.product_id.uom_id.id
res={'product_id':line.product_id.id,
'name':line.product_id.name,
'product_qty':1,
'product_uom':line.product_id.uom_id.id,
'price_unit':line.price_unit,
'date_planned':time.strftime('%Y-%m-%d'),}
lis.append(res)
print "list is .........",lis
res={'value':{'order_line':lis}} // here order_line is o2m field of purchase.
return res
Your on_change method has to return a list of dictionaries made of line values.
For instance:
res['value']['line_ids'] = [
{
'account_id': 1,
'debit': 100,
},
{
'account_id': 2,
'credit': 100,
}]
Also, see the recompute_voucher_lines method of account.voucher as example.
sometimes error '0' will be generate so we have to write a code with like this.
In case of above Example
lis.append((0,0,res))