How to unlink One2many Field values on onchange in Odoo - python

I created a module to add sale order lines as per customer to PO Order lines.
Selecting customer
When i add Sale Order it will be added in PO order.
SO added
I was trying to unlink a specific ids from one2many field. in picture Add SO fields
class PurchaseOrder(models.Model):
_inherit= "purchase.order"
_name = "purchase.order"
order_ids = fields.Many2many('sale.order', String="Add Order",domain="[('partner_id', 'child_of', partner_id),('state', 'in', ('quotation','socreated','done'))]")
#api.onchange('order_ids')
def orders_change(self):
if not self.order_ids:
return {}
if not self.partner_customer_id:
warning = {
'title': _('Warning!'),
'message': _('You must first select a partner!'),
}
# self.order_ids =False
return {'warning': warning}
line_ids = []
u_ids=[]
new_lines = self.env['purchase.order.line']
for qt in self.order_ids:
for i in self.order_line.mapped('sale_order_id'):
line_ids.append(i)
for u in self.order_ids:
if u.id in line_id:
u_ids.append(u)
line_ids.remove(u)
if line_ids and u_ids:
lp = self.order_line.filtered(lambda r: r.sale_order_id <= line_ids[0])
lp2 = self.order_line.filtered(lambda r: r.sale_order_id <= u_ids[0])
for line in self.order_line:
if line in lp:
# self.order_line = [(6, 0, lp2.ids)]
line.unlink()
continue
for line in qt.order_line:
# Load a PO line only once
if line in self.order_line.mapped('sale_order_line_id'):
continue
#seller section
seller = line.product_id._select_seller(
line.product_id,
partner_id=self.partner_id,
quantity=line.product_uom_qty,
date=self.date_order and self.date_order[:10],
uom_id=line.product_uom)
price_unit = self.env['account.tax']._fix_tax_included_price(seller.price,
line.product_id.supplier_taxes_id,
line.tax_id) if seller else 0.0
if price_unit and seller and self.currency_id and seller.currency_id != self.currency_id:
price_unit = seller.currency_id.compute(price_unit, self.currency_id)
if seller and line.product_uom and seller.product_uom != line.product_uom:
price_unit = self.env['product.uom']._compute_price(seller.product_uom.id, price_unit,
to_uom_id=line.product_uom.id)
unit = price_unit
qty = line.product_uom_qty
if float_compare(qty, 0.0, precision_rounding=line.product_uom.rounding) <= 0:
qty = 0.0
tax_id = line.tax_id or line.product_id.taxes_id
data = {
'sale_order_line_id': line.id,
'name': line.name,
'sequence_number':line.sequence_number,
'product_id': line.product_id.id,
'product_qty': qty,
'product_uom': line.product_id.uom_po_id or line.product_id.uom_id,
'price_unit': unit,
'cpo_no' : line.order_id.cpo_number,
'cpo_product_qty': qty,
'cpo_product_uom': line.product_id.uom_id,
'cpo_price_unit': line.price_unit,
'discount': 0.0,
'date_planned':(datetime.today() + relativedelta(weeks=4)).strftime(DEFAULT_SERVER_DATETIME_FORMAT),
}
new_line = new_lines.new(data)
new_line._set_additional_fields(self)
new_lines += new_line
if new_lines :
self.order_line += new_lines
class PurchaseOrderLine(models.Model):
_inherit= "purchase.order.line"
_name = "purchase.order.line"
sale_order_line_id = fields.Many2one('sale.order.line', 'Order Line', ondelete='set null', select=True
)
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order',
store=False)
When i remove a order_ids , i want to unlink related lines from order_line (po)
link_ids will hold order_ids when it selected, when a id removed from order_ids it will be removed from link_ids.
u_ids will hold the rest of order_id when it deleted.
when i remove a id from order_ids iwant to unlink the related line from order_line
but i cant delete it.
i have user [6,0,ids] method to replace values , it wont work in create state.
Please help me.

new_lines = self.env['purchase.order.line']
for qt in self.order_ids:
for i in self.order_line.mapped('sale_order_id'):
line_id.append(i.id)
line_ids.append(i)
for u in self.order_ids:
if u.id in line_id:
u_id.append(u.id)
line_id.remove(u.id)
u_ids.append(u)
line_ids.remove(u)
k = self.order_line.mapped('sale_order_id')
if line_id and line_ids and u_ids:
lp = self.order_line.filtered(lambda r: r.sale_order_id <= line_ids[0])
lp6 = self.order_line.filtered(lambda r: r.sale_order_id not in line_ids)
lp2 = self.order_line.filtered(lambda r: r.sale_order_id in u_ids)
for line in self.order_line:
if line in lp:
self.update({
'order_line': [(5, _, _)],
})
for o in lp6:
data = {
'sale_order_line_id': o.sale_order_line_id.id,
'name': o.name,
'sequence_number': o.sequence_number,
'product_id': o.product_id.id,
'product_qty': o.product_qty,
'product_uom': o.product_uom,
'price_unit': o.price_unit,
'cpo_no': o.cpo_no,
'cpo_product_qty': o.cpo_product_qty,
'cpo_product_uom': o.cpo_product_uom,
'cpo_price_unit': o.cpo_price_unit,
# 'quote_ref':line.order_id.origin,
'discount': 0.0,
'date_planned': (datetime.today() + relativedelta(weeks=4)).strftime(
DEFAULT_SERVER_DATETIME_FORMAT),
}
for line in qt.order_line:
# Load a PO line only once
if line in self.order_line.mapped('sale_order_line_id'):
continue
new_line = new_lines.new(data)
new_line._set_additional_fields(self)
new_lines += new_line
new_line = False
continue

Sorry i'm late it's been a log time:
try to remove every record by using [(5,0,0)] then add the ids. this worked for me but the only problem is that it work if an other field trigger the onchange method not the same many2many or one2many field
but in create mode if you didn't find any solution why don't you use update button when he change the selected order ids he needs to validate before showing the one2many field and this way you will work on write always because create will happen when he validate the the choices. what i'm trying to say is find what it work and work arround it to find a way to use it

For the field you used to make relation:-
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order',
store=False)
please try to give attribute ondelete = 'cascade' and change the line as :-
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order', ondelete ='cascade'
store=False)
If you put like this , when the related sale order is deleted the one2many entry with relation record will be also removed. This is just an example and you could try this way.
Thanks

Following is a common logic for deleting a record in one2many
#api.onchage('field_name')
def onchange_field_name(self):
for line in self.one2many_field_records:
if line.order_id == 'your_satisfying_condition':
line.unlink()
This is the usual way to delete the order line records

Related

How to copy purchase order value to account.invoice in odoo11

I have created a custom field in the purchase order and another in account.invoice with the same name budget_id, when I create a purchase order and create a bill from this order I would like to copy the value of budget_id in the purchase order to the budget_id in the bill.
I have overridden the smart button method "action_view_invoice" in purchase.order and added my code but nothing happen. Are there other ways to do that?
Thanks in advance!
my code
#api.multi
def action_view_invoice(self):
action = self.env.ref('account.action_invoice_tree2')
result = action.read()[0]
result['context'] = {'type': 'in_invoice', 'default_purchase_id': self.id}
if not self.invoice_ids:
# Choose a default account journal in the same currency in case a new invoice is created
journal_domain = [
('type', '=', 'purchase'),
('company_id', '=', self.company_id.id),
('currency_id', '=', self.currency_id.id),
]
default_journal_id = self.env['account.journal'].search(journal_domain, limit=1)
if default_journal_id:
result['context']['default_journal_id'] = default_journal_id.id
else:
# Use the same account journal than a previous invoice
result['context']['default_journal_id'] = self.invoice_ids[0].journal_id.id
# choose the view_mode accordingly
if len(self.invoice_ids) != 1:
result['domain'] = "[('id', 'in', " + str(self.invoice_ids.ids) + ")]"
elif len(self.invoice_ids) == 1:
res = self.env.ref('account.invoice_supplier_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = self.invoice_ids.id
result['context']['default_origin'] = self.name
result['context']['default_reference'] = self.partner_ref
result['context']['default_budget_id'] = self.budget_id.id # my code here
return result
The account.invoice object has the field purchase_id which is a link with purchase.order.
So in the account.invoice you can get any of the way(like in the vendor bill creation best approch) the purchase order budget_id field value into account.invoice budget_id field.
Code link - Purchase link In Invoice

add many2many and one2many record through python code -Odoo 12 / Python 3.7

i am stuck since a couple of days in some many2many and one2many field i am trying to add value to through code. The idea is to consume a web service to get some data from a third party website and re-use these same data in my odoo 12 modules. I was able to parse the JSON request and create entities using model.Models. However, for fields using one2many or many2many i was unable to add their values and link them to my models. Here is my python code. What i want to achieve is after the creation of a record "book.db" i want to also create its category in the same time and link it to the current record. However everytime i run the code only the "book.db" model is created. The category model is never made nor linked. Can someone point me to the right direction or correct my code if possible. Thanks alot.
from odoo import models, fields, api
from . import prestashopproduct
import requests
import json
class Book(models.Model):
_name = "book.db"
prestashop_id = fields.Integer('Prestashop ID')
title = fields.Char(string="book title")
ean13_code = fields.Char(string="EAN13")
author = fields.Char(string="book author")
released = fields.Date(string="Date de publication")
type = fields.Selection([('Numérique', 'Numérique'), ('Papier', 'Papier')], string="type")
catalog = fields.Char(string="catalogue")
collection = fields.Char(string="collection")
isbn = fields.Char(string="Numero ISBN")
description = fields.Html("Description")
distributeur = fields.Char(string="Distribiteur")
code_distribiture = fields.Char(string="Code distribiteur")
code_collection = fields.Char(string="Code collection")
code_dispo = fields.Char(string="Code dispo")
code_tva1 = fields.Integer("Code tva1")
code_tva2 = fields.Integer("Code tva2")
presentation = fields.Html("Presentation")
type_produit = fields.Char(string="Type de produit")
theme_edilectre = fields.Char(string="Type de produit")
categorie = fields.Html("categorie")
poid = fields.Float("Poid en Gramme")
prix = fields.Float("Prix")
largeur = fields.Float("Largeur en MM")
epaisseur = fields.Float("Epaisseur en MM")
hauteur = fields.Float("Hauteur en MM")
image = fields.One2many('product.images', 'product_id', string='Imags du produit')
cate = fields.Many2many('product.cetegorie', 'product_id', string='Imags du produit')
image_product = fields.Char("Image")
#api.one
def get_books(self):
jso = prestashopproduct.Product.get_full_stock(self=prestashopproduct.Product())
for j in jso['products']:
if self.check_unicity(j['id']):
book = [{'title': j['name'][1]['value'],
'ean13_code': j['ean13'],
'isbn': j['isbn'],
'epaisseur': j['width'],
'largeur': j['depth'],
'hauteur': j['height'],
'poid': j['weight'],
'prestashop_id': j['id'],
'description': j['description'][1]['value'],
'presentation': j['description_short'][1]['value'],
'categorie': j['description_short'][1]['value']}]
record = self.create(book)
print (self.id)
record.cate.create({'cate': [{'product_id': record.id, 'name': 'absc'}]})
def check_unicity(self, id):
if self.search_count([('prestashop_id', '=', id)]) > 0:
return False
else:
return True
class Image(models.Model):
_name = 'product.images'
product_id = fields.Many2many('book.db', string='Prestashop ID')
product_image = fields.Binary('Image du produit')
product_image_url = fields.Char("product_image")
def donload_product_image(self, product_id, image_id):
image = prestashopproduct.Product.get_product_image(prestashopproduct.Product(), id_product=product_id,
id_image=image_id)
return image
class Categories(models.Model):
_name = 'product.cetegorie'
product_id = fields.Many2many('book.db', string="Categories")
nb_products_recursive = fields.Integer("nb_products_recursive")
name = fields.Char("Descriptif")
#api.one
def new_record(self, product_id):
self.create([{'product_id': product_id, 'name': 'a'}])
#api.model
def _repare_cate_list(self, cates=[]):
post_cates = []
existing_add = []
for cate_name in cates:
cate_ids = self.env['product.cetegorie'].search([('name', '=', cate_name)])
if cate_ids:
existing_add.append(int(cate_ids[0]))
else:
post_cates.append((0, 0, {'name': cate_name}))
post_cates.insert(0, [6, 0, existing_add])
return post_cates
#api.one
def get_books(self):
jso = prestashopproduct.Product.get_full_stock(self=prestashopproduct.Product())
for j in jso['products']:
if self.check_unicity(j['id']):
cate_vals = self._repare_cate_list(['absc'])
book = [{'title': j['name'][1]['value'],
'ean13_code': j['ean13'],
'isbn': j['isbn'],
'epaisseur': j['width'],
'largeur': j['depth'],
'hauteur': j['height'],
'poid': j['weight'],
'prestashop_id': j['id'],
'description': j['description'][1]['value'],
'presentation': j['description_short'][1]['value'],
'categorie': j['description_short'][1]['value'],
'cate':cate_vals
}]
record = self.create(book)
Also remove line product_id = fields.Many2many('book.db', string="Categories") from the 'product.cetegorie' model its not needed. As Many to many is using separate table to link categories to save them.
Whenever you want to edit, update or delete One2many or Many2many field(s) please refer below lines.
(0, 0, {values}) link to a new record that needs to be created with
the given values ​​dictionary
(1, ID, {values}) update the linked record with id = ID (write values
​​on it)
(2, ID) remove and delete the linked record with id = ID (calls unlink
on ID, that will delete the object completely, and the link to it as
well)
(3, ID) cut the link to the linked record with id = ID (delete the
relationship between the two objects but does not delete the target
object itself)
(4, ID) link to existing record with id = ID (adds a relationship)
(5) unlink all (like using (3, ID) for all linked records)
(6, 0, [IDs]) replace the list of linked IDs (like using (5) then (4,
ID) for each ID in the list of IDs)

How to split a text file into a nested array?

Working on a project creating a python flask website that stores user logins into a text file. I have a text file where each line is one user and each user has 5 parameters stored on the line. All user parameters are separated by a ; character.
Parameters are:
username
password
first name
last name
background color
title
avatar
Sample of the text file:
joebob;pass1;joe;bob;yellow;My title!!;https://upload.wikimedia.org/wikipedia/commons/c/cd/Stick_Figure.jpg
richlong;pass2;rich;long;blue;My title2!!;https://www.iconspng.com/images/stick-figure-walking/stick-figure-walking.jpg
How do I go about storing the parameters into a python array, and how do I access them later when I need to reference log-ins.
Here is what I wrote so far:
accounts = { }
def readAccounts():
file = open("assignment11-account-info.txt", "r")
for accounts in file: #line
tmp = accounts.split(';')
for data in tmp: #data in line
accounts[data[0]] = {
'user': data[0],
'pass': data[1],
'first': data[2],
'last': data[3],
'color': data[4],
'title': data[5],
'avatar': data[6].rstrip()
}
file.close()
You can use the python builtin csv to parse
import csv
with open("assignment11-account-info.txt", "r") as file:
reader = csv.reader(file, delimiter=';')
result = []
for row in reader:
fields = ('user', 'passwd', 'first', 'last', 'color','title','avatar')
res = dict(zip(fields, row))
result.append(res)
Or equivalent but harder to read for a beginner the pythonic list comprehension:
with open("assignment11-account-info.txt", "r") as file:
reader = csv.reader(file, delimiter=';')
fields = ('user', 'passwd', 'first', 'last', 'color','title','avatar')
result = [ dict(zip(fields, row)) for row in reader ]
Here's what I might do:
accounts = {}
with open("assignment11-account-info.txt", "r") as file:
for line in file:
fields = line.rstrip().split(";")
user = fields[0]
pass = fields[1]
first = fields[2]
last = fields[3]
color = fields[4]
title = fields[5]
avatar = fields[6]
accounts[user] = {
"user" : user,
"pass" : pass,
"first" : first,
"last" : last,
"color" : color,
"title" : title,
"avatar" : avatar
}
By using with, the file handle file is closed for you automatically. This is the most "Python"-ic way of doing things.
So long as user is unique, you won't overwrite any entries you put in as you read through the file assignment11-account-info.txt.
If you need to deal with a case where user is repeated in the file assignment11-account-info.txt, then you need to use an array or list ([...]) as opposed to a dictionary ({...}). This is because reusing the value of user will overwrite any previous user entry you add to accounts. Overwriting existing entries is almost always a bad thing when using dictionaries!
If that is the case, I might do the following:
accounts = {}
with open("assignment11-account-info.txt", "r") as file:
for line in file:
fields = line.rstrip().split(";")
user = fields[0]
pass = fields[1]
first = fields[2]
last = fields[3]
color = fields[4]
title = fields[5]
avatar = fields[6]
if user not in accounts:
accounts[user] = []
accounts[user].append({
"user" : user,
"pass" : pass,
"first" : first,
"last" : last,
"color" : color,
"title" : title,
"avatar" : avatar
})
In this way, you preserve any cases where user is duplicated.

Odoo 13 : How to write a good filters in order to sent data to odoo website page?

I've been asked to do some filters before passing the data to the website.
I have four(4) models that are linked with many2many fields. Let me add an image of the four models.
In order to print the model.a, we need to check if it has model.b linked to it, then check if some model.c is linked to model.b and finally, check if some model.d is linked to model.c. After all of that. The result is the same as this image
To do that, I wrote this code :
#http.route(['/agenda'], auth="public", website=True)
def agenda(self):
months = DATES_SELECT
# all dictionary used in the implementation
model_c_dict = {}
model_b_dict = {}
model_a_dict = {}
model_a_key = []
# filter the model.d according to certain condition
# should I set registrations_left field as store=True for performance when using .search()
model_d_ids = request.env['model.d'].search([('date_start', '>', dt.now().date()), ('state', '=', 'opened')], order="date_start").filtered(lambda k: k.registrations_left != 0)
for session in model_d_ids:
course_id = session.course_id_many[:1]
if not course_id.state == 'validated':
continue
model_c_dict.setdefault(course_id.id, {'object': course_id, 'sessions': []})
model_c_dict[course_id.id]['sessions'].append(session)
for k, v in model_c_dict.items():
category_id = v['object'].category_ids[:1]
if not category_id:
continue
model_b_dict.setdefault(category_id.id, {'object': category_id, 'course': {}})
model_b_dict[category_id.id]['course'].setdefault(k, v)
for k, v in model_b_dict.items():
catalogue_id = v['object'].catalogue_ids[:1]
if not catalogue_id:
continue
model_a_dict.setdefault(catalogue_id.id, {'object': catalogue_id, 'category': {}})
model_a_dict[catalogue_id.id]['category'].setdefault(k, v)
if catalogue_id.id in model_a_dict:
model_a_key.append(catalogue_id)
# sort the model_a with model_a.sequence as key
model_a_key = sorted(list(set(model_a_key)), key=lambda k: k.sequence)
# pack key
dict_key = {'model_a_key': model_a_key}
values = {
'months': months,
'categs': model_a_dict,
'dict_key': dict_key,
}
return request.render('website_custom.agenda', values)
It works as intended, but I don't know if It has performance issues, if it's bad coding, ...
So I'm asking your opinion.
PS: I didn't design the models and its relations.
I loved the slice technique to avoid index out of range error, and can be very usefull to check if the record is connected
all the way up to A (catalogue model) in filtered function k.course_id_many[:1].category_ids[:1].catalogue_ids[:1] but I prefer doing this in the domain:
#http.route(['/agenda'], auth="public", website=True)
def agenda(self):
courses_dict = {}
category_dict = {}
catalogue_dict = {}
# extract all record of Model D connected all the way up to A model
sessions = request.env['model.d'].search([('date_start', '>', dt.now().date()),
('state', '=', 'opened'),
# this will make sure that the record retrieved will be connected to catalogue model (A)
('course_id_many.category_ids.catalogue_ids', '!=', False)], order="date_start") \
.filtered(lambda k: k.registrations_left != 0)
for session in sessions:
# if you want to treat olny the first record you can add the slice on the many2many [:1]
# but I think you will skip the rest of the record in the many2many field
# and if this what you want the loop are not needed at all just do `course = session.course_id_many[0]`
# and do the same for all loops. because you don't have to check if the record are connected we all ready did that in search method
course = session.course_id_many[0]
if not course.state == 'validated': continue # skip validated courses
# add course to dict, and add the session to it's list of sessions
course_obj = courses_dict.setdefault(course.id, {'object': course, 'sessions': []})
course_obj['sessions'].append(session)
category = course.category_ids[0]
# store category, and add course to it's list of courses
category_obj = category_dict.setdefault(category.id, {'object': category, 'course': {}})
category_obj = category_dict[category.id]['course'][course.id] = course_obj
catalogue = category.catalogue_ids[0]
# sotre catalog, and add category to it's categories list
catalogue_dict.setdefault(catalogue.id, {'object': catalogue, 'category': {}})['category'][category.id] = category_obj
# sort catalogue
catalogue_keys = sorted(catalogue_dict.keys(), key=lambda k: catalogue_dict[k]['object'].sequence)
values = {
'months': DATES_SELECT,
'categs': catalogue_dict,
'dict_key': catalogue_keys,
}
return request.render('website_custom.agenda', values)
I hope this work I did the best to check for syntax errors, It should work.

filter items in a one to many field using domain in odoo

I have an onchange function which populates products and services in a one to many field I want to filter the items in such a way that only products are populated while services are left out currently my function populates both product types
on change update lines
#api.multi
#api.onchange('project_id')
def change_project_id(self):
# get project id
project_id = self.project_id.id
domain = [
('project_budget','=',project_id),
('state','=','done')
]
# get approved budget
approved_budget = self.env['project.budget'].search(domain,limit=1)
data = {}
#
new_lines = self.env['custom_stock_requisition.line']
# loop through budget lines
for line in approved_budget.budget_line_items:
##populate items
data = {
'product_id': line.product_id.id,
'quantity': line.product_qty,
'product_uom': line.product_uom.id,
'price_unit': line.price_unit,
'product_categ_id': line.product_categ_id,
'name': line.product_id.name,
'date_planned': datetime.datetime.now()
}
new_line = new_lines.new(data) # adding new items to the model
new_lines += new_line
#
self.requisition_order_line += new_lines # set requisition order lines
return {}
#api.multi
#api.onchange('project_id')
def change_project_id(self):
# get project id
project_id = self.project_id.id
domain = [
('project_budget','=',project_id),
('state','=','done')
]
# get approved budget
approved_budget = self.env['project.budget'].search(domain,limit=1)
data = {}
#
new_lines = self.env['custom_stock_requisition.line']
# loop through budget lines
for line in approved_budget.budget_line_items:
##populate items
if line.product_id.type != 'service:
data = {
'product_id': line.product_id.id,
'quantity': line.product_qty,
'product_uom': line.product_uom.id,
'price_unit': line.price_unit,
'product_categ_id': line.product_categ_id,
'name': line.product_id.name,
'date_planned': datetime.datetime.now()
}
new_line = new_lines.new(data) # adding new items to the model
new_lines += new_line
#
self.requisition_order_line += new_lines # set requisition order lines
return {}

Categories

Resources