I want to get a product's location and display it on a custom report table:
and on the "Warehouse" cell it should be all the product's location, so if that product has multiple it should be displayed there. Just for clarification this is the location I'm talking about:
In order to put that there I tried this code:
class StockInventoryValuationReport(models.TransientModel):
_name = 'report.stock.inventory.valuation.report'
_description = 'Stock Inventory Valuation Report'
location_id = fields.Many2one('stock.location')
# filter domain wizard
#api.multi
def _compute_results(self):
self.ensure_one()
stockquant_obj = self.env['stock.quant'].search([("location_id", "=", self.location_id.id)])
print(stockquant_obj.location_id)
line = {
'name': product.name,
'reference': product.default_code,
'barcode': product.barcode,
'qty_at_date': product.qty_at_date,
'uom_id': product.uom_id,
'currency_id': product.currency_id,
'cost_currency_id': product.cost_currency_id,
'standard_price': standard_price,
'stock_value': product.qty_at_date * standard_price,
'cost_method': product.cost_method,
'taxes_id': product.taxes_id,
'location_id': stockquant_obj.location_id,
}
if product.qty_at_date != 0:
self.results += ReportLine.new(line)
but when I'm printing stockquant_obj.location_id it is an empty recordset basically its not finding any locations. Can someone please hint me on anything?
I actually managed to get the product's locations using this code:
class StockInventoryValuationReport(models.TransientModel):
_name = 'report.stock.inventory.valuation.report'
_description = 'Stock Inventory Valuation Report'
location_id = fields.Many2one('stock.location')
# filter domain wizard
#api.multi
def _compute_results(self):
self.ensure_one()
stockquant_obj = self.env['stock.quant'].search([("location_id", "=", self.location_id.id)])
for xyz in stockquant_obj:
line = {
'name': product.name,
'reference': product.default_code,
'barcode': product.barcode,
'qty_at_date': product.qty_at_date,
'uom_id': product.uom_id,
'currency_id': product.currency_id,
'cost_currency_id': product.cost_currency_id,
'standard_price': standard_price,
'stock_value': product.qty_at_date * standard_price,
'cost_method': product.cost_method,
'taxes_id': product.taxes_id,
'location_id': xyz.location_id,
}
if product.qty_at_date != 0:
self.results += ReportLine.new(line)
I debugged further discovering that now stock.quant() could get some record-set but odoo was expecting a singleton when on my old code was stockquant_obj.location_id so since I have seen from other people that the solution to singleton is a for loop and for that reason I added it.
The problem with this is that now not only the warehouse would be added but the same product would repeat as times as long the recordset is. How can I dodge this? How to tell python that I only need to loop through stockquant_obj and xyz should be inside the line variable?
Related
I have inherited account.move model and added job_card_id field(many2one) in it, as shown as below :
Below given is Image of Selected Job Card :
Below given is code of inherited model :
class CustomInvoice(models.Model):
_inherit = "account.move"
job_card_id = fields.Many2one('job.card', string="Job Card", domain="[('customer_id','=',partner_id)]", tracking=True)
Below given is code of Job Card Line :
class JobCardLine(models.Model):
_name = "job.card.line"
job_card_id = fields.Many2one('job.card', string="Job Card Id", tracking=True)
product_id = fields.Many2one('product.template', string="Product", tracking=True)
quantity = fields.Integer(string="Quantity", tracking=True)
price = fields.Float(
'Sales Price',
digits='Product Price')
total = fields.Integer(string='Total', compute='_compute_total', tracking=True,
help="This field will be calculated from dob !")
employee_id = fields.Many2one('hr.employee', string="Employee", tracking=True)
Now I wanted to add product line of selected job card into Invoice product line automatically when I select the job card.
You need to create an onchange method depending on the job_card_id.
In this onchange, you will add a line to invoice_line_ids.
This will triggers all the other onchange and compute methods and set ups everything needed.
Here is a small example:
class CustomInvoice(models.Model):
_inherit = "account.move"
job_card_id = fields.Many2one('job.card', string="Job Card", domain="[('customer_id','=',partner_id)]", tracking=True)
#api.onchange('job_card_id')
def _onchange_job_card_id(self):
# creates your invoice lines vals according to your values
invoice_lines_vals = self.job_card_id.get_invoice_line_vals()
self.write({'invoice_line_ids': [(6, 0, 0)] + [(0, 0, vals) for vals in invoice_lines_vals]})
The 6 is a command that will deletes all previous invoice_line and the 0 is a creation command.
more info on ORM command
Now, you just need to create the get_invoice_line_vals method in your JobCard class.
It should return a list of vals.
Each vals should be a dict containing some information such as the quantity and the price_unit.
It depends mostly on how you want your data to be calculate.
You just need to return a list like this:
def get_invoice_line_vals(self):
vals_list = []
for job_card_line in self.line_ids:
vals_list.append({
'price_unit': job_card_line.price_unit,
'quantity': job_card_line.quantity
})
return vals_list
I have created new one2many field like order lines in sale after other info tab. Also,I have created a new one2many field like invoice lines in invoice form after other info tab.
What i need to do is, I have to pass values from sale one2many field to invoice one2many field while clicking create invoice button.
I have tried inheriting _prepare_invoice and _prepare_invoice_line function in default. It does not work for other one2many field.
Could anyone please help me to do this!
If you are using Odoo14/Odoo13/Odoo12/Odoo11 then you need to override the sale.order method i.e _prepare_invoice.
Below is the example of Odoo14
#api.model
def _prepare_invoice(self):
"""
Prepare the dict of values to create the new invoice for a sales order. This method may be
overridden to implement custom invoice generation (making sure to call super() to establish
a clean extension chain).
"""
self.ensure_one()
journal = self.env['account.move'].with_context(default_move_type='out_invoice')._get_default_journal()
if not journal:
raise UserError(_('Please define an accounting sales journal for the company %s (%s).') % (self.company_id.name, self.company_id.id))
invoice_vals = {
'ref': self.client_order_ref or '',
'move_type': 'out_invoice',
'narration': self.note,
'currency_id': self.pricelist_id.currency_id.id,
'campaign_id': self.campaign_id.id,
'medium_id': self.medium_id.id,
'source_id': self.source_id.id,
'invoice_user_id': self.user_id and self.user_id.id,
'team_id': self.team_id.id,
'partner_id': self.partner_invoice_id.id,
'partner_shipping_id': self.partner_shipping_id.id,
'fiscal_position_id': (self.fiscal_position_id or self.fiscal_position_id.get_fiscal_position(self.partner_invoice_id.id)).id,
'partner_bank_id': self.company_id.partner_id.bank_ids[:1].id,
'journal_id': journal.id, # company comes from the journal
'invoice_origin': self.name,
'invoice_payment_term_id': self.payment_term_id.id,
'payment_reference': self.reference,
'transaction_ids': [(6, 0, self.transaction_ids.ids)],
'invoice_line_ids': [],
'company_id': self.company_id.id,
'tax_line': [(6, 0, self.tax_line.ids)] //This is my customize field.
}
return invoice_vals
You can't directly pass the o2M which will be entirely different ids.
'account_tax_line_ids': [(6, 0, self.sale_tax_line_ids.ids)] is not possible.
If you need to edit account_tax_line_ids again in the invoice form and
sale_tax_line_ids value should not change, then try below.
#api.multi
def _prepare_invoice(self):
self.ensure_one()
invoice_vals = super(SaleOrder, self)._prepare_invoice()
invoice_vals['account_tax_line_ids'] = [(0, 0, {'FIELD1': line.FIELD1_VALUE, ... } for line in self.sale_tax_line_ids]
return invoice_vals
Specify the fields in side Dictionary.
Read your comments and find out why it's not shown or not getting errors.
In the sale order field:
sale_tax_line_ids = fields.One2many('**sale.order.tax**', 'sale_id', string='Tax Lines', readonly=True, states={'draft': [('readonly', False)]}, copy=True)
And, in the account, move:
account_tax_line_ids = fields.One2many('**account.tax.line**', 'account_line_id', string='Taxes') and you set value like 'account_tax_line_ids': [(6, 0,**self.sale_tax_line_ids.ids**)]
So in the account_tax_line_ids fields, you should set the account.tax.line model records, but you are trying to set sale.order.tax model records.
Also, you can see in the odoo default, like in the sale order line, there is product_id, so it is related to the product.product model, and same with account: move line there product_id, which relates to product.product model. So it sets sale order line product to account move line product.
My goal is to get all items from parent quotation, to the wizard window.
I don't know i do this right way or not, but for now i can get all the products from quotation, and don't understand how to fill them into my wizard items line.
Quotation example
Wizard on that quotation
I remove the pieces of code which not matter.
from odoo import fields, models, api
import logging
class back_to_back_order(models.Model):
_name = "back.to.back.order"
_description = "Back to Back Order"
line_ids = fields.One2many('back.to.back.order.line','back_order_id', 'Order Lines', required=True)
def get_items_from_quotation(self, context):
items = []
quotation_id = context['id']
current_quotation = self.env['sale.order'].search([('id','=',quotation_id)])
if quotation_id == current_quotation.id:
for line in current_quotation.order_line:
item = {
'product_id': line.product_id,
'qty': line.product_uom_qty,
'price': line.price_unit,
'subtotal': line.price_unit*line.product_uom_qty
}
items.append(item)
class back_to_back_order_line(models.Model):
_name = "back.to.back.order.line"
_description = "Back to Back Order"
product_id = fields.Many2one('product.product', 'Product')
back_order_id = fields.Many2one('back.to.back.order', 'Back Order')
qty = fields.Float('Quantity')
price = fields.Float('Unit Price')
subtotal = fields.Float('Subtotal')
Firstly, if you are creating a wizard, then it's very likely you should be using models.TransientModel and not model.Model for your classes.
class back_to_back_order(models.TransientModel):
...
Wizard records are not meant to be persistent; they are automatically deleted from the database after a certain time. This is why they are called transient.
You mentioned you are already able to get the Sales Order Lines within your wizard, but you aren't sure how to fill your Back to Back Order Lines with the data.
One2many and Many2many use a special "commands" format to manipulate the set of records stored in/associated with the field.
I originally found these commands on another answer but they are also covered in the Documentation.
As for your specific application, you should be able to simply create your back.to.back.order.line records and they will be linked as long as you provide the back_order_id.
#api.multi
def get_items_from_quotation(self, context):
self.ensure_one()
b2b_line_obj = self.env['back.to.back.order.line']
quotation = self.env['sale.order'].browse(context['id'])
if quotation:
back_order_id = self.id
for line in quotation.order_line:
b2b_line_obj.create({
'back_order_id': back_order_id,
'product_id': line.product_id.id,
'qty': line.product_uom_qty,
'price': line.price_unit,
'subtotal': line.price_unit * line.product_uom_qty,
})
Put fields of an employee in accounting through a module
Hi. I am creating a module for a client that has a specific need. He wants us to add a price per hour to employees. That is, the price charged by the employee each hour of work. Then create a small expense report that goes into accounting.
The first thing I have done is to modify the employee module using _inherit to add two fields. Nickname that allows employees to filter by nickname. And the hourly price of that employee (what this employee charges for every hour).
employee example changes
The second has been to create a new model that allows adding employees and importing said data. In addition to adding a description.
form example
The challenge now is to link this information to the accounting module so that it will appreciate as a Journal Item and then have the copy confirmed to be appreciated as a Journal Entries.
I am really new in the development of odoo and there are many things that I am still assimilating. So the questions I have are the following:
How could I do this?
Do I have a problem with what I have done so far?
It is my first post and I would appreciate the help. Thanks in advance.
This its the code:
class EmpleadoObra(models.Model):
_inherit = 'hr.employee'
apodo = fields.Char('apodo', readonly=False, store=True)
precio_por_hora = fields.Float('Salario por hora', store=True)
#api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
args = args or []
recs = self.browse()
if name:
recs = self.search(['|', ('apodo', 'ilike', name), ('name', operator, name) ] + args, limit=limit)
return recs.name_get()
class EmpleadosProductos(models.Model):
_name = "employee.as.product"
# _inherits = {'hr.employee' : 'empleado_id'}
employee_line = fields.One2many(
'employee.line',
'id',
string='Employee Lines'
)
class EmployeLine(models.Model):
_name = 'employee.line'
descripcion = fields.Text(string='DescripciĆ³n', required=False)
employee_id = fields.Many2one(
'hr.employee',
string="Empleado",
requiered=True,
change_default=True
)
apodo = fields.Char('apodo', readonly=False)
precio_por_hora = fields.Float('precio_por_hora')
_rec_name = 'apodo'
#api.onchange('employee_id')
def onchange_employee_id(self):
addr = {}
if not self.employee_id.display_name:
return addr
if not self.employee_id.apodo:
self.apodo = "no apodo"
else:
self.apodo = self.employee_id.apodo
self.precio_por_hora = self.employee_id.precio_por_hora
return addr
Partner ledger report name is 'account.report_partnerledger.pdf' by default. I want to change it to customer name (eg: john.pdf if customer name is john). How to do this?
Install report_custom_filename.
Go to Settings > Actions > Reports and search for Partner Ledger.
Fill in the Download filename field. This field is evaluated as jinja2 template with objects being a list of browse records of the records to print, and o the first record. If your model contains a name field, you might write something like ${o.name}_report.pdf as filename.
Possible through complex coding but thanks to odoo community we have one module named
report_custom_filename
which will let you do this by little configuration
Install 'report_custom_filename' module and make the following changes in report_routes method
def report_routes(self, reportname, docids=None, converter=None, **data):
cr, uid, context,registry = request.cr, request.uid, request.context,request.registry
response = super(ReportController, self).report_routes(
reportname, docids=docids, converter=converter, **data)
if docids:
docids = [int(i) for i in docids.split(',')]
report_xml = http.request.session.model('ir.actions.report.xml')
report_ids = report_xml.search(
[('report_name', '=', reportname)])
options_data = simplejson.loads(data['options'])
partner_id = options_data.get('ids')
for report in report_xml.browse(report_ids):
if not report.download_filename:
continue
#objects = http.request.session.model(report.model).browse(docids or [])
objects = request.registry[report.model].browse(cr, uid, partner_id, context=context)
customer_name = str(objects.name)
generated_filename = email_template.mako_template_env\
.from_string(report.download_filename)\
.render({
'objects': objects,
'o': customer_name,
'object': objects[:1],
'ext': report.report_type.replace('qweb-', ''),
})
response.headers['Content-Disposition'] = content_disposition(
generated_filename)
return response