lims_administrative_task: add task scheduling

This commit is contained in:
Adrián Bernardi 2021-05-20 11:48:53 -03:00
parent 32368c132d
commit e39f5eef02
8 changed files with 386 additions and 4 deletions

View File

@ -14,6 +14,8 @@ def register():
task.AdministrativeTaskTemplate,
task.AdministrativeTask,
task.EditAdministrativeTaskStart,
task.AdministrativeTaskProgram,
task.GenerateAdministrativeTaskStart,
department.Department,
user.User,
configuration.Configuration,
@ -21,4 +23,5 @@ def register():
module='lims_administrative_task', type_='model')
Pool.register(
task.EditAdministrativeTask,
task.GenerateAdministrativeTask,
module='lims_administrative_task', type_='wizard')

View File

@ -10,6 +10,10 @@ msgctxt "field:lims.administrative.task,closing_date:"
msgid "Closing Date"
msgstr "Fecha de cierre"
msgctxt "field:lims.administrative.task,color:"
msgid "Color"
msgstr "Color"
msgctxt "field:lims.administrative.task,comments:"
msgid "Comments"
msgstr "Observaciones"
@ -50,6 +54,10 @@ msgctxt "field:lims.administrative.task,responsible:"
msgid "Responsible User"
msgstr "Usuario responsable"
msgctxt "field:lims.administrative.task,scheduled:"
msgid "Scheduled"
msgstr "Programada"
msgctxt "field:lims.administrative.task,state:"
msgid "State"
msgstr "Estado"
@ -87,6 +95,42 @@ msgctxt "field:lims.administrative.task.edit.start,responsible:"
msgid "Responsible User"
msgstr "Usuario responsable"
msgctxt "field:lims.administrative.task.generate.start,end_date:"
msgid "End Date"
msgstr "Fecha de finalización"
msgctxt "field:lims.administrative.task.generate.start,start_date:"
msgid "Start Date"
msgstr "Fecha de inicio"
msgctxt "field:lims.administrative.task.generate.start,task_program:"
msgid "Administrative Task Scheduling"
msgstr "Programación de Tarea administrativa"
msgctxt "field:lims.administrative.task.generate.start,tasks:"
msgid "Tasks"
msgstr "Tareas"
msgctxt "field:lims.administrative.task.program,description:"
msgid "Description"
msgstr "Descripción"
msgctxt "field:lims.administrative.task.program,frequency:"
msgid "Frequency"
msgstr "Frecuencia"
msgctxt "field:lims.administrative.task.program,latest_date:"
msgid "Latest scheduled date"
msgstr "Última fecha programada"
msgctxt "field:lims.administrative.task.program,responsible:"
msgid "Responsible User"
msgstr "Usuario responsable"
msgctxt "field:lims.administrative.task.program,type:"
msgid "Type"
msgstr "Tipo"
msgctxt "field:lims.administrative.task.template,description:"
msgid "Description"
msgstr "Descripción"
@ -116,6 +160,10 @@ msgctxt "model:ir.action,name:act_task"
msgid "All Administrative Tasks"
msgstr "Todas las tareas administrativas"
msgctxt "model:ir.action,name:act_task_calendar_list"
msgid "Administrative Tasks Calendar"
msgstr "Calendario de Tareas administrativas"
msgctxt "model:ir.action,name:act_task_configuration"
msgid "Configuration"
msgstr "Configuración"
@ -124,6 +172,10 @@ msgctxt "model:ir.action,name:act_task_mine"
msgid "My Administrative Tasks"
msgstr "Mis tareas administrativas"
msgctxt "model:ir.action,name:act_task_program_list"
msgid "Administrative Tasks Scheduling"
msgstr "Programación de Tareas administrativas"
msgctxt "model:ir.action,name:act_task_template"
msgid "Administrative Tasks Configuration"
msgstr "Configuración de Tareas administrativas"
@ -132,6 +184,10 @@ msgctxt "model:ir.action,name:wiz_edit_task"
msgid "Edit Administrative Task"
msgstr "Editar Tarea administrativa"
msgctxt "model:ir.action,name:wizard_generate_task_calendar"
msgid "Generate Administrative Tasks Calendar"
msgstr "Generar Calendario de Tareas administrativas"
msgctxt "model:ir.action.act_window.domain,name:act_task_domain_all"
msgid "All"
msgstr "Todas"
@ -250,6 +306,10 @@ msgctxt "model:ir.ui.menu,name:menu_task"
msgid "All Administrative Tasks"
msgstr "Todas las tareas administrativas"
msgctxt "model:ir.ui.menu,name:menu_task_calendar"
msgid "Calendar"
msgstr "Calendario"
msgctxt "model:ir.ui.menu,name:menu_task_configuration"
msgid "Configuration"
msgstr "Configuración"
@ -258,6 +318,10 @@ msgctxt "model:ir.ui.menu,name:menu_task_mine"
msgid "My Administrative Tasks"
msgstr "Mis tareas administrativas"
msgctxt "model:ir.ui.menu,name:menu_task_program"
msgid "Administrative Tasks Scheduling"
msgstr "Programación de Tareas administrativas"
msgctxt "model:ir.ui.menu,name:menu_task_template"
msgid "Automatic generation of administrative tasks"
msgstr "Generación automática de Tareas administrativas"
@ -278,6 +342,14 @@ msgctxt "model:lims.administrative.task.edit.start,name:"
msgid "Edit Administrative Task"
msgstr "Editar Tarea administrativa"
msgctxt "model:lims.administrative.task.generate.start,name:"
msgid "Generate Administrative Tasks Calendar"
msgstr "Generar Calendario de Tareas administrativas"
msgctxt "model:lims.administrative.task.program,name:"
msgid "Administrative Task Scheduling"
msgstr "Programación de Tarea administrativa"
msgctxt "model:lims.administrative.task.template,name:"
msgid "Administrative Task Configuration"
msgstr "Configuración de Tarea administrativa"
@ -358,6 +430,22 @@ msgctxt "selection:lims.administrative.task.edit.start,priority:"
msgid "Very Low"
msgstr "Muy baja"
msgctxt "selection:lims.administrative.task.program,frequency:"
msgid "Daily"
msgstr "Diaria"
msgctxt "selection:lims.administrative.task.program,frequency:"
msgid "Monthly"
msgstr "Mensual"
msgctxt "selection:lims.administrative.task.program,frequency:"
msgid "Weekly"
msgstr "Semanal"
msgctxt "selection:lims.administrative.task.program,frequency:"
msgid "Yearly"
msgstr "Anual"
msgctxt "view:lims.administrative.task:"
msgid "Comments"
msgstr "Observaciones"
@ -393,3 +481,11 @@ msgstr "Confirmar"
msgctxt "wizard_button:lims.administrative.task.edit,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:lims.administrative.task.generate,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:lims.administrative.task.generate,start,generate:"
msgid "Generate"
msgstr "Generar"

View File

@ -8,9 +8,10 @@ from email.mime.multipart import MIMEMultipart
from email.header import Header
from trytond.model import Workflow, ModelSQL, ModelView, fields
from trytond.pyson import Eval
from trytond.pyson import PYSONEncoder, Eval
from trytond.pool import Pool
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
Button
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
@ -113,6 +114,8 @@ class AdministrativeTask(Workflow, ModelSQL, ModelView):
], 'State', select=True, readonly=True, required=True)
icon = fields.Function(fields.Char('Icon'), 'get_icon')
comments = fields.Text('Comments')
scheduled = fields.Boolean('Scheduled', readonly=True)
color = fields.Function(fields.Char('Color'), 'get_color')
@classmethod
def __setup__(cls):
@ -159,6 +162,10 @@ class AdministrativeTask(Workflow, ModelSQL, ModelView):
def default_priority():
return '3'
@staticmethod
def default_scheduled():
return False
@classmethod
def create(cls, vlist):
pool = Pool()
@ -190,16 +197,22 @@ class AdministrativeTask(Workflow, ModelSQL, ModelView):
super().delete(tasks)
def get_date(self, name):
if self.scheduled:
return self.expiration_date
return self.create_date.date()
@classmethod
def search_date(cls, name, clause):
cursor = Transaction().connection.cursor()
operator_ = clause[1:2][0]
value = clause[2:3][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE create_date::date ' + operator_ + ' %s',
clause[2:3])
'WHERE (scheduled IS FALSE AND create_date::date '
+ operator_ + ' %s) '
'OR (scheduled IS TRUE AND expiration_date::date '
+ operator_ + ' %s)',
(value, value))
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
@ -232,6 +245,15 @@ class AdministrativeTask(Workflow, ModelSQL, ModelView):
result[t.id] = 'lims-red'
return result
@classmethod
def get_color(cls, tasks, name):
result = {}
for t in tasks:
result[t.id] = 'lightgray'
if t.state in ('pending', 'ongoing', 'standby'):
result[t.id] = 'lightblue'
return result
@classmethod
def check_transition(cls, records, state):
filtered = []
@ -321,6 +343,8 @@ class AdministrativeTask(Workflow, ModelSQL, ModelView):
logger.error("Missing address for '%s' to send email",
task.responsible.rec_name)
continue
if task.scheduled:
continue
subject, body = task._get_subject_body()
msg = cls.create_msg(from_addr, to_addr, subject, body)
@ -455,3 +479,136 @@ class EditAdministrativeTask(Wizard):
AdministrativeTask.send_email_responsible(tasks)
return 'end'
class AdministrativeTaskProgram(ModelSQL, ModelView):
'Administrative Task Scheduling'
__name__ = 'lims.administrative.task.program'
_rec_name = 'type'
type = fields.Selection('get_types', 'Type', required=True)
description = fields.Char('Description', required=True)
responsible = fields.Many2One('res.user', 'Responsible User',
required=True)
frequency = fields.Selection([
('daily', 'Daily'),
('weekly', 'Weekly'),
('monthly', 'Monthly'),
('yearly', 'Yearly'),
], 'Frequency', required=True, sort=False)
latest_date = fields.Function(fields.Date('Latest scheduled date'),
'get_latest_date')
@classmethod
def get_types(cls):
AdministrativeTaskTemplate = Pool().get(
'lims.administrative.task.template')
return AdministrativeTaskTemplate.get_types()
@classmethod
def get_latest_date(cls, programs, name):
AdministrativeTask = Pool().get('lims.administrative.task')
result = {}
for p in programs:
latest_task = AdministrativeTask.search([
('type', '=', p.type),
('responsible', '=', p.responsible.id),
('state', '=', 'pending'),
], order=[('date', 'DESC')], limit=1)
result[p.id] = (latest_task and
latest_task[0].date or None)
return result
class GenerateAdministrativeTaskStart(ModelView):
'Generate Administrative Tasks Calendar'
__name__ = 'lims.administrative.task.generate.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
task_program = fields.Many2Many(
'lims.administrative.task.program', None, None,
'Administrative Task Scheduling', required=True)
tasks = fields.One2Many('lims.administrative.task',
None, 'Tasks')
class GenerateAdministrativeTask(Wizard):
'Generate Administrative Tasks Calendar'
__name__ = 'lims.administrative.task.generate'
start = StateView('lims.administrative.task.generate.start',
'lims_administrative_task.generate_task_calendar_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Generate', 'generate', 'tryton-ok', default=True),
])
generate = StateTransition()
open = StateAction('lims_administrative_task.act_task')
def default_start(self, fields):
Date = Pool().get('ir.date')
today = Date.today()
programs = Transaction().context['active_ids']
defaults = {
'start_date': today,
'end_date': today,
'task_program': programs,
}
return defaults
def transition_generate(self):
AdministrativeTask = Pool().get('lims.administrative.task')
new_tasks = []
for program in self.start.task_program:
new_tasks.extend(self._get_new_tasks(program))
tasks = AdministrativeTask.create(new_tasks)
if tasks:
AdministrativeTask.pending(tasks)
self.start.tasks = tasks
return 'open'
return 'end'
def _get_new_tasks(self, program):
new_tasks = []
for date in self._get_dates(self.start.start_date,
program.frequency, self.start.end_date):
task = {
'type': program.type,
'description': program.description,
'responsible': program.responsible.id,
'expiration_date': date,
'priority': '3',
'state': 'draft',
'scheduled': True,
}
new_tasks.append(task)
return new_tasks
def _get_dates(self, start_date, frequency, end_date):
dates = []
if frequency == 'daily':
delta = relativedelta(days=1)
elif frequency == 'weekly':
delta = relativedelta(days=7)
elif frequency == 'monthly':
delta = relativedelta(months=1)
elif frequency == 'yearly':
delta = relativedelta(years=1)
date = start_date
while date <= end_date:
dates.append(date)
date += delta
return dates
def do_open(self, action):
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', [m.id for m in self.start.tasks]),
])
return action, {}
def transition_open(self):
return 'end'

View File

@ -304,5 +304,97 @@
<field name="action" ref="wiz_edit_task"/>
</record>
<!-- Administrative Tasks Scheduling -->
<record model="ir.ui.view" id="task_program_view_form">
<field name="model">lims.administrative.task.program</field>
<field name="type">form</field>
<field name="name">task_program_form</field>
</record>
<record model="ir.ui.view" id="task_program_view_list">
<field name="model">lims.administrative.task.program</field>
<field name="type">tree</field>
<field name="name">task_program_list</field>
</record>
<record model="ir.action.act_window" id="act_task_program_list">
<field name="name">Administrative Tasks Scheduling</field>
<field name="res_model">lims.administrative.task.program</field>
</record>
<record model="ir.action.act_window.view"
id="act_task_program_view_list">
<field name="sequence" eval="10"/>
<field name="view" ref="task_program_view_list"/>
<field name="act_window" ref="act_task_program_list"/>
</record>
<record model="ir.action.act_window.view"
id="act_task_program_view_form">
<field name="sequence" eval="20"/>
<field name="view" ref="task_program_view_form"/>
<field name="act_window" ref="act_task_program_list"/>
</record>
<menuitem name="Administrative Tasks Scheduling"
action="act_task_program_list"
id="menu_task_program"
parent="menu_config" sequence="30"/>
<!-- Wizard Generate Administrative Tasks Calendar -->
<record model="ir.ui.view"
id="generate_task_calendar_start_view_form">
<field name="model">lims.administrative.task.generate.start</field>
<field name="type">form</field>
<field name="name">generate_task_calendar_start_form</field>
</record>
<record model="ir.action.wizard" id="wizard_generate_task_calendar">
<field name="name">Generate Administrative Tasks Calendar</field>
<field name="wiz_name">lims.administrative.task.generate</field>
</record>
<record model="ir.action.keyword"
id="wizard_generate_task_calendar_keyword">
<field name="keyword">form_action</field>
<field name="model">lims.administrative.task.program,-1</field>
<field name="action" ref="wizard_generate_task_calendar"/>
</record>
<!-- Administrative Tasks Calendar -->
<record model="ir.ui.view" id="task_view_calendar">
<field name="model">lims.administrative.task</field>
<field name="type">calendar</field>
<field name="name">task_calendar</field>
</record>
<record model="ir.action.act_window" id="act_task_calendar_list">
<field name="name">Administrative Tasks Calendar</field>
<field name="res_model">lims.administrative.task</field>
</record>
<record model="ir.action.act_window.view"
id="act_task_calendar_view_calendar">
<field name="sequence" eval="10"/>
<field name="view" ref="task_view_calendar"/>
<field name="act_window" ref="act_task_calendar_list"/>
</record>
<record model="ir.action.act_window.view"
id="act_task_calendar_view_list">
<field name="sequence" eval="20"/>
<field name="view" ref="task_view_list"/>
<field name="act_window" ref="act_task_calendar_list"/>
</record>
<record model="ir.action.act_window.view"
id="act_task_calendar_view_form">
<field name="sequence" eval="30"/>
<field name="view" ref="task_view_form"/>
<field name="act_window" ref="act_task_calendar_list"/>
</record>
<menuitem name="Calendar"
action="act_task_calendar_list"
id="menu_task_calendar"
parent="menu_administrative_task" sequence="50"/>
</data>
</tryton>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<form>
<label name="start_date"/>
<field name="start_date"/>
<label name="end_date"/>
<field name="end_date"/>
<field name="task_program" colspan="4"/>
</form>

View File

@ -0,0 +1,5 @@
<?xml version="1.0"?>
<calendar dtstart="date" dtend="date" mode="month" background_color="color">
<field name="description"/>
<field name="responsible"/>
</calendar>

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<form>
<label name="type"/>
<field name="type"/>
<label name="description"/>
<field name="description"/>
<label name="responsible"/>
<field name="responsible"/>
<label name="frequency"/>
<field name="frequency"/>
<label name="latest_date"/>
<field name="latest_date"/>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<tree>
<field name="type"/>
<field name="description"/>
<field name="responsible"/>
<field name="frequency"/>
<field name="latest_date"/>
</tree>