Odoo 11 How to override compute_all() method? - python

I am developping a module to use a custom formula for the amount calculation method in invoices and sales orders.
The line amount formula should be : nbJours * price_unit * quantity instead of the default formula : price_unit * quantity
I added a custom field to AccountInvoiceLine class by inheriting it as following :
class AccountInvoiceLine(models.Model):
_inherit = "account.invoice.line"
# Nombre de jours de location
nombreJours = fields.Integer("Nombre de jours",default=1,required=True)
#api.multi
#api.depends('nombreJours','price_unit', 'discount', 'invoice_line_tax_ids', 'quantity', 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id', 'invoice_id.date_invoice', 'invoice_id.date')
def _compute_price(self):
...
...
if self.invoice_line_tax_ids:
taxes = self.invoice_line_tax_ids.compute_all(self.nombreJours, price, currency, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
# Calcul du sous-total de la ligne
self.price_subtotal = price_subtotal_signed = taxes['total_excluded'] if taxes else self.quantity * price * self.nombreJours
self.price_total = taxes['total_included'] if taxes else self.price_subtotal
...
...
And I also need to set the custom formula in the AccountTax class. I tried to override the compute_all() method :
# Modification du modèle de Taxes
class AccountTax(models.Model):
_inherit = 'account.tax'
#api.multi
def compute_all(self, nbJrs=1, price_unit=1, currency=None, quantity=1.0, product=None, partner=None):
...
...
if not base_values:
odooAmount = price_unit * quantity
customAmount = nbJrs * odooAmount
total_excluded = total_included = base = round( customAmount , prec)
else:
total_excluded, total_included, base = base_values
...
...
return {
'taxes': sorted(taxes, key=lambda k: k['sequence']),
'total_excluded': currency.round(total_excluded) if round_total else total_excluded,
'total_included': currency.round(total_included) if round_total else total_included,
'base': base,
}
I've successfully updated my module, but when I try to add a product in a new invoice I get this error :
... ... ...
File "/OdooERP/Odoo 11.0/addons/account/models/account_invoice.py", line 618, in _onchange_invoice_line_ids taxes_grouped = self.get_taxes_values()
File "/OdooERP/Odoo 11.0/addons/account/models/account_invoice.py", line 889, in get_taxes_values taxes = line.invoice_line_tax_ids.compute_all(price_unit, self.currency_id, line.quantity, line.product_id, self.partner_id)['taxes']
File "/OdooERP/Instances/xaymalab/addons/sunu_location_event/models/accountinvoice.py",line 82, in compute_all odooAmount = price_unit * quantity
TypeError: unsupported operand type(s) for *: 'res.currency' and 'product.product'
When I remove "nbJrs" parameter in the definition, it works!
The values shift one parameter to the right.
Can anybody help me out regarding it?

You've made a mistake on overriding compute_all() of account.tax. Seems you've added a new parameter nbJrs. The original Method:
def compute_all(self, price_unit, currency=None,
quantity=1.0, product=None, partner=None)
and yours:
def compute_all(self, nbJrs=1, price_unit=1, currency=None,
quantity=1.0, product=None, partner=None):
That just won't work this way. The error is: You're multiplying the value of currency with the value of product, because the values shift one parameter to the right.

How I solved this :
I was trying to override the compute_all() by adding a new parameter. It was the wrong way.
I updated the _compute_price() by adding a new variable "rentalprice" and passing it to compute_all():
def _compute_price(self):
currency = self.invoice_id and self.invoice_id.currency_id or None
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
# Prix unitaire d'un article pendant la durée totale de location:
rentalprice = self.nombrejours * price
taxes = False
if self.invoice_line_tax_ids:
taxes = self.invoice_line_tax_ids.compute_all(rentalprice, currency, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
I removed the AccountTax inheritance code because I no longer need it, updated my module and now it's working.
Thanks to #CZoellner for helping me to better understand what I was missing.

Related

Python Classes and Object assignment

I need to write a program that does the following:
First, find the County that has the highest turnout, i.e. the highest percentage of the
population who voted, using the objects’ population and voters attributes
Then, return a tuple containing the name of the County with the highest turnout and the
percentage of the population who voted, in that order; the percentage should be
represented as a number between 0 and 1.
I took a crack at it, but am getting the following error:
Error on line 19:
allegheny = County("allegheny", 1000490, 645469)
TypeError: object() takes no parameters
Here is what I've done so far. Thank you so much for your help.
class County:
def __innit__(self, innit_name, innit_population, innit_voters) :
self.name = innit_name
self.population = innit_population
self.voters = innit_voters
def highest_turnout(data) :
highest_turnout = data[0]
for County in data:
if (county.voters / county.population) > (highest_turnout.voters / highest_turnout.population):
highest_turnout = county
return highest_turnout
# your program will be evaluated using these objects
# it is okay to change/remove these lines but your program
# will be evaluated using these as inputs
allegheny = County("allegheny", 1000490, 645469)
philadelphia = County("philadelphia", 1134081, 539069)
montgomery = County("montgomery", 568952, 399591)
lancaster = County("lancaster", 345367, 230278)
delaware = County("delaware", 414031, 284538)
chester = County("chester", 319919, 230823)
bucks = County("bucks", 444149, 319816)
data = [allegheny, philadelphia, montgomery, lancaster, delaware, chester, bucks]
result = highest_turnout(data) # do not change this line!
print(result) # prints the output of the function
# do not remove this line!
def __innit__(self, innit_name, innit_population, innit_voters) :
You mispelled __init__

How to fix KeyError in python --> KeyError: 'compensation_remaining_leaves' [duplicate]

This question already has answers here:
I'm getting Key error in python
(8 answers)
Closed 3 years ago.
i have problem with my code, the problem is KeyError, I spent hours but did not find the mistake, KeyError: days_compensation_remaining
Here's my code.
Class and Model:
class HrEmployee(models.Model):
_inherit = 'hr.employee'
days_compensation_remaining = fields.Float(
'Jours de récupération restant',
compute='_compute_days_compensation_remaining',
readonly=True,
inverse='_inverse_days_compensation_remaining',
help='Nombre total des jours de récupération '
)
My first function:
#api.multi
def _inverse_days_compensation_remaining(self):
self.ensure_one()
days_compensation = self.company_id.days_compensation_holidays_status_id
if not compensation_legal_leave:
raise UserError(_("le type de congé pour les jours de récupération n'est pas défini"))
diff = self.days_compensation_remaining - compensation_legal_leave.get_days(
self.id)[compensation_legal_leave.id]['days_compensation_remaining']
If the difference is greater than 0:
if diff > 0:
leave = self.env['hr.holidays'].create(
{
'name': 'Allocation for %s' % self.name,
'employee_id': self.id,
'holiday_status_id': compensation_legal_leave.id,
'type': 'add',
'holiday_type': 'employee',
'number_of_days_temp': diff
}
)
leave.action_approve()
if leave.double_validation:
leave.action_validate()
elif diff < 0:
raise UserError(_('vous ne pouvez pas réduire le nombre de jours'))
My second function:
#api.multi
def _compute_days_compensation_remaining(self):
for r in self:
compensation_legal_leave = r.company_id.days_compensation_holidays_status_id
if not compensation_legal_leave:
raise UserError(_("le type de congé pour les jours de récupération n'est pas défini"))
r.days_compensation_remaining = compensation_legal_leave.get_days(
r.id)[compensation_legal_leave.id]['days_compensation_remaining']
There is nothing wrong in your code, but it is due to the input which you received. A lot of times we don't get the desired values, hence we should write the code such that when a key is not present still our code doesn't break and work as desired.
You should use the input_obj.get('key', default_value) syntax which will help you get through even when the key is not present and if not present then you can give default value. There are other ways to do it such as hasattr which is explain very well here.

Odoo 10 - Retrieve the value of a field of another model

Here is code:
Target model:
class SchoolYears(models.Model):
_name = "ecole.partner.school.years"
_rec_name = "school_years" # POUR ASSIGNER PAR DEFAUT UN AUTRE CHAMP AUTRE QUE NAME
_order = 'id desc'
school_years = fields.Char(string='School year', required=True, copy=False)
year_begin_date = fields.Date(string='Start date', required=True, copy=False)
year_end_date = fields.Date(string='End date', required=True, copy=False)
default_school_year = fields.Boolean(string='Current school year', copy=False)
period_school_year = fields.Boolean(string='Registration period', copy=False)
active = fields.Boolean(default=True)
Targeted fields:-> year_begin_date = fields.Date(string='Start date', required=True, copy=False)
Model where I want to access the fields:
class ResPartnerSchool(models.Model):
_name = 'ecole.partner.school'
_order = 'id desc'
#api.multi
def _get_begin_date(self):
domain = [('period_school_year', '=', False), ('default_school_year', '=', True)]
begin_date_id = self.env['ecole.partner.school.years'].search(domain, limit=1).year_begin_date
begin_date = fields.Date.from_string(begin_date_id)
date_j = datetime.date.today()
if begin_date_id:
if begin_date > date_j:
return begin_date_id
else:
return date_j
...
school_year_id = fields.Many2one(string='Period',
ondelete='SET NULL',
comodel_name="ecole.partner.school.years",
default=_get_period_year)
school_registration = fields.Date(string='Beginning',
copy=False,
default=_get_begin_date)
...
Here is the view:
I want to get the correct start date of the school year related to school_years which is of type char and which is a Many2one in the model ecole.partner.school.
I know there are many ways to do this, especially with a "related field". Except that I have a function that allows me to recover the date of the day as of the beginning of the school year when one is in full in a school period.
Currently my function is written in "hard" -> this is what we see in the variable "domain". And I do not want to use "related field" in my school_registration field.
Do you have an idea to get the right start date when choosing a school year?
Thank you
You can try with a computed fields :
school_year_id = fields.Many2one(string='Period',
ondelete='SET NULL',
comodel_name="ecole.partner.school.years",
default=_get_period_year)
school_registration = fields.Date(string='Beginning',
copy=False,
compute=_get_begin_date)
#api.multi
#api.depends('school_year_id')
def _get_begin_date(self):
for record in self:
record.school_registration = record.school_year_id.year_begin_date

Python Function Exception Error

I've writen a code you'll find below but it doesn't work. I get an indentation error. This is the message I've received from cmd.
Can you tell me where I've done the mistake?
loan = input("indiquer le montant de l'emprunt")
loan = float(loan)
duree = input("indiquer la durée de l'emprunt en mois")
duree = int(duree)
principal = loan / duree
tauxinteret = input("mettre le taux d'intéret")
tauxinteret = float(tauxinteret)
interets = principal * tauxinteret
assurance = input("indiquer le montant des assurances")
assurance = float(assurance)
mensualite = principal + interets + assurance
return mensualite
print("Mr l'esclave, la mensualité à payer s'élève à {} dirhams". format (mensualite)")
calcul_mensualite(mensualite)
Have a look at defining functions in Python (seems like you tried to do that) - after def you need to indent next lines:
def my_function():
print("Hello from a function")
my_function()
And here's the link to the basics:
functions
You seem to have left out the define statement of the method. You will again get an indentation error because you have included a return statement outside of a method. The following code should work fine after removing the return statement.
loan = input("indiquer le montant de l'emprunt")
loan = float(loan)
duree = input("indiquer la durée de l'emprunt en mois")
duree = int(duree)
principal = loan / duree
tauxinteret = input("mettre le taux d'intéret")
tauxinteret = float(tauxinteret)
interets = principal * tauxinteret
assurance = input("indiquer le montant des assurances")
assurance = float(assurance)
mensualite = principal + interets + assurance
print("Mr l'esclave, la mensualité à payer s'élève à {} dirhams". format (mensualite)")
Sorry for you all. there is one missing line in my code:
def calcul_mensualite (loan, principal, duree,tauxinteret, interets, assurance, mensualite):

How to create stock move in Odoo from custom module?

I'm creating a custom module for Odoo.
I have Fabrication Orders with different phases : Order , Preparation , Fabrication , Shipping and Final.
And I have the product_id for the product that I want to Fabricate.
When the "Final" phase comes , the stock for that product need to be increased with the quantity that I choose from a field called "quantity".
Here's the code :
class fabrication_orders(models.Model):
_name = 'proyecto.fabrication_orders'
order_id = fields.Many2one('proyecto.products',required="true",string="Product Id")
name = fields.Char(related='order_id.name',required="true",string="Product Name")
order_number = fields.Char(compute='_get_order_number',string="Order Nº",store="true")
image = fields.Binary(related='order_id.image_medium',string="Image")
quantity = fields.Float(required="true")
date = fields.Datetime(required="true",string="Order Date") ### Order Date
end_date = fields.Datetime(compute='_get_end_date',string="Finish Date",store="true") ### Finish Date
initial_cost = fields.Float(related='order_id.standard_price',string="Initial Cost")
final_cost = fields.Float(compute='_get_order_cost',string="Fabrication Cost")
#venue = fields.Many2one('proyecto.venues',required="true",string="Ship to")
order_state = fields.Selection([
('orden', "Order"),
('preparacion', "Preparation"),
('fabricacion', "Fabrication"),
('envio', "Shipping"),
('final', "Final"),
], default='orden')
#Este metodo pone la fecha final segun la cantidad
#api.depends('date')
def _get_end_date(self):
for d in self:
if d.date:
d.end_date = datetime.now() + timedelta(hours=d.quantity)
#api.depends('order_id')
def _get_order_number(self):
for r in self:
if r.order_id:
r.order_number=str(random.randint(1, 1e6))
#api.multi
def change_order_state(self):
for r in self:
if r.order_state == 'orden':
r.write({'order_state':'preparacion'})
elif r.order_state == 'preparacion':
r.write({'order_state':'fabricacion'})
elif r.order_state == 'fabricacion':
r.write({'order_state':'envio'})
elif r.order_state == 'envio':
r.write({'order_state':'final'})
r.end_date = datetime.now()
elif r.order_state == 'final':
raise ValidationError("La fabricación ha finalizado !")
Can you , please , help to increase the stock quantity of the product ?
Thanks a lot.
I am using Odoo 11. In my use case I wanted to reduce the amount of a product when the stage of my custom model is set to 'done'. Inventory amount is changed by stock.move and stock.move.line. So what I did is creating a stock.move and linked a stock.move.line to it when the state changes to 'done'.
Examples of stock.move creation can be found in addons/stock/tests/test_move.py
Here is a recipe:
(1.) If you do not have one yet, create a location
<record id="location_mylocation" model="stock.location">
<field name="name">MyLocation</field>
<field name="location_id" ref="stock.stock_location_locations_virtual"/>
<field name="usage">inventory</field>
<field name="company_id"></field>
</record>
The usage is set to 'inventory' to reduce the amount of the product. Scrap Orders use the same mechanism.
(2.) Create a stock.move
stock_location = self.env.ref('stock.stock_location_stock')
move = self.env['stock.move'].create({
'name': 'Use on MyLocation',
'location_id': stock_location.id,
'location_dest_id': mylocation.id,
'product_id': product.id,
'product_uom': product.uom_id.id,
'product_uom_qty': qty,
})
move._action_confirm()
move._action_assign()
# This creates a stock.move.line record.
# You could also do it manually using self.env['stock.move.line'].create({...})
move.move_line_ids.write({'qty_done': qty})
move._action_done()

Categories