123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- from collections import defaultdict
- from odoo import api, fields, models, _
- from odoo.osv import expression
- class AccountMove(models.Model):
- _inherit = "account.move"
- timesheet_ids = fields.One2many('account.analytic.line', 'timesheet_invoice_id', string='Timesheets', readonly=True, copy=False)
- timesheet_count = fields.Integer("Number of timesheets", compute='_compute_timesheet_count')
- timesheet_encode_uom_id = fields.Many2one('uom.uom', related='company_id.timesheet_encode_uom_id')
- timesheet_total_duration = fields.Integer("Timesheet Total Duration", compute='_compute_timesheet_total_duration', help="Total recorded duration, expressed in the encoding UoM, and rounded to the unit")
- @api.depends('timesheet_ids', 'company_id.timesheet_encode_uom_id')
- def _compute_timesheet_total_duration(self):
- if not self.user_has_groups('hr_timesheet.group_hr_timesheet_user'):
- self.timesheet_total_duration = 0
- return
- group_data = self.env['account.analytic.line']._read_group([
- ('timesheet_invoice_id', 'in', self.ids)
- ], ['timesheet_invoice_id', 'unit_amount'], ['timesheet_invoice_id'])
- timesheet_unit_amount_dict = defaultdict(float)
- timesheet_unit_amount_dict.update({data['timesheet_invoice_id'][0]: data['unit_amount'] for data in group_data})
- for invoice in self:
- total_time = invoice.company_id.project_time_mode_id._compute_quantity(timesheet_unit_amount_dict[invoice.id], invoice.timesheet_encode_uom_id)
- invoice.timesheet_total_duration = round(total_time)
- @api.depends('timesheet_ids')
- def _compute_timesheet_count(self):
- timesheet_data = self.env['account.analytic.line']._read_group([('timesheet_invoice_id', 'in', self.ids)], ['timesheet_invoice_id'], ['timesheet_invoice_id'])
- mapped_data = dict([(t['timesheet_invoice_id'][0], t['timesheet_invoice_id_count']) for t in timesheet_data])
- for invoice in self:
- invoice.timesheet_count = mapped_data.get(invoice.id, 0)
- def action_view_timesheet(self):
- self.ensure_one()
- return {
- 'type': 'ir.actions.act_window',
- 'name': _('Timesheets'),
- 'domain': [('project_id', '!=', False)],
- 'res_model': 'account.analytic.line',
- 'view_id': False,
- 'view_mode': 'tree,form',
- 'help': _("""
- <p class="o_view_nocontent_smiling_face">
- Record timesheets
- </p><p>
- You can register and track your workings hours by project every
- day. Every time spent on a project will become a cost and can be re-invoiced to
- customers if required.
- </p>
- """),
- 'limit': 80,
- 'context': {
- 'default_project_id': self.id,
- 'search_default_project_id': [self.id]
- }
- }
- def _link_timesheets_to_invoice(self, start_date=None, end_date=None):
- """ Search timesheets from given period and link this timesheets to the invoice
- When we create an invoice from a sale order, we need to
- link the timesheets in this sale order to the invoice.
- Then, we can know which timesheets are invoiced in the sale order.
- :param start_date: the start date of the period
- :param end_date: the end date of the period
- """
- for line in self.filtered(lambda i: i.move_type == 'out_invoice' and i.state == 'draft').invoice_line_ids:
- sale_line_delivery = line.sale_line_ids.filtered(lambda sol: sol.product_id.invoice_policy == 'delivery' and sol.product_id.service_type == 'timesheet')
- if sale_line_delivery:
- domain = line._timesheet_domain_get_invoiced_lines(sale_line_delivery)
- if start_date:
- domain = expression.AND([domain, [('date', '>=', start_date)]])
- if end_date:
- domain = expression.AND([domain, [('date', '<=', end_date)]])
- timesheets = self.env['account.analytic.line'].sudo().search(domain)
- timesheets.write({'timesheet_invoice_id': line.move_id.id})
- class AccountMoveLine(models.Model):
- _inherit = 'account.move.line'
- @api.model
- def _timesheet_domain_get_invoiced_lines(self, sale_line_delivery):
- """ Get the domain for the timesheet to link to the created invoice
- :param sale_line_delivery: recordset of sale.order.line to invoice
- :return a normalized domain
- """
- return [
- ('so_line', 'in', sale_line_delivery.ids),
- ('project_id', '!=', False),
- '|', '|',
- ('timesheet_invoice_id', '=', False),
- ('timesheet_invoice_id.state', '=', 'cancel'),
- ('timesheet_invoice_id.payment_state', '=', 'reversed')
- ]
- def unlink(self):
- move_line_read_group = self.env['account.move.line'].search_read([
- ('move_id.move_type', '=', 'out_invoice'),
- ('move_id.state', '=', 'draft'),
- ('sale_line_ids.product_id.invoice_policy', '=', 'delivery'),
- ('sale_line_ids.product_id.service_type', '=', 'timesheet'),
- ('id', 'in', self.ids)],
- ['move_id', 'sale_line_ids'])
- sale_line_ids_per_move = defaultdict(lambda: self.env['sale.order.line'])
- for move_line in move_line_read_group:
- sale_line_ids_per_move[move_line['move_id'][0]] += self.env['sale.order.line'].browse(move_line['sale_line_ids'])
- timesheet_read_group = self.sudo().env['account.analytic.line']._read_group([
- ('timesheet_invoice_id.move_type', '=', 'out_invoice'),
- ('timesheet_invoice_id.state', '=', 'draft'),
- ('timesheet_invoice_id', 'in', self.move_id.ids)],
- ['timesheet_invoice_id', 'so_line', 'ids:array_agg(id)'],
- ['timesheet_invoice_id', 'so_line'],
- lazy=False)
- timesheet_ids = []
- for timesheet in timesheet_read_group:
- move_id = timesheet['timesheet_invoice_id'][0]
- if timesheet['so_line'] and timesheet['so_line'][0] in sale_line_ids_per_move[move_id].ids:
- timesheet_ids += timesheet['ids']
- self.sudo().env['account.analytic.line'].browse(timesheet_ids).write({'timesheet_invoice_id': False})
- return super().unlink()
|