[IMP] hr_timesheet: Small cleaning and linting
This commit is contained in:
@@ -65,10 +65,6 @@
|
||||
<field name="model_id" ref="analytic.model_account_analytic_line"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('account.group_account_invoice'))]"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- For a perfect onboarding, we want the tour of project to be consumed
|
||||
only by admin when installing hr_timesheet, to avoid confusing -->
|
||||
<record id="web_tour_project_consumed_by_admin" model="web_tour.tour">
|
||||
<field name="name">project_tour</field>
|
||||
<field name="user_id" ref='base.user_admin'/>
|
||||
</record>
|
||||
<!-- Set the JS widget -->
|
||||
<record id="uom.product_uom_day" model="uom.uom">
|
||||
<field name="timesheet_widget">float_toggle</field>
|
||||
</record>
|
||||
|
||||
<!-- Set the JS widget -->
|
||||
<record id="uom.product_uom_day" model="uom.uom">
|
||||
<field name="timesheet_widget">float_toggle</field>
|
||||
</record>
|
||||
<record id="uom.product_uom_hour" model="uom.uom">
|
||||
<field name="timesheet_widget">float_time</field>
|
||||
</record>
|
||||
|
||||
<record id="uom.product_uom_hour" model="uom.uom">
|
||||
<field name="timesheet_widget">float_time</field>
|
||||
</record>
|
||||
<!-- Force Analytic account creation for projects allowing timesheet (default is True) -->
|
||||
<function
|
||||
model="project.project"
|
||||
name="_init_data_analytic_account"
|
||||
eval="[]"/>
|
||||
|
||||
<!-- Force Analytic account creation for projects allowing timesheet (default is True) -->
|
||||
<function
|
||||
model="project.project"
|
||||
name="_init_data_analytic_account"
|
||||
eval="[]"/>
|
||||
|
||||
<function model="ir.config_parameter" name="set_param" eval="('hr_timesheet.timesheet_rounding', '15')"/>
|
||||
<function model="ir.config_parameter" name="set_param" eval="('hr_timesheet.timesheet_min_duration', '15')"/>
|
||||
</data>
|
||||
<record id="ir_config_parameter_timesheet_rounding" model="ir.config_parameter">
|
||||
<field name="key">hr_timesheet.timesheet_rounding</field>
|
||||
<field name="value">15</field>
|
||||
</record>
|
||||
<record id="ir_config_parameter_timesheet_min_duration" model="ir.config_parameter">
|
||||
<field name="key">hr_timesheet.timesheet_min_duration</field>
|
||||
<field name="value">15</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -48,12 +48,9 @@ class AccountAnalyticLine(models.Model):
|
||||
employee_id = fields.Many2one('hr.employee', "Employee", check_company=True, domain=_domain_employee_id)
|
||||
department_id = fields.Many2one('hr.department', "Department", compute='_compute_department_id', store=True, compute_sudo=True)
|
||||
encoding_uom_id = fields.Many2one('uom.uom', compute='_compute_encoding_uom_id')
|
||||
|
||||
# Timer Fields
|
||||
display_timer = fields.Boolean(
|
||||
compute='_compute_display_timer',
|
||||
help="used to display the timer if the encoding unit is 'Hours'"
|
||||
)
|
||||
help="Technical field used to display the timer if the encoding unit is 'Hours'.")
|
||||
|
||||
def _compute_encoding_uom_id(self):
|
||||
for analytic_line in self:
|
||||
@@ -82,10 +79,6 @@ class AccountAnalyticLine(models.Model):
|
||||
for line in self:
|
||||
line.department_id = line.employee_id.department_id
|
||||
|
||||
# ----------------------------------------------------
|
||||
# ORM overrides
|
||||
# ----------------------------------------------------
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
default_user_id = self._default_user()
|
||||
@@ -134,10 +127,6 @@ class AccountAnalyticLine(models.Model):
|
||||
node.set('string', _('Duration (%s)') % (re.sub(r'[\(\)]', '', encoding_uom.name or '')))
|
||||
return etree.tostring(doc, encoding='unicode')
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Business Methods
|
||||
# ----------------------------------------------------
|
||||
|
||||
def _timesheet_get_portal_domain(self):
|
||||
return ['|', '&',
|
||||
('task_id.project_id.privacy_visibility', '=', 'portal'),
|
||||
@@ -207,51 +196,37 @@ class AccountAnalyticLine(models.Model):
|
||||
})
|
||||
return result
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Timer Methods
|
||||
# ----------------------------------------------------
|
||||
def _compute_display_timer(self):
|
||||
""" Check if the encoding unit is 'Hours',
|
||||
|
||||
if yes then the timer button is displayed
|
||||
"""
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
for analytic_line in self:
|
||||
analytic_line.display_timer = analytic_line.encoding_uom_id.id == self.env.ref('uom.product_uom_hour').id
|
||||
analytic_line.display_timer = analytic_line.encoding_uom_id == uom_hour
|
||||
|
||||
def action_timer_start(self):
|
||||
""" Action start the timer of the current timesheet.
|
||||
|
||||
Start timer and search if another timer hasn't been launched.
|
||||
""" Start timer and search if another timer hasn't been launched.
|
||||
If yes, then stop the timer before launch this timer.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not self.timer_start and self.display_timer:
|
||||
self._stop_running_timers()
|
||||
super().action_timer_start()
|
||||
|
||||
def _stop_running_timers(self):
|
||||
""" Search if a timesheet has a timer activated and stop the timer.
|
||||
|
||||
Check if a timer is activated for another timesheet
|
||||
if yes, then update unit_amount field and stop timer,
|
||||
otherwise, do nothing.
|
||||
"""
|
||||
analytic_line = self.search([('timer_start', '!=', False), ('user_id', '=', self.env.uid)])
|
||||
|
||||
if analytic_line:
|
||||
analytic_line.action_timer_stop()
|
||||
|
||||
def action_timer_stop(self):
|
||||
""" Action stop the timer of the current timesheet.
|
||||
|
||||
When the timer must be stopped, we must calculate the new
|
||||
unit_amount based on the timer and the previous value of
|
||||
unit_amount for the current timesheet.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.timer_start and self.display_timer:
|
||||
minutes_spent = self._get_minutes_spent()
|
||||
|
||||
if self.unit_amount == 0 and minutes_spent < 1:
|
||||
# Check if unit_amount equals 0 and minutes_spent is less than 1 minute,
|
||||
# if yes, then remove the timesheet
|
||||
@@ -261,7 +236,5 @@ class AccountAnalyticLine(models.Model):
|
||||
amount = self.unit_amount
|
||||
else:
|
||||
amount = self.unit_amount + minutes_spent * 60 / 3600
|
||||
|
||||
self.write({'unit_amount': amount})
|
||||
|
||||
super().action_timer_stop()
|
||||
|
||||
@@ -20,15 +20,12 @@ class Project(models.Model):
|
||||
allow_timesheet_timer = fields.Boolean('Timesheet Timer', default=False, help="Use a timer to record timesheets on tasks")
|
||||
|
||||
_sql_constraints = [
|
||||
('timer_only_when_timesheet', "CHECK((allow_timesheets = 'f' AND allow_timesheet_timer = 'f') OR (allow_timesheets = 't'))", 'The timesheet timer can only be activated on project allowing timesheet.'),
|
||||
('timer_only_when_timesheet', "CHECK((allow_timesheets = 'f' AND allow_timesheet_timer = 'f') OR (allow_timesheets = 't'))", 'The timesheet timer can only be activated on project allowing timesheets.'),
|
||||
]
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def _onchange_partner_id(self):
|
||||
domain = []
|
||||
if self.partner_id:
|
||||
domain = [('partner_id', '=', self.partner_id.id)]
|
||||
return {'domain': {'analytic_account_id': domain}}
|
||||
return {'domain': {'analytic_account_id': [('partner_id', '=', self.partner_id.id)] if self.partner_id else []}}
|
||||
|
||||
@api.onchange('analytic_account_id')
|
||||
def _onchange_analytic_account(self):
|
||||
@@ -74,16 +71,12 @@ class Project(models.Model):
|
||||
project._create_analytic_account()
|
||||
result = super(Project, self).write(values)
|
||||
if 'allow_timesheet_timer' in values and not values.get('allow_timesheet_timer'):
|
||||
self.env['project.task'].with_context(active_test=False).search([('project_id', 'in', self.ids)]).write({
|
||||
self.with_context(active_test=False).mapped('task_ids').write({
|
||||
'timer_start': False,
|
||||
'timer_pause': False,
|
||||
})
|
||||
return result
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Business Methods
|
||||
# ---------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def _init_data_analytic_account(self):
|
||||
self.search([('analytic_account_id', '=', False), ('allow_timesheets', '=', True)])._create_analytic_account()
|
||||
@@ -102,8 +95,9 @@ class Task(models.Model):
|
||||
subtask_effective_hours = fields.Float("Sub-tasks Hours Spent", compute='_compute_subtask_effective_hours', store=True, help="Sum of actually spent hours on the subtask(s)")
|
||||
timesheet_ids = fields.One2many('account.analytic.line', 'task_id', 'Timesheets')
|
||||
|
||||
timer_start = fields.Datetime("Timesheet Timer Start", default=None)
|
||||
timer_start = fields.Datetime("Timesheet Timer Start")
|
||||
timer_pause = fields.Datetime("Timesheet Timer Last Pause")
|
||||
# YTI FIXME: Those field seems quite useless
|
||||
timesheet_timer_first_start = fields.Datetime("Timesheet Timer First Use", readonly=True)
|
||||
timesheet_timer_last_stop = fields.Datetime("Timesheet Timer Last Use", readonly=True)
|
||||
display_timesheet_timer = fields.Boolean("Display Timesheet Time", compute='_compute_display_timesheet_timer')
|
||||
@@ -145,10 +139,6 @@ class Task(models.Model):
|
||||
for task in self:
|
||||
task.display_timesheet_timer = task.allow_timesheets and task.project_id.allow_timesheet_timer and task.analytic_account_active
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# ORM
|
||||
# ---------------------------------------------------------
|
||||
|
||||
def write(self, values):
|
||||
# a timesheet must have an analytic account (and a project)
|
||||
if 'project_id' in values and self and not values.get('project_id'):
|
||||
@@ -173,10 +163,6 @@ class Task(models.Model):
|
||||
result['arch'] = self.env['account.analytic.line']._apply_timesheet_label(result['arch'])
|
||||
return result
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Timer Methods
|
||||
# ---------------------------------------------------------
|
||||
|
||||
def action_timer_start(self):
|
||||
self.ensure_one()
|
||||
if not self.timesheet_timer_first_start:
|
||||
@@ -201,7 +187,7 @@ class Task(models.Model):
|
||||
|
||||
def _action_create_timesheet(self, time_spent):
|
||||
return {
|
||||
"name": _("Confirm Time Spent"),
|
||||
"name": _("Validate Spent Time"),
|
||||
"type": 'ir.actions.act_window',
|
||||
"res_model": 'project.task.create.timesheet',
|
||||
"views": [[False, "form"]],
|
||||
|
||||
@@ -7,25 +7,20 @@ from odoo import models, fields, api
|
||||
class TimerMixin(models.AbstractModel):
|
||||
_name = 'timer.mixin'
|
||||
_description = 'Timer Mixin'
|
||||
# YTI Note: This mixin is supposed to be extended to
|
||||
# any models
|
||||
|
||||
timer_start = fields.Datetime("Timer Start")
|
||||
timer_pause = fields.Datetime("Timer Last Pause")
|
||||
|
||||
# is_timer_running field is used with timer_toggle_button widget
|
||||
# to create a timer button in the view.
|
||||
# If the timer field is set on False,
|
||||
# then it displays a button with fa-icon-play icon.
|
||||
# Otherwise, it displays a button with fa-icon-stop icon
|
||||
is_timer_running = fields.Boolean(compute="_compute_timer")
|
||||
|
||||
@api.depends('timer_start')
|
||||
def _compute_timer(self) -> None:
|
||||
def _compute_timer(self):
|
||||
for record in self:
|
||||
record.is_timer_running = bool(record.timer_start)
|
||||
|
||||
def action_timer_start(self) -> None:
|
||||
def action_timer_start(self):
|
||||
""" Action start the timer.
|
||||
|
||||
Start timer and search if another timer hasn't been launched.
|
||||
If yes, then stop the timer before launch this timer.
|
||||
"""
|
||||
@@ -45,32 +40,26 @@ class TimerMixin(models.AbstractModel):
|
||||
self.write({'timer_start': False, 'timer_pause': False})
|
||||
return minutes_spent
|
||||
|
||||
def _get_minutes_spent(self) -> float:
|
||||
""" Compute the minutes spent with the timer
|
||||
|
||||
:return minutes spent
|
||||
"""
|
||||
def _get_minutes_spent(self):
|
||||
start_time = self.timer_start
|
||||
stop_time = fields.Datetime.now()
|
||||
|
||||
# timer was either running or paused
|
||||
if self.timer_pause:
|
||||
start_time += (stop_time - self.timer_pause)
|
||||
|
||||
return (stop_time - start_time).total_seconds() / 60
|
||||
|
||||
def action_timer_pause(self) -> None:
|
||||
def action_timer_pause(self):
|
||||
self.write({'timer_pause': fields.Datetime.now()})
|
||||
|
||||
def action_timer_resume(self) -> None:
|
||||
def action_timer_resume(self):
|
||||
new_start = self.timer_start + (fields.Datetime.now() - self.timer_pause)
|
||||
self.write({'timer_start': new_start, 'timer_pause': False})
|
||||
|
||||
@api.model
|
||||
def get_server_time(self):
|
||||
""" Get the time of the server
|
||||
|
||||
The problem with the timer, it's the time can be different between server side and client side.
|
||||
We need to have the time of the server and don't use the local time. Then, we have a timer beginning at 0:00
|
||||
and not 23:59 or something else.
|
||||
""" Returns the server time.
|
||||
The timer widget needs the server time instead of the client time
|
||||
to avoid time desynchronization issues like the timer beginning at 0:00
|
||||
and not 23:59 and so on.
|
||||
"""
|
||||
return fields.Datetime.now()
|
||||
|
||||
@@ -36,17 +36,12 @@
|
||||
<field name="domain_force">[
|
||||
('user_id', '=', user.id),
|
||||
('project_id', '!=', False),
|
||||
'|',
|
||||
'|', '|',
|
||||
('project_id.privacy_visibility', '!=', 'followers'),
|
||||
'|',
|
||||
('project_id.message_partner_ids', 'in', [user.partner_id.id]),
|
||||
('task_id.message_partner_ids', 'in', [user.partner_id.id])
|
||||
('project_id.message_partner_ids', 'in', [user.partner_id.id]),
|
||||
('task_id.message_partner_ids', 'in', [user.partner_id.id])
|
||||
]</field>
|
||||
<field name="groups" eval="[(4, ref('group_hr_timesheet_user'))]"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="timesheet_line_rule_approver" model="ir.rule">
|
||||
@@ -59,10 +54,6 @@
|
||||
('project_id.message_partner_ids', 'in', [user.partner_id.id])
|
||||
]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_timesheet.group_hr_timesheet_approver'))]" />
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="timesheet_line_rule_manager" model="ir.rule">
|
||||
@@ -70,10 +61,6 @@
|
||||
<field name="model_id" ref="analytic.model_account_analytic_line"/>
|
||||
<field name="domain_force">[('project_id', '!=', False)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_timesheet_manager'))]"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -2,8 +2,8 @@ odoo.define('hr_timesheet.Timer', function (require) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This class allow to facilitate to display the timer
|
||||
* in unit_amount field of account.analytic.line.
|
||||
* This class improves the timer display using the
|
||||
* unit_amount field on account.analytic.line.
|
||||
*/
|
||||
class Timer {
|
||||
constructor(hours, minutes, seconds) {
|
||||
|
||||
@@ -19,6 +19,7 @@ var TimerFieldWidget = AbstractField.extend({
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._startTimeCounter();
|
||||
},
|
||||
/**
|
||||
@@ -52,7 +53,7 @@ var TimerFieldWidget = AbstractField.extend({
|
||||
return this._rpc({
|
||||
model: 'account.analytic.line',
|
||||
method: 'get_server_time',
|
||||
args: [false]
|
||||
args: []
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ const FieldTimesheetTime = basicFields.FieldFloatTime.extend({
|
||||
const time = await this._rpc({
|
||||
model: this.model,
|
||||
method: 'get_server_time',
|
||||
args: [false]
|
||||
args: []
|
||||
});
|
||||
this.time = Timer.createTimer(this.recordData.unit_amount, this.recordData.timer_start, time);
|
||||
return this._startTimeCounter();
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
&:hover > i.o-timer-stop-button {
|
||||
color: darken($red, 15%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
<t t-esc="record.name.value"/>
|
||||
</span>
|
||||
<div class="float-right">
|
||||
<field name="is_timer_running" widget="timer_toggle_button" readonly='True' attrs="{ 'invisible': [('display_timer', '=', False)] }" />
|
||||
<field name="is_timer_running" widget="timer_toggle_button" attrs="{ 'invisible': [('display_timer', '=', False)] }" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -572,7 +572,7 @@ class SaleOrder(models.Model):
|
||||
action['context'] = context
|
||||
return action
|
||||
|
||||
def _create_invoices(self, grouped=False, final=False):
|
||||
def _create_invoices(self, grouped=False, final=False, date=None):
|
||||
"""
|
||||
Create the invoice associated to the SO.
|
||||
:param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by
|
||||
|
||||
@@ -59,7 +59,7 @@ class AccountMove(models.Model):
|
||||
domain = line._timesheet_domain_get_invoiced_lines(sale_line_delivery)
|
||||
if date:
|
||||
domain = expression.AND([domain, [('date', '<=', date)]])
|
||||
timesheets = self.env['account.analytic.line'].search(domain).sudo()
|
||||
timesheets = self.env['account.analytic.line'].sudo().search(domain)
|
||||
timesheets.write({'timesheet_invoice_id': line.move_id.id})
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ class AccountMoveLine(models.Model):
|
||||
:return a normalized domain
|
||||
"""
|
||||
return [
|
||||
'&',
|
||||
('so_line', 'in', sale_line_delivery.ids),
|
||||
'&',
|
||||
('timesheet_invoice_id', '=', False),
|
||||
('project_id', '!=', False)
|
||||
]
|
||||
|
||||
@@ -62,7 +62,7 @@ class Project(models.Model):
|
||||
|
||||
# hours (from timesheet) and rates (by billable type)
|
||||
dashboard_domain = [('project_id', 'in', self.ids), ('timesheet_invoice_type', '!=', False), '|', ('so_line', '=', False), ('so_line.state', '!=', 'cancel')] # force billable type
|
||||
dashboard_data = self.env['account.analytic.line'].sudo().read_group(dashboard_domain, ['unit_amount', 'timesheet_invoice_type'], ['timesheet_invoice_type'])
|
||||
dashboard_data = self.env['account.analytic.line'].read_group(dashboard_domain, ['unit_amount', 'timesheet_invoice_type'], ['timesheet_invoice_type'])
|
||||
dashboard_total_hours = sum([data['unit_amount'] for data in dashboard_data]) + total_canceled_hours
|
||||
for data in dashboard_data:
|
||||
billable_type = data['timesheet_invoice_type']
|
||||
|
||||
@@ -65,8 +65,16 @@ class SaleOrder(models.Model):
|
||||
@api.depends('timesheet_ids', 'company_id.timesheet_encode_uom_id')
|
||||
def _compute_timesheet_total_duration(self):
|
||||
for sale_order in self:
|
||||
duration_list = []
|
||||
timesheets = sale_order.timesheet_ids if self.user_has_groups('hr_timesheet.group_hr_timesheet_approver') else sale_order.timesheet_ids.filtered(lambda t: t.user_id.id == self.env.uid)
|
||||
sale_order.timesheet_total_duration = sum([timesheet.unit_amount for timesheet in timesheets])
|
||||
for timesheet in timesheets:
|
||||
timesheet_uom = timesheet.product_uom_id or timesheet.company_id.project_time_mode_id
|
||||
if timesheet_uom != sale_order.timesheet_encode_uom_id and timesheet_uom.category_id == sale_order.timesheet_encode_uom_id.category_id:
|
||||
duration_list.append(timesheet_uom._compute_quantity(timesheet.unit_amount, sale_order.timesheet_encode_uom_id))
|
||||
else:
|
||||
duration_list.append(timesheet.unit_amount)
|
||||
sale_order.timesheet_total_duration = sum(duration_list)
|
||||
|
||||
|
||||
@api.onchange('project_id')
|
||||
def _onchange_project_id(self):
|
||||
@@ -144,18 +152,14 @@ class SaleOrder(models.Model):
|
||||
|
||||
def _create_invoices(self, grouped=False, final=False, date=None):
|
||||
""" Override the _create_invoice method in sale.order model in sale module
|
||||
|
||||
Add new parameter in this method, to invoice sale.order with a date. This date is used in sale_make_invoice_advance_inv into this module.
|
||||
|
||||
:param date
|
||||
|
||||
:return {account.move}: the invoices created
|
||||
"""
|
||||
moves = super(SaleOrder, self)._create_invoices(grouped, final)
|
||||
moves._link_timesheets_to_invoice(date)
|
||||
return moves
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
|
||||
@@ -6,18 +6,12 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
|
||||
@api.model
|
||||
def _default_invoicing_timesheet_enabled(self):
|
||||
product_ids = None
|
||||
if self._context.get('active_id', False):
|
||||
sale_order = self.env['sale.order'].browse(self._context.get('active_id'))
|
||||
product_ids = sale_order.order_line.filtered(lambda sol: sol.invoice_status == 'to invoice').mapped('product_id')
|
||||
elif self._context.get('active_ids', []):
|
||||
sale_orders = self.env['sale.order'].browse(self._context.get('active_ids'))
|
||||
order_lines = sale_orders.mapped('order_line').filtered(lambda sol: sol.invoice_status == 'to invoice')
|
||||
product_ids = order_lines.mapped('product_id')
|
||||
|
||||
if product_ids:
|
||||
return any(product._is_delivered_timesheet() for product in product_ids)
|
||||
return False
|
||||
if 'active_id' not in self._context and 'active_ids' not in self._context:
|
||||
return False
|
||||
sale_orders = self.env['sale.order'].browse(self._context.get('active_id') or self._context.get('active_ids'))
|
||||
order_lines = sale_orders.mapped('order_line').filtered(lambda sol: sol.invoice_status == 'to invoice')
|
||||
product_ids = order_lines.mapped('product_id').filtered(lambda p: p._is_delivered_timesheet())
|
||||
return bool(product_ids)
|
||||
|
||||
date_invoice_timesheet = fields.Date(
|
||||
string='Invoice Timesheets Up To This Date',
|
||||
|
||||
Reference in New Issue
Block a user