Read product qty on lines and on warehouses - python

I need to check in my lines, the products I have, their respective quantities, and know what is the availability of such products in warehouses, the stock.move and stock.picking do something like that, but it's old api, I need a custom method.
This is my method:
class bsi_production_order(models.Model):
_name = 'bsi.production.order'
name = fields.Char('Reference', required=True, index=True, copy=False, readonly='True', default='New')
date = fields.Date(string="Production Date")
production_type = fields.Selection([
('budgeted','Budgeted'),
('nonbudgeted','Non Budgeted'),
('direct','Direct Order'),
], string='Type of Order', index=True,
track_visibility='onchange', copy=False,
help=" ")
notes = fields.Text(string="Notes")
order_lines = fields.One2many('bsi.production.order.lines', 'production_order', states={'finished': [('readonly', True)], 'cancel': [('readonly', True)]}, string="Order lines", copy=True)
print_orders = fields.One2many('bsi.print.order', 'production_orders', string="Print Orders")
warehouse_quantity = fields.Char(compute='quantity', string='Quantity per warehouse')
class bsi_production_order_lines(models.Model):
_name = 'bsi.production.order.lines'
production_order = fields.Many2one('bsi.production.order', string="Production Orders")
isbn = fields.Many2one('product.product', string="ISBN", domain="[('is_isbn', '=', True)]")
qty = fields.Integer(string="Quantity")
consumed_qty = fields.Float(string="Consumed quantity")
remaining_qty = fields.Float(string="Remaining quantity")
I need to check from bsi.production.order on the order_lines One2many field, the isbn which is a product, how much of it is available on all the locations of the system, also, compare it to the qty field, so, from there I can go to another state on the object.
Think about the stock.picking or stock.move objects. It's basically the same logic.
So far, I've tried this method, to check if there is any line on the One2many object.
#api.multi
#api.depends('order_lines', 'order_lines.isbn')
def checkit(self):
#actual_stock = self.env['product.product'].browse(qty_available)
for record in self:
if self.order_lines:
for line in self.order_lines:
if line.isbn:
return line.isbn
else:
raise Warning(('Enter​ ​at least​ ​1​ ​ISBN to produce'))
This works so far, to check whether there is a isbn on the line, or not, I'll need to also check if there is enough on warehouse to make the calculation, and if there is, then proceed to the next stage, I'm only tuck on the stock.location part.
I've checked some other modules on stock management OCA repo, although there are similar routines, I couldn't find something really suitable to this.
There is this method, which seems quite likely what I need:
#api.multi
#api.depends('order_lines', 'order_lines.isbn')
def quantity(self):
for record in self:
warehouse_quantity_text = ''
isbn = self.env['product.product'].sudo().search([('product_tmpl_id', '=', record.id)])
if isbn:
quant_ids = self.env['stock.quant'].sudo().search([('isbn','=',isbn[0].id),('location_id.usage','=','internal')])
t_warehouses = {}
for quant in quant_ids:
if quant.location_id:
if quant.location_id not in t_warehouses:
t_warehouses.update({quant.location_id:0})
t_warehouses[quant.location_id] += quant.qty
tt_warehouses = {}
for location in t_warehouses:
warehouse = False
location1 = location
while (not warehouse and location1):
warehouse_id = self.env['stock.warehouse'].sudo().search([('lot_stock_id','=',location1.id)])
if len(warehouse_id) > 0:
warehouse = True
else:
warehouse = False
location1 = location1.location_id
if warehouse_id:
if warehouse_id.name not in tt_warehouses:
tt_warehouses.update({warehouse_id.name:0})
tt_warehouses[warehouse_id.name] += t_warehouses[location]
for item in tt_warehouses:
if tt_warehouses[item] != 0:
warehouse_quantity_text = warehouse_quantity_text + ' ** ' + item + ': ' + str(tt_warehouses[item])
record.warehouse_quantity = warehouse_quantity_text
But it doesn't works since it needs a field, also, I think it's far complex, there must be an easier way to do this checking.
In a nutshell: I need to check the quantities on the system, compare it to every isbn (product) on the line, which it'll be the qty field, if not enough, do nothing, if there is, then pass to the next state.

First if you want to check if the data is correct use #api.constrains not #api.depends, #api.depends if for computing.
From what is see isbn is many2one to product.product so just make that field required and check if order_lines is empty or not.
#api.constrains('order_lines', 'order_lines.isbn')
def checkit(self):
#actual_stock = self.env['product.product'].browse(qty_available)
for record in self:
# inside the loop use record not self
if self.order_lines:continue # if the order_lines contains one record go back and check the second record
# no need for other instruction because if the field is empty this will full
# another thing if you return the program will exit the function but you only
# checked one record what if someone user write with mutliple record
else: # here order_line is empty
raise Warning(('Enter? ?at least? ?1? ?ISBN to produce'))
but if some how you need to keep it unrequired i think will be much faster.
#api.constrains('order_lines', 'order_lines.isbn')
def checkit(self):
for record in self:
# inside the loop use record not self
if self.order_lines:
found_isbn = False
for line in self.order_lines:
if line.isbn:
found_isbn = True
break # no need to check other lines.
if not found_isbn: # after the looping the lines check if the isbn is found
raise Warning(('Enter at least one ISBN to produce'))
else: # here order_line is empty
raise Warning(('Enter? ?at least? ?1? ?ISBN to produce'))
and about the the quantity i didn't understand exactly what you need but i think this answer will help you a lot.
how to get available quantity of Lot number
and what you need to do is something like this.
if you want just to show a warning to user and don't stop him from working use onchange
#api.onchange('order_lines.qty')
def check_quantity(self):
if self.order_lines:
for line in rec.order_lines:
if line.qty > line.isbn.qty_available:
# return warning or validation error if it's restricted .
return {'warning': {
'title': _('Warning'),
'message': _('Quantity is invalid.')
}
but if this operation is restricted and should not be saved in database use constrains:
#api.constrains('order_lines.qty')
def check_quantity(self):
for rec in self:
if rec.order_lines:
for line in rec.order_lines:
if line.qty > line.isbn.qty_available:
# raise validation error to user .

Related

How to Handle When Request Returns None

I have a list of IDs which corresponds to a set of records (opportunities) in a database. I then pass this list as a parameter in a RESTful API request where I am filtering the results (tickets) by ID. For each match, the query returns JSON data pertaining to the individual record. However, I want to handle when the query does not find a match. I would like to assign some value for this case such as the string "None", because not every opportunity has a ticket. How can I make sure there exists some value in presales_tickets for every ID in opportunity_list? Could I provide a default value in the request for this case?
views.py
opportunities = cwObj.get_opportunities()
temp = []
opportunity_list = []
cw_presales_engineers = []
for opportunity in opportunities:
temp.append(str(opportunity['id']))
opportunity_list = ','.join(temp)
presales_tickets = cwObj.get_tickets_by_opportunity(opportunity_list)
for opportunity in opportunities:
try:
if opportunity['id'] == presales_tickets[0]['opportunity']['id']:
try:
for presales_ticket in presales_tickets:
cw_engineer = presales_ticket['owner']['name']
cw_presales_engineers.append(cw_engineer)
except:
pass
else:
cw_engineer = 'None'
cw_presales_engineers.append(cw_engineer)
except AttributeError:
cw_engineer = ''
cw_presales_engineers.append(cw_engineer)
So, lets say you have a Ticket model and Opportunity model. Connected via a foreign key.
class Opportunity(models.Model):
... some fields here ...
class Ticket(models.Model):
opportunity = models.ForeignKey(Opportunity)
and in your view, you get a list of opportunity ids
def some_view(request):
ids = request.GET['ids']
It sounds, like what you want is to fetch all the tickets for the supplied opportunities and add some default processing for the opportunities that do not have tickets. If that is the case, why not do something like
def some_view(request):
ids = request.GET['ids']
tickets = Ticket.objects.filter(opportunity__id__in=ids)
results = []
for ticket in tickets:
result = ... do your thing here ...
results.append(result)
# now handle missing opportunities
good_ids = tickets.values_list('opportunity__id', flat=True).distinct()
for id in ids:
if id not in good_ids:
result = ... do your default processing ...
results.append(result)
Is that what you are trying to do?

how to compute the average price of each column in purchase order lines?

I want to add a field that will compute the average cost price from vendors line in product. In the template, I got the first line's price works, but if I add a new vendor I get a line error
I added a computed field to product. The template form to get the price from vendors.
#api.one
#api.depends('seller_ids.price')
def av_price(self):
for rec in self:
avg_price_list = [rec.seller_ids.price]
avg_price_list= [float(x) for x in avg_price_list]
try:
rec.av_price = float(sum(avg_price_list) / len(avg_price_list))
except ZeroDivisionError:
if rec.av_price == float("inf") or self.av_price == float("-inf"):
return float('nan') # or x or return whatever makes sense
pass
av_price = fields.Float(string="av price", required=False, compute=av_price )
this the log
File "/home/autoparts/Developments/odoo11/odoo/odoo/models.py", line 4371, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: product.supplierinfo(<odoo.models.NewId object at 0x7f0004af1ee8>, <odoo.models.NewId object at 0x7f0004af1c78>)
The mistake is in [rec.seller_ids.price]. seller_ids will be a recordset with 2 entries, when you add the second one. You can't call attributes/fields on such recordsets directly, but only on singletons (recordsets with exactly one record).
So your method should be more like this one:
#api.depends('seller_ids.price')
def av_price(self):
for rec in self:
price_list = [s.price for s in rec.seller_ids]
if price_list:
rec.av_price = sum(price_list) / len(price_list)
else:
rec.av_price = -1 # can make sense or 0
IMO you should rename the field in avg_price, because it's is more common to abbreviate average into avg. And try to stick to Odoo's guidelines and rename the method to compute_avg_price or compute_av_price.
You are trying to get a field value from a record set using dot notation which will ends with:
ValueError: Expected singleton
If you follow the guidelines about Basic fields and Computed fields. your method may look like the following:
#api.depends('seller_ids.price')
def _compute_av_price(self):
for rec in self:
if rec.seller_ids:
rec.av_price = sum(seller.price for seller in rec.seller_ids) / len(rec.seller_ids)
av_price = fields.Float(string="av price", compute=_compute_av_price)
required – whether the value of the field is required (boolean, by default False)

Odoo: How to create many records in Transient.Model?

This code only creates one record. What is wrong?
class PartnerTagCreate(models.TransientModel):
""" Choose tags to be added to partner."""
_name = 'partner.tags.create'
_description = __doc__
market_id = fields.Many2one('partner.tags', string='Market Tag')
application_id = fields.Many2one('partner.tags', string='Application Tag')
partner_id = fields.Integer()
#api.multi
def create_contact_tag(self):
for record in self.env['sale.order.line'].browse(self._context.get('active_ids', [])):
vals = {}
vals['partner_id'] = record.order_partner_id
self.write(vals)
return True
I need this function to create one record for each order_partner_id I selected before opening the wizard...
How to achieve that?
Here my new code (function) ...
def create_contact_tag(self):
sale_order_line_ids = self.env['sale.order.line'].browse(self._context.get('active_ids', []))
for partner in sale_order_line_ids:
values = {}
values['partner_id'] = partner.order_partner_id
self.create(values)
return {}
This creates one record for marketing_id and/or application_id and dedicated records for each partner_id in the record.
You use the 'create' method to create new records; this is the same for TransientModel as for the persistent Model.
So, replace
self.write(vals)
by
self.create(vals)
and you should be fine.

How to prevent field value repetition on already assigned date in Odoo?

I am working with Odoo 10.
I have a one2many field with two columns in the hr.employee model. If the field "Bonus" (many2one field) is assigned to a particular date, it should not be saved or repeated once again on the same date.
How to achieve this?
Take a look at this below code, this is one possible solution, not the best.
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class HrEmployee(models.Model):
_inherit = 'hr.employee'
prod_details_ids = fields.One2many(
string=u'Product details',
comodel_name='prod.details',
inverse_name='employee_id',
)
class ProdDetails(models.Model):
_name = 'prod.details'
employee_id = fields.Many2one(
string=u'Employee',
comodel_name='hr.employee',
)
date = fields.Date(
string=u'Date',
default=fields.Date.context_today,
)
bonus_id = fields.Many2one(
string=u'Bonus',
comodel_name='res.partner', # just an example
)
And then you need to add the constrains:
Solution 1
_sql_constraints = [
('bonus_unique', 'unique(employee_id, date, bonus_id)',
_('Date + Bonus cannot be repeated in one employee!')),
]
Solution 2
#api.one
#api.constrains('date', 'bonus_id')
def _check_unique_date(self):
# you have more freedom here if you want to check more things
rest = self.employee_id.prod_details_ids - self
for record in rest:
if record.date == self.date and record.bonus_id.id == self.bonus_id.id:
raise ValidationError("Date + Bonus already exists and violates unique field constraint")
Note: If you have date already in your database make sure that the constrains can be added with this data, because if not the constraint cannot be added to the database. This happens with the _sql_constraints at least
Use constrains to stop creating another record with the same name, so duplication of records doesnot occur.
you can use constraints and the search_count() method to check if there is a record. like below
#api.constraints('date')
def validate_date(self):
result = self.search_count([your_domain])
if result:
raise ValidationError(_('Your Text'))

Show one2many field in a tree view of different model [Odoo]

I want to display all the taxes that are applied in a particular invoice and their amounts[in the tree view of a model account.invoice]
This is the output:
The column Tax Lines shows the ids of the taxes that are present in table account.invoice.tax (whereas I want to show their names and corresponding amount)
The model account.invoice has a field called tax_line_ids[Tax Lines] that contains the record of all the taxes on a invoice which is stored in a separate table account.invoice.tax, which in its own tree view looks like this:
I want to extract the tax name and its corresponding amount for it to reflect in account.invoice's tree view
Here's my python code which does'nt seem to work:
#api.one
def taxz(self):
tax_pool = self.pool.get("account.tax")
found_taxes = tax_pool.read(cr, uid, [tax_id,], ["tax_line_ids"], context)
found_tax = found_taxes[0] if found_taxes else None
tax_line_ids = found_tax["tax_line_ids"]
_logger.critical("context type: " + type(context))
_logger.critical("context content: " + str(context))
_logger.critical(tax_line_ids)
xml code for the view:
<field name="tax_line_ids" widget="many2many_tags" />
Try this:
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_ids = fields.Many2many('account.invoice.tax',
'invoice_taxes',
'invoice_id',
'taxt_id',
'List of taxes',
compute='get_tax_list', store=True)
#api.depends('tax_line', 'tax_line.amount')
def get_tax_list(self):
for rec in self:
if rec.taxe_line:
rec.tax_line_ids = [(6,0,rec.tax_line.ids)]
else:
rec.tax_line_ids = [(5,0,0)]
But doing this will just show the list of taxes without amount in your tree view
if you want to show the amount you need to override the name_get method in account.invoice.tax
but this will affect all the x2many fields.
class AcountInvoiceTax(models.Model):
_inherit = 'account.invoice.tax'
#api.multi
def name_get(self):
res = []
for rec in self:
res.append((rec.id, rec.name +': '+ str(rec.amount)))
return res
If you don't want this then you need to change the type to Char and recompute the field
or create another model to save taxes and define name_get for that model.
As you can see this worked for me now if you still getting the keyErro you must be doing some thing wrong check your code for indentation inherit value... :
You can do add a char field just to use it in display in treeview :
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_tree_view = fields.Char(compute='get_tax_list')
#api.multi
def get_tax_list(self):
tax_disp = ""
for rec in self:
if rec.taxe_line:
for tax in taxe_line:
tax_disp = tax_disp + "["+tax.name+"], "
rec.tax_line_tree_view = tax_disp[:-2] # [:-2] just to remove the last ', ' characters.
else:
rec.tax_line_tree_view = tax_disp
In your XML file where tree_view is located, add this field:
<field name="tax_line_tree_view " />
instead of tax_line_ids.
I hope that answer your question.

Categories