合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
7
mrp_workorder_hr/models/__init__.py
Normal file
7
mrp_workorder_hr/models/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import hr_employee
|
||||
from . import mrp_routing
|
||||
from . import mrp_workorder
|
||||
from . import mrp_workcenter
|
||||
from . import quality
|
||||
36
mrp_workorder_hr/models/hr_employee.py
Normal file
36
mrp_workorder_hr/models/hr_employee.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee'
|
||||
|
||||
def login(self, pin=False, set_in_session=True):
|
||||
""" Use the session to remember the current employee between views.
|
||||
The main purpose is to avoid a hash implementation on client side.
|
||||
"""
|
||||
if not pin:
|
||||
pin = False
|
||||
if self.pin == pin:
|
||||
if set_in_session:
|
||||
request.session['employee_id'] = self.id
|
||||
return True
|
||||
elif not pin and self.id == request.session.get('employee_id', []):
|
||||
return True
|
||||
return False
|
||||
|
||||
def logout(self, pin=False):
|
||||
if not pin:
|
||||
pin = False
|
||||
if self.pin == pin:
|
||||
request.session['employee_id'] = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_employee_fields_for_tablet(self):
|
||||
return [
|
||||
'id',
|
||||
'name',
|
||||
'barcode',
|
||||
]
|
||||
8
mrp_workorder_hr/models/mrp_routing.py
Normal file
8
mrp_workorder_hr/models/mrp_routing.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class MrpRouting(models.Model):
|
||||
_inherit = 'mrp.routing.workcenter'
|
||||
|
||||
employee_ratio = fields.Float("Employee Capacity", default=1, help="Number of employees needed to complete operation.")
|
||||
39
mrp_workorder_hr/models/mrp_workcenter.py
Normal file
39
mrp_workorder_hr/models/mrp_workcenter.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from ast import literal_eval
|
||||
|
||||
from odoo import models, fields
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class MrpWorkcenter(models.Model):
|
||||
_inherit = 'mrp.workcenter'
|
||||
|
||||
allow_employee = fields.Boolean("Requires Log In")
|
||||
employee_ids = fields.Many2many(
|
||||
'hr.employee', string="employees with access",
|
||||
help='if left empty, all employees can log in to the workcenter')
|
||||
currency_id = fields.Many2one(related='company_id.currency_id')
|
||||
employee_costs_hour = fields.Monetary(string='Employee Hourly Cost', currency_field='currency_id', default=0.0)
|
||||
|
||||
def action_work_order(self):
|
||||
action = super().action_work_order()
|
||||
# for the call to literal_eval
|
||||
context = action.get('context', '{}')
|
||||
context = context.replace('active_id', str(self.id))
|
||||
action['context'] = dict(literal_eval(context), employee_id=request.session.get('employee_id'))
|
||||
return action
|
||||
|
||||
|
||||
class MrpWorkcenterProductivity(models.Model):
|
||||
_inherit = "mrp.workcenter.productivity"
|
||||
|
||||
employee_id = fields.Many2one(
|
||||
'hr.employee', string="Employee",
|
||||
help='employee that record this working time')
|
||||
|
||||
def _check_open_time_ids(self):
|
||||
self.env['mrp.productivity.time']._read_group([
|
||||
('workorder_id', 'in', self.workorder_id.ids),
|
||||
('date_stop', '=', False),
|
||||
('employee_id', '!=', False),
|
||||
], ['employee_id', 'workorder_id'], ['employee_id', 'workorder_id'], lazy=False)
|
||||
# TODO make check on employees
|
||||
140
mrp_workorder_hr/models/mrp_workorder.py
Normal file
140
mrp_workorder_hr/models/mrp_workorder.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import Command, models, fields, api
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class MrpWorkorder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string="Employee", compute='_compute_employee_id')
|
||||
employee_ids = fields.Many2many('hr.employee', string='Working Employees', copy=False)
|
||||
employee_name = fields.Char(compute='_compute_employee_id')
|
||||
allow_employee = fields.Boolean(related='workcenter_id.allow_employee')
|
||||
|
||||
def _compute_duration(self):
|
||||
wo_ids_without_employees = set()
|
||||
for wo in self:
|
||||
if not wo.workcenter_id.allow_employee:
|
||||
wo_ids_without_employees.add(wo.id)
|
||||
continue
|
||||
now = fields.Datetime.now()
|
||||
wo.duration = self._intervals_duration([(t.date_start, t.date_end or now, t) for t in wo.time_ids])
|
||||
return super(MrpWorkorder, self.env['mrp.workorder'].browse(wo_ids_without_employees))._compute_duration()
|
||||
|
||||
@api.depends('employee_ids')
|
||||
def _compute_employee_id(self):
|
||||
self.employee_id = self.env['hr.employee']
|
||||
self.employee_name = False
|
||||
if request and 'employee_id' in request.session:
|
||||
employee_id = request.session.get('employee_id')
|
||||
else:
|
||||
employee_id = 0
|
||||
for workorder in self:
|
||||
if employee_id in workorder.employee_ids.ids:
|
||||
workorder.employee_id = employee_id
|
||||
workorder.employee_name = self.env['hr.employee'].browse(employee_id).name
|
||||
|
||||
def start_employee(self, employee_id):
|
||||
self.ensure_one()
|
||||
if employee_id in self.employee_ids.ids and any(not t.date_end for t in self.time_ids if t.employee_id.id == employee_id):
|
||||
return
|
||||
self.employee_ids = [Command.link(employee_id)]
|
||||
time_data = self._prepare_timeline_vals(self.duration, datetime.now())
|
||||
time_data['employee_id'] = employee_id
|
||||
self.env['mrp.workcenter.productivity'].create(time_data)
|
||||
|
||||
def stop_employee(self, employee_id):
|
||||
self.ensure_one()
|
||||
if employee_id not in self.employee_ids.ids:
|
||||
return
|
||||
self.employee_ids = [Command.unlink(employee_id)]
|
||||
self.env['mrp.workcenter.productivity'].search([
|
||||
('employee_id', '=', employee_id),
|
||||
('workorder_id', '=', self.id),
|
||||
('date_end', '=', False)
|
||||
])._close()
|
||||
self.employee_ids = [Command.unlink(employee_id)]
|
||||
|
||||
def get_workorder_data(self):
|
||||
# Avoid to get the products full name because code and name are separate in the barcode app.
|
||||
data = super().get_workorder_data() or {}
|
||||
if not self.workcenter_id.allow_employee:
|
||||
data['employee_id'] = False
|
||||
data['employee_ids'] = []
|
||||
data['employee_list'] = []
|
||||
return data
|
||||
employee_domain = [('company_id', '=', self.company_id.id)]
|
||||
if self.workcenter_id.employee_ids:
|
||||
employee_domain = [('id', 'in', self.workcenter_id.employee_ids.ids)]
|
||||
fields_to_read = self.env['hr.employee']._get_employee_fields_for_tablet()
|
||||
data.update({
|
||||
"employee_id": self.employee_id.id,
|
||||
"employee_ids": self.employee_ids.ids,
|
||||
"employee_list": self.env['hr.employee'].search_read(employee_domain, fields_to_read, load=False),
|
||||
})
|
||||
return data
|
||||
|
||||
def record_production(self):
|
||||
action = super().record_production()
|
||||
if action is not True and self.employee_id:
|
||||
action.get('context', {})['employee_id'] = self.employee_id.id
|
||||
return action
|
||||
|
||||
def action_back(self):
|
||||
action = super().action_back()
|
||||
if self.employee_id:
|
||||
action['context']['employee_id'] = self.employee_id.id
|
||||
action['context']['employee_name'] = self.employee_id.name
|
||||
return action
|
||||
|
||||
def _should_start_timer(self):
|
||||
""" Return True if the timer should start once the workorder is opened."""
|
||||
self.ensure_one()
|
||||
if self.workcenter_id.allow_employee:
|
||||
return False
|
||||
return super()._should_start_timer()
|
||||
|
||||
def _intervals_duration(self, intervals):
|
||||
""" Return the duration of the given intervals.
|
||||
If intervals overlaps the duration is only counted once.
|
||||
|
||||
The timer could be share between several intervals. However it is not
|
||||
an issue since the purpose is to make a difference between employee time and
|
||||
blocking time.
|
||||
|
||||
:param list intervals: list of tuple (date_start, date_end, timer)
|
||||
"""
|
||||
if not intervals:
|
||||
return 0.0
|
||||
duration = 0
|
||||
intervals.sort(key=lambda i: i[0])
|
||||
date_start, date_stop, timer = intervals[0]
|
||||
for index, interval in enumerate(intervals):
|
||||
if interval[0] <= date_stop:
|
||||
date_stop = max(date_stop, interval[1])
|
||||
if index != len(intervals) - 1:
|
||||
continue
|
||||
duration += timer.loss_id._convert_to_duration(date_start, date_stop, timer.workcenter_id)
|
||||
date_start, date_stop, timer = interval
|
||||
return duration
|
||||
|
||||
|
||||
class MrpWorkcenterProductivity(models.Model):
|
||||
_inherit = "mrp.workcenter.productivity"
|
||||
|
||||
employee_cost = fields.Monetary('employee_cost', default=0)
|
||||
currency_id = fields.Many2one(related='company_id.currency_id')
|
||||
total_cost = fields.Float('Cost', compute='_compute_total_cost')
|
||||
|
||||
@api.depends('employee_id', 'employee_cost')
|
||||
def _compute_total_cost(self):
|
||||
for time in self:
|
||||
time.total_cost = time.employee_cost * time.duration
|
||||
|
||||
def _close(self):
|
||||
for timer in self:
|
||||
if timer.employee_id:
|
||||
timer.employee_cost = timer.employee_id.hourly_cost
|
||||
return super()._close()
|
||||
14
mrp_workorder_hr/models/quality.py
Normal file
14
mrp_workorder_hr/models/quality.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class QualityCheck(models.Model):
|
||||
_inherit = 'quality.check'
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string="Employee")
|
||||
|
||||
def do_pass(self):
|
||||
res = super().do_pass()
|
||||
if self.workorder_id and self.workorder_id.employee_id:
|
||||
self.employee_id = self.workorder_id.employee_id
|
||||
return res
|
||||
Reference in New Issue
Block a user