Odoo: updating related field without changing it's original value - python

I am trying to update field price in model custom.sale.line that's related to field product_id.sell_price in model custom.price but without affecting the original value of the latter (in other way i want the cashier to able to change the value on the sales invoice but without changing the value of the product in the database), i'm wondering if there's any change to do so
I tried making another field other_price that takes the value of price, but even with store=True it doesn't save the new value in the database and reverts to the original value
if anyone have a solution for this problem i would be thankful
below is the code
class CustomSaleLine(models.Model):
_name = 'custom.sale.line'
_sql_constraints = [
('product_br_uniq', 'unique(order_id, product_id)',
'Cannot add a product that already exists')]
product_id = fields.Many2one(comodel_name="custom.product", string="Product",
domain="[('branch_line.branch_id.user_lines.user_id','=', user_id)]")
sell_price = fields.Float(string='Main Price', related='product_id.sell_price', required=True, )
other_price = fields.Float(string='Other Price', compute='_compute_price', store=True)
store=True, )
#api.depends('sell_price')
def _compute_price(self):
for rec in self:
rec.other_price = rec.sell_price

Try
sell_price = fields.Float(string='Main Price', inverse='_compute_dummy', compute='_compute_price', required=True, store=True)
#api.depends('product_id.sell_price')
def _compute_price(self):
for rec in self:
rec.sell_price = rec.product_id.sell_price
def _compute_dummy(self):
pass

Related

Get all information on 1 QueryDjango

I am trying to get all values in a single queryset, i have the following model:
class Temporal(model.Models):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
value=models.CharField(max_length=60)
created_at = models.DateTimeField(auto_now_add=True)
date_start = models.DateTimeField(auto_now_add=True)
date_end = models.DateTimeField(auto_now_add=True)
rate_name = models.ForeignKeyField("RateName")
concept_payment = models.CharField(max_length=60)
order = models.IntegerField(null=True, blank=True)
and some other fields...
I am getting all the differents concept_payment this way:
energy_concepts = Temporal.objects.filter(rate_name=rate_name,
date_start__month__lte=month_apply,
date_end__month__gte=month_apply,
concept_payment='Energy').first()
demand_concepts = Temporal.objects.filter(rate_name=rate_name,
date_start__month__lte=month_apply,
date_end__month__gte=month_apply,
concept_payment='Demand').first()
other_concepts = Temporal.objects.filter(rate_name=rate_name,
date_start__month__lte=month_apply,
date_end__month__gte=month_apply,
concept_payment='Others').first()
taxes_concepts = Temporal.objects.filter(rate_name=rate_name,
date_start__month__lte=month_apply,
date_end__month__gte=month_apply,
concept_payment='Taxes').first()
and so on... where the only difference is the concept_payment, So I was wonder if there is a way to get them all, using annotate instead of getting one by one, by the way the concept_payment sis dynamic so I think I would have to get all the difference concepts first.
Thanks in advance.
Any direction or suggestions would be very helpful, my main concern is the fact every filter hits the database, which I believe is not very efficient.
You can use django's in_bulk to give you a dictionary with the concept_payment as the key, and a Temporal instance as the value.
This would be tricky since there can be more than one instance per concept_type, but since you are using postgres and you said it doesn't matter which instance is returned, then you can use distinct with concept_payment to remove duplicates.
So try with:
Temporal.objects.filter(
rate_name=rate_name,
date_start__month__lte=month_apply,
date_end__month__gte=month_apply,
concept_payment__in=['Taxes', 'Others', 'Demand', 'Energy'],
).distinct('concept_payment').in_bulk(field_name='concept_payment')
This should return something like:
{
'Taxes': '<Temporal object with Taxes concept_type>',
'Demand': '<Temporal object with Demand concept_type>',
'Energy': '<Temporal object with Energy concept_type>',
'Others': '<Temporal object with Others concept_type>',
}

Django - Can I add a calculated field that only exists for a particular sub-set or occurences of my model?

Imagine that you have a model with some date-time fields that can be categorized depending on the date. You make an annotation for the model with different cases that assign a different 'status' depending on the calculation for the date-time fields:
#Models.py
class Status(models.TextChoices):
status_1 = 'status_1'
status_2 = 'status_2'
status_3 = 'status_3'
special_status = 'special_status'
class MyModel(models.Model):
important_date_1 = models.DateField(null=True)
important_date_2 = models.DateField(null=True)
calculated_status = models.CharField(max_length=32, choices=Status.choices, default=None, null=True, blank=False,)
objects = MyModelCustomManager()
And the manager with which to do the calculation as annotations:
# managers.py
class MyModelCustomManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset().annotate(**{
'status': Case(
When(**{'important_date_1' is foo, 'then':
Value(Status.status_1)}),
When(**{'important_date_2' is fii, 'then':
Value(Status.status_2)}),
When(**{'important_date_1' is foo AND 'importante_date_2' is whatever, 'then':
Value(Status.status_3)}),
# And so on and so on
)
}
)
return queryset
Now, here's where it gets tricky. Only one of these sub-sets of occurrences on the model requires an ADDITIONAL CALCULATED FIELD that literally only exists for it, that looks something like this:
special_calculated_field = F('important_date_1') - F('importante_date_2') #Only for special_status
So, basically I want to make a calculated field with the condition that the model instance must belong to this specific status. I don't want to make it an annotation, because other instances of the model would always have this value set to Null or empty if it were a field or annotation and I feel like it would be a waste of a row in the database.
Is there way, for example to do this kind of query:
>>> my_model_instance = MyModel.objects.filter(status='special_status')
>>> my_model_instance.special_calculated_field
Thanks a lot in advance if anyone can chime in with some help.

irregularity with One2Many field Odoo

I have the following problem I am creating a form that is a budget replica, but this type of budget does not carry VAT% and good will not pass through accounting.
Well the problem is the following I have created a model called budget.table
It is the following :
class TableElements(models.Model):
_name = 'budget.table'
product_id = fields.Many2one('product.product', string='Product',ondelete='restrict', index=True)
name = fields.Text(string='Description', required=True)
quantity = fields.Float(string='Quantity',required=True, default=1)
price_unit = fields.Float(string='Unit Price', required=True,)
price_subtotal = fields.Float(string='Amount',store=True, readonly=True)
and I have another model called budget.two which is the following:
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','id' ,string = 'Pedidos' )
total = fields.Float(string = 'Total:' ,compute="_total")
Well I want: as you can see in 'budget.two' there is a One2Many field which I will add all the new products that in turn will be saved in this type of new budget that I am created as I already commented without VAT and it will not happen by the accounting module.
When I select the products that I am going to save the One2manny, I keep it blank. Example:
So it should be kept:
but when you save it, look how it is stored without any element in the One2MAny field:
[![enter code here][2]][2]
In 'budget.table' add this field:
budget_two_id = fields.Many2one('budget.two')
In 'budget.two' correct this field:
order_line = fields.One2many('budget.table', 'budget_two_id', string='Pedidos')
The point is any One2many field should have an inverse field (Many2one) on the other model as a foreign key.

Get field value of inherited model Odoo 8

Hello to all I have been developing module under Odoo 8. I have a class "hrEmployee" with "_inherit=hr.employee" , now in my hrEmployee there is a One2many field having relation with another model "hr.employee.visa". I want to get the field values of the "hrEmployee" with onchange function defined on the field of "hr.employee.visa". Like when I change field value of "hrEmployee", I can get the field value entered on the current form (hrEmployee). How am I able to achieve this in Odoo v8? My Python code is shown below:
class hrEmployee(models.Model):
_inherit = "hr.employee"
diwan_no = fields.Char('Diwan No', size=30, help='Diwan Number')
zeo_number = fields.Char('ZEO Number',size=30, help='ZEO Number')
visas_ids = fields.One2many('hr.employee.visas', 'employee_id', 'Visas')
class hr_employee_visas(models.Model):
_name='hr.employee.visas'
employee_id = fields.Many2one("hr.employee.visas", "Employee" )
#api.onchange('visas_number')
#api.depends( 'visas_number')
def _visa_num(self):
cr=self._cr
uid=self._uid
ids=self._ids
for id in ids:
obj1=self.pool.get('hr.employee').browse(cr,uid,id,context=None)
print obj1.name_related
visas_sponsor = fields.Char('Sponsor')
visas_states = fields.Selection([('apply','Apply'),('active','Active'),('expire','Expire'),('cancel','Cancelled')], string='State' )
visas_number = fields.Char('Visa No', help='Visa Number')
I tried to use self.pool.get browse but it gives me "False" . Plz guide me or point me my mistake. Hopes for suggestion
Try following,
class hr_employee_visas(models.Model):
_name='hr.employee.visas'
employee_id = fields.Many2one("hr.employee", "Employee" )
#api.onchange('visas_number')
#api.depends( 'visas_number')
def _visa_num(self):
for obj in self:
print obj.employee_id.name
Here is the mistake
employee_id = fields.Many2one("hr.employee.visas", "Employee" )
You need to set hr.employee here.
No need to write both of the decorators together, in case of any changes into the visas_number field this method will be called, you can use any of the single decorator for this.

How do I make an auto increment integer field in Django?

I am making an Order model for a shopping cart and I need to make a field that auto increments when the order is made:
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField()
enable = models.BooleanField(default=True)
How do I make the IntegerField auto increment?
In Django
1 : Django model class has default field with name id which is auto increment Field.
2 : You can define your own auto increment field using AutoField
field.
class Order(models.Model):
auto_increment_id = models.AutoField(primary_key=True)
# primary_key = True if you do not want to use default field "id" given by django to your model
db design
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
`auto_increment_id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`auto_increment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
If you want to use django's default id as increment field .
class Order(models.Model):
add_date = models.DateTimeField(auto_now_add=True)
db design
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`add_date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
In django with every model you will get the by default id field that is auto increament. But still if you manually want to use auto increment. You just need to specify in your Model AutoField.
class Author(models.Model):
author_id = models.AutoField(primary_key=True)
you can read more about the auto field in django in Django Documentation for AutoField
class Belly(models.Model):
belly_id = models.AutoField(primary_key=True)
belly_name = models.CharField(max_length=50)
******** or *******
class Belly(models.Model):
belly_name = models.CharField(max_length=50)
The difference is:
The first table has the primary key belly_id (specified as AutoField) and second table has the primary key id (implicitly).
I think no need to use this directly; a primary key field will automatically be added to your model if you don’t specify. Otherwise
Check the Django Documentation for AutoField for further details related to AutoField.
You can create an autofield. Here is the documentation for the same
Please remember Django won't allow to have more than one AutoField in a model, In your model you already have one for your primary key (which is default). So you'll have to override model's save method and will probably fetch the last inserted record from the table and accordingly increment the counter and add the new record.
Please make that code thread safe because in case of multiple requests you might end up trying to insert same value for different new records.
Edited: Fixed mistake in code that stopped it working if there were no YourModel entries in the db.
There's a lot of mention of how you should use an AutoField, and of course, where possible you should use that.
However there are legitimate reasons for implementing auto-incrementing fields yourself (such as if you need an id to start from 500 or increment by tens for whatever reason).
In your models.py
from django.db import models
def from_500():
'''
Returns the next default value for the `ones` field,
starts from 500
'''
# Retrieve a list of `YourModel` instances, sort them by
# the `ones` field and get the largest entry
largest = YourModel.objects.all().order_by('ones').last()
if not largest:
# largest is `None` if `YourModel` has no instances
# in which case we return the start value of 500
return 500
# If an instance of `YourModel` is returned, we get it's
# `ones` attribute and increment it by 1
return largest.ones + 1
def add_ten():
''' Returns the next default value for the `tens` field'''
# Retrieve a list of `YourModel` instances, sort them by
# the `tens` field and get the largest entry
largest = YourModel.objects.all().order_by('tens').last()
if not largest:
# largest is `None` if `YourModel` has no instances
# in which case we return the start value of 10
return 10
# If an instance of `YourModel` is returned, we get it's
# `tens` attribute and increment it by 10
return largest.tens + 10
class YourModel(model.Model):
ones = models.IntegerField(primary_key=True,
default=from_500)
tens = models.IntegerField(default=add_ten)
You can override Django save method official doc about it.
The modified version of your code:
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField(default=0) # changed here
enable = models.BooleanField(default=True)
def save(self, *args, **kwargs):
self.order_number = self.order_number + 1
super().save(*args, **kwargs) # Call the "real" save() method.
Another way is to use signals. More one:
official Django docs about pre-save
stackoverflow example about using pre-save signal
What I needed: A document number with a fixed number of integers that would also act like an AutoField.
My searches took me all over incl. this page.
Finally I did something like this:
I created a table with a DocuNumber field as an IntegerField with foll. attributes:
max_length=6
primary_key=True
unique=True
default=100000
The max_length value anything as required (and thus the corresponding default= value).
A warning is issued while creating the said model, which I could ignore.
Afterwards, created a document (dummy) whence as expected, the document had an integer field value of 100000.
Afterwards changed the model key field as:
Changed the field type as: AutoField
Got rid of the max_length And defaultattributes
Retained the primary_key = True attribute
The next (desired document) created had the value as 100001 with subsequent numbers getting incremented by 1.
So far so good.
You can use default primary key (id) which auto increaments.
Note: When you use first design i.e. use default field (id) as a primary key, initialize object by mentioning column names.
e.g.
class User(models.Model):
user_name = models.CharField(max_length = 100)
then initialize,
user = User(user_name="XYZ")
if you initialize in following way,
user = User("XYZ")
then python will try to set id = "XYZ" which will give you error on data type.
If you are not going to use the auto increment field as the primary key, you can define an integer field and update this integer field in the save() method.
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField()
enable = models.BooleanField(default=True)
def save(self, *args, **kwargs):
orders = Order.objects.all()
if orders.exists() and self._state.adding:
last_order = orders.latest('order')
self.order = int(last_order.order) + 1
super().save(*args, **kwargs)
If we do not use self._state.adding here, the order will increase automatically in the update process as well. We only query the self._state.adding that we want to increase the order in the create process.

Categories

Resources