in ower society, employee's payslip is based on timesheet attendance. so in order to make that possible, I have been changing in openerp(7) code, exactly in payroll.py
the previews code is based on" contract.working_hours " it takes the total of hours and it puts it in pyaslip.number_of_hours
def get_worked_day_lines(self, cr, uid, contract_ids, date_from, date_to, context=None):
"""
#param contract_ids: list of contract id
#return: returns a list of dict containing the input that should be applied for the given contract between date_from and date_to
"""
def was_on_leave(employee_id, datetime_day, context=None):
res = False
day = datetime_day.strftime("%Y-%m-%d")
holiday_ids = self.pool.get('hr.holidays').search(cr, uid, [('state','=','validate'),('employee_id','=',employee_id),('type','=','remove'),('date_from','<=',day),('date_to','>=',day)])
if holiday_ids:
res = self.pool.get('hr.holidays').browse(cr, uid, holiday_ids, context=context)[0].holiday_status_id.name
return res
res = []
for contract in self.pool.get('hr.contract').browse(cr, uid, contract_ids, context=context):
if not contract.working_hours:
#fill only if the contract as a working schedule linked
continue
attendances = {
'name': _("Normal Working Days paid at 100%"),
'sequence': 1,
'code': 'WORK100',
'number_of_days': 0.0,
'number_of_hours': 0.0,
'contract_id': contract.id,
}
leaves = {}
day_from = datetime.strptime(date_from,"%Y-%m-%d")
day_to = datetime.strptime(date_to,"%Y-%m-%d")
nb_of_days = (day_to - day_from).days + 1
for day in range(0, nb_of_days):
working_hours_on_day = self.pool.get('resource.calendar').working_hours_on_day(cr, uid, contract.working_hours, day_from + timedelta(days=day), context)
if working_hours_on_day:
#the employee had to work
leave_type = was_on_leave(contract.employee_id.id, day_from + timedelta(days=day), context=context)
if leave_type:
#if he was on leave, fill the leaves dict
if leave_type in leaves:
leaves[leave_type]['number_of_days'] += 1.0
leaves[leave_type]['number_of_hours'] += working_hours_on_day
else:
leaves[leave_type] = {
'name': leave_type,
'sequence': 5,
'code': leave_type,
'number_of_days': 1.0,
'number_of_hours': working_hours_on_day,
'contract_id': contract.id,
}
else:
#add the input vals to tmp (increment if existing)
attendances['number_of_days'] += 1.0
attendances['number_of_hours'] += working_hours_on_day
leaves = [value for key,value in leaves.items()]
res += [attendances] + leaves
return res
I'v been doing researche about how to browse and search a model,and what I want to do is get total from timesheet where hr_timeshet.employee_id = payslip.employee_id
def get_worked_day_lines(self, cr, uid, employee_id, date_from, date_to, context=None):
res = []
for sheet in self.pool.get('hr_timesheet_sheet.sheet').search(cr,uid, [('state','=','confirm'),('employee_id','=',employee_id)]):
if not sheet:
continue
attendances = {
'name': _("Normal Working Days paid at 100%"),
'sequence': 1,
'code': 'WORK100',
'number_of_days': 0.0,
'number_of_hours': 0.0,
#'contract_id': contract.id,
}
leaves = {}
day_from = datetime.strptime(date_from,"%Y-%m-%d")
day_to = datetime.strptime(date_to,"%Y-%m-%d")
nb_of_days = (day_to - day_from).days + 1
for day in range(0, nb_of_days):
if sheet:
attendances['number_of_days'] += 1.0
attendances['code'] = sheet.total_attendance
I really need that, thanks in advance
error:
Invalid value hr.employee(1,) in domain term ('employee_id', '=', hr.employee(1,))
The error is because you are getting the hr.employee object when you actually need only the id.
Try to change:
('employee_id','=',employee_id)]
to
('employee_id','=',employee_id.id)]
sheet is an integer, you must browse first the object and then access to total_attendance.
Try:
if sheet:
sheet_obj=self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, sheet, context=context)
attendances['number_of_days'] += 1.0
attendances['code'] = sheet_obj.total_attendance
I hope this helps you.
Related
I'm working on database that uses lot of data. One invoice could have 7482 different articles. Validating invoice cost so much time, it took 26 minutes to validate one with 7482 articles. I find the method that take time to finish, it is the "action_move_create" inside "odoo\addons\account\models\account_invoice.py".
#api.multi
def action_move_create(self):
""" Creates invoice related analytics and financial move lines """
account_move = self.env['account.move']
for inv in self:
if not inv.journal_id.sequence_id:
raise UserError(_('Please define sequence on the journal related to this invoice.'))
if not inv.invoice_line_ids.filtered(lambda line: line.account_id):
raise UserError(_('Please add at least one invoice line.'))
if inv.move_id:
continue
if not inv.date_invoice:
inv.write({'date_invoice': fields.Date.context_today(self)})
if not inv.date_due:
inv.write({'date_due': inv.date_invoice})
company_currency = inv.company_id.currency_id
# create move lines (one per invoice line + eventual taxes and analytic lines)
iml = inv.invoice_line_move_line_get()
iml += inv.tax_line_move_line_get()
diff_currency = inv.currency_id != company_currency
# create one move line for the total and possibly adjust the other lines amount
total, total_currency, iml = inv.compute_invoice_totals(company_currency, iml)
name = inv.name or ''
if inv.payment_term_id:
totlines = inv.payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
res_amount_currency = total_currency
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency._convert(t[1], inv.currency_id, inv.company_id, inv._get_currency_rate_date() or fields.Date.today())
else:
amount_currency = False
# last line: add the diff
res_amount_currency -= amount_currency or 0
if i + 1 == len(totlines):
amount_currency += res_amount_currency
iml.append({
'type': 'dest',
'name': name,
'price': t[1],
'account_id': inv.account_id.id,
'date_maturity': t[0],
'amount_currency': diff_currency and amount_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
else:
iml.append({
'type': 'dest',
'name': name,
'price': total,
'account_id': inv.account_id.id,
'date_maturity': inv.date_due,
'amount_currency': diff_currency and total_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
line = inv.group_lines(iml, line)
line = inv.finalize_invoice_move_lines(line)
date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
'line_ids': line,
'journal_id': inv.journal_id.id,
'date': date,
'narration': inv.comment,
}
move = account_move.create(move_vals)
# Pass invoice in method post: used if you want to get the same
# account move reference when creating the same invoice after a cancelled one:
move.post(invoice = inv)
# make the invoice point to that move
vals = {
'move_id': move.id,
'date': date,
'move_name': move.name,
}
inv.write(vals)
return True
Could you suggest some solutions?
We suppose that the hardware is efficient to run odoo correctly.
I optimize it by using raw sql query. I made these codes in account.invoice model:
The first one is the definition of _mock_create_move_line (called in action_move_create).
def _mock_create_move_line(self, model, values, move):
bad_names = ["analytic_line_ids", "tax_ids", "analytic_tag_ids"]
other_fields = [
"currency_id", "debit", "credit", "balance",
"debit_cash_basis", "credit_cash_basis", "balance_cash_basis",
"company_currency_id", "amount_residual",
"amount_residual_currency", "tax_base_amount", "reconciled",
"company_id", "counterpart"
]
cr = self.env.cr
quote = '"{}"'.format
columns = []
columns1 = []
for i, v in enumerate(values):
v = model._add_missing_default_values(v)
account_id = self.env['account.account'].browse(v['account_id'])
# compulsory columns and some stored related columns
# related fields are not triggered, krrrrr
v.update({
'move_id': move.id,
'date_maturity': move.date,
'company_id': account_id.company_id.id,
'date': move.date,
'journal_id': move.journal_id.id,
'user_type_id': account_id.user_type_id.id,
'create_uid': self.env.uid,
'create_date': fields.Datetime.now()
})
######
temp_column = []
for name, val in sorted(v.items()):
if name in bad_names:
continue
field = model._fields[name]
if field.column_type:
col_val = field.convert_to_column(val, model, v)
temp_column.append(col_val)
if not i:
columns1.append((name, field.column_format, col_val))
columns.append(tuple(temp_column))
model.check_access_rule('create')
try:
query = "INSERT INTO {} ({}) VALUES {} RETURNING id".format(
quote(model._table),
", ".join(quote(name) for name, fmt, val in columns1),
", ".join('%s' for fmt in columns),
)
cr.execute(query, columns)
ids = cr.fetchall()
# clear the model cache to take account of the new insertion
# if not executed, relationnal field will not be updated
model.invalidate_cache()
account_move_line_ids = model.browse(ids)
account_move_line_ids.modified(other_fields)
account_move_line_ids.recompute()
# update parent_path
account_move_line_ids._parent_store_create()
except Exception as e:
_logger.info(e)
cr.rollback()
return
The second one is the overriding of native method action_move_create. I make some modification, call _mock_create_move_line if there is 'raw_sql' in the context.
#api.multi
def action_move_create(self):
""" Creates invoice related analytics and financial move lines """
# TODO : make choice between ORM or raw sql according to the context
account_move = self.env['account.move']
for inv in self:
if not inv.journal_id.sequence_id:
raise UserError(_('Please define sequence on the journal related to this invoice.'))
if not inv.invoice_line_ids.filtered(lambda line: line.account_id):
raise UserError(_('Please add at least one invoice line.'))
if inv.move_id:
continue
if not inv.date_invoice:
inv.write({'date_invoice': fields.Date.context_today(self)})
if not inv.date_due:
inv.write({'date_due': inv.date_invoice})
company_currency = inv.company_id.currency_id
# create move lines (one per invoice line + eventual taxes and analytic lines)
iml = inv.invoice_line_move_line_get()
iml += inv.tax_line_move_line_get()
diff_currency = inv.currency_id != company_currency
# create one move line for the total and possibly adjust the other lines amount
total, total_currency, iml = inv.compute_invoice_totals(company_currency, iml)
name = inv.name or ''
if inv.payment_term_id:
totlines = \
inv.payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
res_amount_currency = total_currency
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency._convert(t[1], inv.currency_id, inv.company_id,
inv._get_currency_rate_date() or fields.Date.today())
else:
amount_currency = False
# last line: add the diff
res_amount_currency -= amount_currency or 0
if i + 1 == len(totlines):
amount_currency += res_amount_currency
iml.append({
'type': 'dest',
'name': name,
'price': t[1],
'account_id': inv.account_id.id,
'date_maturity': t[0],
'amount_currency': diff_currency and amount_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
else:
iml.append({
'type': 'dest',
'name': name,
'price': total,
'account_id': inv.account_id.id,
'date_maturity': inv.date_due,
'amount_currency': diff_currency and total_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
line = inv.group_lines(iml, line)
line = inv.finalize_invoice_move_lines(line)
date = inv.date or inv.date_invoice
if self.env.context.get('raw_sql', None):
move_vals = {
'ref': inv.reference,
'journal_id': inv.journal_id.id,
'date': date,
'narration': inv.comment,
}
# remove (0, 0, ...)
# override the group_lines method to avoid looping on next instruction
new_lines = [nl[2] for nl in line]
# TODO do not call compute here, add with ...norecompute()
move = account_move.create(move_vals)
move.env.cr.commit()
self._mock_create_move_line(self.env['account.move.line'], new_lines, move)
# Pass invoice in method post: used if you want to get the same
# account move reference when creating the same invoice after a cancelled one:
# compute move, it is not triggered automatically bc raw sql insertion
# is it correct to call it like this ? find better way
move._amount_compute()
move._compute_partner_id()
move._compute_matched_percentage()
else:
# make default behavior
move_vals = {
'ref': inv.reference,
'line_ids': line,
'journal_id': inv.journal_id.id,
'date': date,
'narration': inv.comment,
}
move = account_move.create(move_vals)
move.post(invoice=inv)
# make the invoice point to that move
vals = {
'move_id': move.id,
'date': date,
'move_name': move.name,
}
inv.write(vals)
return True
Now, the execution time is less than 1 minutes for about 7000 records to insert inside invoice.move.line
This method to get the product price from the PO, and it works well if the PO have only one record otherwise I am getting this error.
raise ValueError("Expected singleton: %s" % self)
This is the method
#api.multi
def create_refund_invoice(self):
inv_obj = self.env['account.invoice']
for pick in self.filtered(lambda x:x.return_type):
type = 'in_refund' if pick.return_type == 'purchase' else 'out_refund'
inv_lines = {'type':type, 'partner_id':pick.partner_id.id, 'invoice_line_ids':[]}
account = pick.return_type == 'sale' and pick.partner_id.property_account_receivable_id.id or pick.partner_id.property_account_payable_id.id
inv_lines['account_id'] = account
inv_lines['origin'] = pick.name
inv_lines['name'] = pick.origin
for line in pick.move_lines:
name = line.product_id.partner_ref
for rec in self:
rec.order_id = line.env['purchase.order'].search([('name', '=', line.origin)]).order_line
rec.price = rec.order_id.price_unit
inv_lines['invoice_line_ids'] += [(0, None, {
'product_id':line.product_id.id,
'name':name,
'quantity':line.quantity_done,
'price_unit': rec.price,
'account_id':line.product_id.product_tmpl_id.get_product_accounts()['income'].id})]
if inv_lines['invoice_line_ids']:
inv_id = inv_obj.create(inv_lines)
pick.invoice_id = inv_id.id
It is necessary for odoo that when you are getting more than one record then you can not access it's field values directly.
In your code you are trying to get purchase_order_line of purchase_order It may possible that many lines are available in a single order.
def create_refund_invoice(self):
purchase_order_obj = self.env['purchase.order']
inv_obj = self.env['account.invoice']
for pick in self.filtered(lambda x:x.return_type):
type = 'in_refund' if pick.return_type == 'purchase' else 'out_refund'
inv_lines = {'type':type, 'partner_id':pick.partner_id.id, 'invoice_line_ids':[]}
account = pick.return_type == 'sale' and pick.partner_id.property_account_receivable_id.id or pick.partner_id.property_account_payable_id.id
inv_lines['account_id'] = account
inv_lines['origin'] = pick.name
inv_lines['name'] = pick.origin
for line in pick.move_lines:
name = line.product_id.partner_ref
for rec in self:
order_lines = purchase_order_obj.search([('name', '=', line.origin)]).order_line
for pol in order_lines:
price = pol.order_id.price_unit
inv_lines['invoice_line_ids'] += [(0, None, {
'product_id':line.product_id.id,
'name':name,
'quantity':line.quantity_done,
'price_unit': price,
'account_id':line.product_id.product_tmpl_id.get_product_accounts()['income'].id})
]
if inv_lines['invoice_line_ids']:
inv_id = inv_obj.create(inv_lines)
pick.invoice_id = inv_id.id
I have updated code test above code and update it as per your requirement.
Why am I not getting the same calculation result in invoice line as in sale order line?
When I'm confirming the sale order, it gives me a correct calculation result in invoice line, but as I separately create it from invoice line, it gives me a different calculation result. Below is my code.
What else can I do in account_invoice_line.py file?
class account_invoice_line(models.Model):
_inherit = 'account.invoice.line'
def _make_invoice(self, cr, uid, order, lines, context=None):
inv_id = super(sale_order_line, self)._make_invoice(cr, uid, order, lines, context)
inv_obj = self.pool.get('account.invoice.line')
if order.name_of_your_field:
inv_obj.write(cr, uid, [inv_id], {'order_line': order_line.id}, context=context)
return inv_id
# _columns = {
qty_char = fields.Float('Qty in M. Sqr')
def on_change_qty_squ(self, cr, uid, ids, qty_char, product , price_unit=0 , quantity=0 ,price_after_discount=0 , context=None):
qty_char = qty_char
product_obj = self.pool.get('product.product')
product_obj = product_obj.browse(cr, uid, product, context=context)
prod_type = product_obj.supp_ref
# prod_price = product_obj.lst_price
squ_meter = product_obj.squ_meter
price = price_unit
value = {}
if prod_type == 'T':
if qty_char:
typ = float(qty_char)
qty = typ / squ_meter
round = math.ceil(qty)
new_qty = round * squ_meter
value['quantity'] = round
subtotal = (quantity) * (price_after_discount)
value['price_subtotal'] = subtotal
else:
if qty_char:
typ = float(qty_char)
qty = typ / squ_meter
round = math.ceil(qty)
new_qty = round * squ_meter
value['quantity'] = round
subtotal = (quantity) * (price_after_discount)
value['price_subtotal'] = subtotal
return {'value': value}
#api.depends('price_after', 'price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
'qty_char','product_id', 'invoice_id.partner_id', 'invoice_id.discount', 'invoice_id.currency_id', 'invoice_id.company_id')
def _compute_price_after(self):
self.price_after = self.price_unit * (1 - (self.invoice_id.discount or 0.0) / 100.0)
price_after = fields.Float(string='Price After Discount', digits= dp.get_precision('Account'),
store=True, readonly=True, compute='_compute_price_after')
When the onchange...() function is executed I am getting the error:
File "/opt/odoo/odoo/openerp/models.py", line 5652, in _onchange_eval
result.setdefault('domain', {}).update(method_res['domain'])
ValueError: dictionary update sequence element #0 has length 3; 2 is required
Here is my code. I think error has something to do with domain:
def onchange_template_id(self, cr, uid, ids, id, context=None):
print "\n\n on change template global_template_id ", global_template_id
bom_ids = []
pd_ids = []
product_complete = []
ptemplid = global_template_id
mbl_obj = self.pool.get('mrp.bom.line')
id_s = mbl_obj.search(cr, uid, [('product_id', '=', ptemplid)])
for rec in mbl_obj.browse(cr, uid, id_s, context=context):
bom_ids.append(rec.bom_id.id)
mb_obj = self.pool.get('mrp.bom')
for rec in mb_obj.browse(cr, uid, bom_ids, context=context):
pd_ids.append(rec.product_id.id)
pp_obj = self.pool.get('product.product')
for rec in pp_obj.browse(cr, uid, pd_ids, context=context):
product_complete.append('['+ str(rec.default_code) + ']'+ ' ' + str(rec.name_template))
print "\n\n bom_ids ", bom_ids
domain = [('id','=',bom_ids)]
return {
'type': 'ir.actions.act_window',
'name': _('BOM'),
'res_model': 'mrp.bom',
'view_mode': 'tree',
'target': 'new',
'domain': domain,
}
bom_ids = []
It is a list, means more then one value will be there.And the domain that you are passing is
[('id','=',bom_ids)]
so if we take small example then consider that bom_ids = [1,2,3]; then the domain will be
domain = [('id','=',bom_ids)] -> [('id','=',[1,2,3])]
Which is wrong as per SQL concept. the id = will always have single value. for multiple values you should use either in or like.
Here the solution you can try is
domain = [('id','in',bom_ids)]
Hope this will help you.
Thanks.
I am trying to calculate the due date on changing invoice date but its not working
def onchange_date_invoice(self,cr,uid,ids,type,partner_id,date_invoice=False,payment_term=False):
partner_payment_term = False
raise osv.except_osv(_('OnChangeDateInvoice'), _('Start'))
if date_invoice != time.strftime('%Y-%m-%d'):
if not payment_term_id:
return {'value':{'date_due': date_invoice}}
pterm_list = self.pool.get('account.payment.term').compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
if payment_term != partner_payment_term:
if partner_payment_term:
to_update = self.onchange_payment_term_date_invoice(
cr, uid, ids, partner_payment_term, date_invoice)
result['value'].update(to_update['value'])
if pterm_list:
pterm_list = [line[0] for line in pterm_list]
pterm_list.sort()
result = {'value':{'date_due': pterm_list[-1]}}
else:
result['value']['date_due'] = False
raise osv.except_osv(_('OnChangeDateInvoice'), _('End'))
return result
You'r code should look's like:
def onchange_invoice_date(self, cr, uid, ids, payment_term_id, date_invoice):
res = {}
import time
if date_invoice != time.strftime('%Y-%m-%d'):
due_date = # calculate you'r date here
res['due_date'] = due_date
return {'value': res}