I have two models in my odoo project. Employee and equipment, as shown bellow.
Equipment/model.py
from openerp import models, fields, api
import datetime
class equipment(models.Model):
_name = 'equipment.equipment'
name = fields.Char(string='Name', )
date_of_purchase = fields.Date(string='Date Of Purchase', default=fields.Date.today(), )
write_off_days = fields.Integer(string="Days To Write-off", required=True, )
write_off_date = fields.Date(string="Write-off Date", compute="_get_write_off_date", )
price = fields.Float(string="Price '$'", required=True, )
description = fields.Char(string="Description", required=False, )
employee_id = fields.Char(string="Owner", compute="_get_owner", )
#api.one
#api.depends('employee_id')
def _get_owner(self):
//result = self.env.['res.equipment_table'].
//get id from relation database <-------------
#api.one
#api.depends('write_off_days', 'date_of_purchase')
def _get_write_off_date(self):
date = datetime.datetime.strptime(self.date_of_purchase, "%Y-%m-%d")
self.write_off_date = date + datetime.timedelta(days=self.write_off_days)
employee/model.py
from openerp import models, fields, api
class employee(models.Model):
_name = 'employee.employee'
name = fields.Char(string='First Name')
last_name = fields.Char(string='Last Name')
birth_date = fields.Date(string='Birth Date', default=fields.Date.today(), )
equipment_ids = fields.Many2many(string="Equipment", comodel_name="equipment.equipment", relation="equipment_table", )
total_equipment_price = fields.Float(string="Total Equipment Price '$'", compute="_get_total_equipment_price", )
#api.one
#api.depends('equipment_ids')
def _get_total_equipment_price(self):
total = 0
for equipment in self.equipment_ids:
total += equipment.price
self.total_equipment_price = total
I have many2many field, which holds all equipment that employee owns. I need to update the equipment owner every time the field is changed. The reason for this is.. when a user adds new equipment to the employee, there should be shown only unowned equipment. That is why I need to check and update the owner.
I already made a domain to check if equipment already has an owner, which will be shown below. Just need to update that employee_id field somehow.
<notebook>
<page string="Equipment">
<group>
<field name="equipment_ids" domain="[('employee_id', '=', False)]"/>
</group>
</page>
</notebook>
Change the type of employee_id to Many2one:
employee_id = fields.Many2one('employee.employee', string="Owner")
Override create and write methods to set employee_id each time new equipment is added to the list:
class Employee(models.Model):
_name = 'employee.employee'
#api.model
def create(self, values):
res = super(Employee, self).create(values)
if res.equipment_ids:
res.equipment_ids.write({'employee_id': res.id})
return res
#api.multi
def write(self, values):
res = super(Employee, self).write(values)
for r in self:
equipment_ids = r.equipment_ids.filtered(lambda s: not s.employee_id)
if equipment_ids:
equipment_ids.write({'employee_id': r.id})
return res
Because an equipment can set to one owner, I suggest you to change the type of equipment_ids to One2many to be able to set a list of equipments by employee and the employee_id field value will be set automatically for you when you add equipment to the list.
If you change the value of employee_id the equipment will be automatically visible in the list of equipments of the new employee.
equipment_ids = fields.One2many("equipment.equipment", 'employee_id', string="Equipments")
Reset owner
#api.multi
def write(self, values):
removed_ids = set()
old_ids = {r.id: r.equipment_ids.ids for r in self}
res = super(Employee, self).write(values)
for r in self:
equipment_ids = r.equipment_ids.filtered(lambda s: not s.employee_id)
if equipment_ids:
equipment_ids.write({'employee_id': r.id})
removed_ids |= set(old_ids[r.id]) - set(r.equipment_ids.ids)
self.equipment_ids.browse(removed_ids).write({'employee_id': False})
return res
Related
I an having a confusion on how to write a django query to get my data. I have 2 tables 'ticket' and 'ticket_details'. Below is the schema for them.
Ticket(id, name, type, user)
TicketDetails(ticket_id, message, created_time)
Note: Multiple message can be associated to one ticket id.
And ticket_id is a foreign key to the Ticket table.
I would like to fetch all the columns from both the table where only the latest message from the TicketDetails table should be picked for a particular ticket id.
Example:
Ticket
id, name, type, user
1,install, application, usr1
TicketDetails
ticket_id, message, creted_time
1, <message1>, 12:00 PM
1, <message2>, 04:00 PM
2, <message3>, 05:00 PM -->latest entry
Expected Output:
id, name, type, user, message, created_time
1, install, application, usr1, <message3>, 05:00PM
Thanks in advance
I made some assumptions about your models, you didn't provide any:
class Ticket(models.Model):
name = models.CharField(max_length=50)
type = models.CharField(max_length=50)
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
# Model names should NEVER end with "s"
class TicketDetail(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
message = models.CharField(max_length=50)
created_time = models.DateTimeField(auto_now_add=True)
You have 2 options:
you can write it in pure sql, you lose the ability to filter
sql = """
SELECT ticket.id, ticket.name, ticket.type, ticket.user_id, detail.message
FROM {ticket} ticket
LEFT JOIN (
SELECT detail.ticket_id, detail.message
FROM {detail} detail
INNER JOIN (
SELECT MAX(id) id, ticket_id
FROM {detail}
GROUP BY ticket_id
) detail_message ON detail.id = detail_message.id
) detail ON detail.ticket_id = ticket.id
""".format(ticket=Ticket._meta.db_table, detail=TicketDetail._meta.db_table)
tickets = Ticket.objects.raw(sql)
for ticket in tickets:
print(ticket.id, ticket.message)
Write it in the "django" way
latest_messages = TicketDetail.objects.values('ticket_id').annotate(id=models.Max('id')).values('id')
tickets = Ticket.objects.prefetch_related(models.Prefetch('ticketdetail_set', TicketDetail.objects.filter(id__in=latest_messages))).order_by('id')
for ticket in tickets:
print(ticket.id)
# this iteration will only ever yield 1 result.. or nothing.
for detail in ticket.ticketdetail_set.all():
print(detail.message)
Here are the tests:
# uses factoryboy and faker to fill in the data
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth.models.User
django_get_or_create = ('username',)
first_name = fake.first_name()
last_name = fake.last_name()
email = factory.LazyAttribute(lambda obj: "{}.{}#gmail.com".format(obj.last_name, obj.first_name).lower())
username = factory.Sequence(lambda n: 'user' + str(n))
class SimpleTestCase(TestCase):
def setUp(self):
ticket1 = Ticket.objects.create(user=UserFactory(), type='A', name='Number 1')
TicketDetail.objects.create(ticket=ticket1, message='you wont see this')
TicketDetail.objects.create(ticket=ticket1, message='you wont see this either')
TicketDetail.objects.create(ticket=ticket1, message='YES!!')
ticket2 = Ticket.objects.create(user=UserFactory(), type='B', name='Number 2')
TicketDetail.objects.create(ticket=ticket2, message='you also wont see this')
TicketDetail.objects.create(ticket=ticket2, message='you also wont see this either')
TicketDetail.objects.create(ticket=ticket2, message='also YES!!')
def test_flatten_pure_sql(self):
sql = """
SELECT ticket.id, ticket.name, ticket.type, ticket.user_id, detail.message
FROM {ticket} ticket
LEFT JOIN (
SELECT detail.ticket_id, detail.message
FROM {detail} detail
INNER JOIN (
SELECT MAX(id) id, ticket_id
FROM {detail}
GROUP BY ticket_id
) detail_message ON detail.id = detail_message.id
) detail ON detail.ticket_id = ticket.id
""".format(ticket=Ticket._meta.db_table, detail=TicketDetail._meta.db_table)
self.assertEquals(['YES!!', 'also YES!!'], [x.message for x in Ticket.objects.raw(sql)])
def test_orm_way(self):
latest_messages = TicketDetail.objects.values('ticket_id').annotate(id=models.Max('id')).values('id')
tickets = Ticket.objects.prefetch_related(models.Prefetch('ticketdetail_set', TicketDetail.objects.filter(id__in=latest_messages))).order_by('id')
self.assertEquals(['Number 1', 'Number 2'], [x.name for x in tickets])
self.assertEquals(['YES!!'], [x.message for x in tickets[0].ticketdetail_set.all()])
self.assertEquals(['also YES!!'], [x.message for x in tickets[1].ticketdetail_set.all()])
I will want to retrieve different values from a chosen field. Let me explain:
I have this class:
class SchoolWebServices(models.Model):
_name = 'ecole.webservices'
name = fields.Char(string='Nom')
code_produit = fields.Char(string='Produit')
code_CDG = fields.Char(string='Centre de Gestion')
code_Catalog = fields.Char(string='Catalogue Produits')
I have this other class:
class ResPartner_school(models.Model):
_name = 'ecole.partner.school'
_order = 'id desc'
half_pension_name = fields.Many2one(comodel_name="ecole.webservices",
string="Lieu")
And I have a function who is in the class: ecole.partner.school
#api.multi
def create_compte_cantine(self):
print "Inscription réussie"
get_halfpension_name = self.half_pension_name.id
if get_halfpension_name:
code_Catalog = self.env['ecole.webservices'].code_Catalog
I get the id of half_pension_name in my variable get_halfpension_name but I wish to recover the code_Catalog of the same recording too. How to do?
You just need to use dot-notation to retrieve the value:
#api.multi
def create_compte_cantine(self):
self.ensure_one()
if self.half_pension_name:
code_Catalog = self.half_pension_name.code_Catalog
Try to stay in the "rules" of the Odoo guideline. For example a Many2one relation field should be end with _id -> half_pension_id = fields.Many2one(comodel_name="ecole.webservices", string="Lieu")
I want to create recordset with product in my custom class from sale order after calling an event. I will create a record in sale.order and like creating invoice, I will create record in my custom module.
What I have done is:
In my custom class:
class LoadingSlip(models.Model):
_name = 'loading.slip'
_description = 'loading information'
partner_id = fields.Char("Customer Name")
order_date = fields.Date("Order Date")
expiration_date = fields.Date("Expiration Date")
# order_line = fields.One2many('sale.order.line', 'order_id', string="Order Lines")
product_line = fields.One2many('loading.product.line', 'loading_product', string="Loading Products")
class LoadingProduct(models.Model):
_name = 'loading.product.line'
_description = "Loading Product Informations"
products_id = fields.Many2one('product.product', string='Product',
ondelete='restrict', index=True)
quantity = fields.Float(string='Quantity', default=1)
loading_product = fields.Many2one('loading.slip', string="Loading Reference", ondelete='cascade', index='True')
In sale.order
class sale_func(models.Model):
_inherit = 'sale.order'
#api.multi
def _prepare_slip(self):
test.set_trace()
self.ensure_one()
slip = {
'partner_id': self.partner_id.name,
'order_date': self.date_order,
'expiration_date': self.validity_date,
}
return slip
#api.multi
def action_slip_create(self, grouped=False, final=False):
test.set_trace() # debug point
pprint(self)
inv_obj = self.env['loading.slip']
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
slips={}
pprint(slips)
slipReferences={}
test.set_trace()
for order in self:
group_key = order.id
test.set_trace()
for line in order.order_line:
if group_key not in slips:
inv_data = order._prepare_slip()
loadingslip = inv_obj.create(inv_data)
slipReferences[loadingslip] = order
slips[group_key] = loadingslip
if line.product_uom_qty > 0:
line.slip_line_create(slips[group_key].id)
if not slips:
raise UserError(_('There is no loading slip line.'))
#api.multi
def create_slip(self):
test.set_trace()
pprint(self)
sale_orders = self.env['sale.order'].browse(self._context.get('active_ids', []))
self.action_slip_create(sale_orders)
In sale.order.line
class sales_order(models.Model):
_inherit="sale.order.line"
#api.multi
def _prepare_slip_line(self):
test.set_trace()
self.ensure_one()
res={}
pprint(res)
res={
'products_id': self.product_id.id or False,
'quantity': self.product_uom_qty
}
pprint(res)
#api.multi
def slip_line_create(self, loading_product):
test.set_trace()
prdct_order = self.env['loading.product.line']
for line in self:
vals = line._prepare_slip_line()
prdct_order.create(vals)
My error is:
> /home/diwap/odoo-dev/custom-addons/sales_ext_agni/models/models.py(196)slip_line_create()
195 vals = line._prepare_slip_line()
--> 196 prdct_order.create(vals)
197
ipdb> n
TypeError: "argument of type 'NoneType' is not iterable"
I have tried update and write method instead of create in line 196 but I could not get any result it's just come up with an empty field and also no error. However when I try the real thing and i.e. write() I get this error. Is there any wrong somewhere in my code or its just an horrible thing I am doing.
I tried to use api.constrain in odoo. The case is, I want to give condition to odoo if user missing one field, odoo will give some warning like alert, and user have to give some input to the previous field. This is my code :
class Provinc(models.Model):
_name = 'provinsi.model'
_rec_name ='prov'
prov = fields.Char(
string='Provinsi',
)
res_partner_ids = fields.One2many(
'res.partner',
'provinsi'
city_id = fields.One2many(
'city.model',
'provinsi_id'
)
class city(models.Model):
_name = 'kota.model'
_rec_name ='city'
city = fields.Char(
string='City',
)
res_partner_city = fields.One2many(
'res.partner',
'city'
)
provinsi_id = fields.Many2one(
'provinsi.model',
string='provinsi',
)
class Master_data(models.Model):
_inherit = 'res.partner'
provinsi = fields.Many2one(
'provinsi.model',
'provinsi',
)
city = fields.Many2one(
'city.model',
'city',
)
#api.onchange('provinsi')
def show_provinsi(self):
return {'domain': {'city': [('provinsi_id', '=', self.provinsi.id)]}}
#api.constrains('provinsi')
#api.onchange('city')
def show_kota(self):
for record in self:
if record.provinsi == False:
raise ValidationError("is gonna be error")
I have tried 2 ways. First, I put the api.constrain insode class City, and it's doesn't work,second ways I tried put the api.constrain like the code inside class master, and the result remains the same.
you only need onchang not any constrains to display warning!
#api.onchange('city')
def show_kota(self):
if self.city and not self.provinsi:
raise ValidationError("is gonna be error")
now if you select value in city field and provinsi missing value then it will display error msg
guessing field "city" and "provinsi" is in 'res.partner' form view!
If you want to check a field value is empty or not, just use required ="1" like:
<field name="provinsi" required="1"/>
Currently i am creating a custom module, in which i need to create a demo record while a customer invoice is created.
This is my custom model.
class csm(models.Model):
_name= 'csm'
invoice_date = fields.Datetime('Invoice date',readonly=True)
delivery_date = fields.Datetime('Delivery date',readonly=True)
In account.invoice i have added as,
class invoice_csm(models.Model):
_inherit = 'account.invoice'
delivery_date = fields.Datetime('Delivery date')
date_invoice= fields.Datetime('Invoice date')
#api.multi
def write(self,values):
record = super(invoice_csm, self).write(values)
if self.state == 'paid':
if self.delivery_date and self.installation_date:
self.env['csm'].write({
'invoice_date': record .date_invoice,
'delivery_date': record .delivery_date,
})
return record
What i want is when the status of customer invoice is become paid, i need to create a record in my csm model. But it is not creating. But if i use directly create function then record is creating in csm, but the condtion state=='paid' wont work because we can change the state even after creating invoice. That's why i used write method, but here it is not creating the record.
(Delivery and invoice dates have values)
############ CODE FOR REFERENCE #########
class customer(models.Model):
_name = "csm"
_inherit = ['mail.thread']
name = fields.Char(string='Subject', required=True)
## RETAIL ##
invoice_date = fields.Datetime('Invoice date',readonly=True)
delivery_date = fields.Datetime('Delivery date',readonly=True)
installation_date = fields.Datetime('Installation date',readonly=True)
service1_date = fields.Datetime('First service date',readonly=True)
service2_date = fields.Datetime('Second service date',readonly=True)
service3_date = fields.Datetime('Third service date',readonly=True)
#api.multi
def unlink(self):
if self.pool['res.users'].has_group(self._cr, self.env.user.id, 'customer_service_management.group_managerz'):
pass
elif self.create_uid == self.env.user:
pass
else:
if self.create_uid != self.env.user:
raise Warning('You are not authorized to delete this record. Contact administrator/manager')
return super(customer, self).unlink()
INVOICE
class invoice_csm(models.Model):
_inherit = 'account.invoice'
delivery_date = fields.Datetime('Delivery date')
installation_date = fields.Datetime('Installation date')
service1_date = fields.Datetime('First service date')
service2_date = fields.Datetime('Second service date')
service3_date = fields.Datetime('Third service date')
#api.multi
def write(self,values):
record = super(invoice_csm, self).write(values)
if 'state' in values and values['state'] == 'paid':
self.env['csm'].create({
'name': values['number'],
'delivery_date': values['delivery_date'],
'installation_date': values['installation_date'],
'service1_date': values['service1_date'],
'service2_date': values['service2_date'],
'service3_date': values['service3_date'],
})
return record
Use self.env['csm'].create , instead of self.env['csm'].write:
#api.multi
def write(self,values):
record = super(invoice_csm, self).write(values)
if 'state' in values and values['state'] == 'paid':
if values.get('date_invoice') and values.get('delivery_date'):
self.env['csm'].create({
'invoice_date': values['date_invoice'],
'delivery_date': values['delivery_date'],
})
return record