[WIP] sale_timesheet_manager:

Agile portal time-sheet manager.  Allow users to create time lines for assigned tasks and add notes before confirming.  Time lines must be confirmed before they are committed. To-do's made for users that have unconfirmed lines that get committed to time-sheets by EOD cron job. (optional)
This commit is contained in:
Brett Spaulding
2020-10-18 23:08:47 -04:00
parent 18d21f462b
commit cdbdc6c937
13 changed files with 258 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
from . import controllers
from . import models

View File

@@ -0,0 +1,23 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{
'name': 'Hibou Project Time Manager',
'version': '13.0.1.3.0',
'category': 'Warehouse',
'author': 'Hibou Corp.',
'license': 'OPL-1',
'website': 'https://hibou.io/',
'depends': [
'sale_timesheet',
'website',
],
'data': [
'security/ir.model.access.csv',
# 'views/project_views.xml',
'views/time_manager_templates.xml',
'views/web_assets.xml',
],
'installable': True,
'application': False,
'auto_install': False,
}

View File

@@ -0,0 +1 @@
from . import portal

View File

@@ -0,0 +1,20 @@
from odoo import _
from odoo.http import Controller, request, route
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError
from odoo.http import request
from logging import getLogger as Logs
_logger = Logs(__name__)
class SaleTimesheetManager(Controller):
@route('/my/task/time/manager', type='http', auth='public', website=True)
def portal_my_timesheet_manager(self, **kwargs):
user = request.env.user
vals = {
'user': user,
'projects': user._get_tasks_grouped_by_project(),
}
return request.render('sale_timesheet_manager.dashboard', vals)

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- TODO: Create a daily cron that will force confirm lines daily if setting is enabled in res config -->

View File

@@ -0,0 +1,2 @@
from . import project
from . import users

View File

@@ -0,0 +1,45 @@
from odoo import api, fields, models
from logging import getLogger as Logs
_logger = Logs(__name__)
class ProjectTask(models.Model):
_inherit = 'project.task'
time_manager_ids = fields.One2many('project.task.time.manager', 'task_id', string='Uncommitted time entries.')
class ProjectTaskTimeManager(models.Model):
_name = 'project.task.time.manager'
_description = 'Used to hold arbitrary data as o2m lines until it is committed via user or server action at EOD'
# Relational Fields
task_id = fields.Many2one('project.task', string='Parent Task')
time_line_ids = fields.One2many('project.task.time.manager.line', 'time_manager_id')
user_id = fields.Many2one('res.user', string='')
class ProjectTaskTimeManagerLine(models.Model):
_name = 'project.task.time.manager.line'
_description = 'Informational time lines that hold data until committed'
# Relational Fields
time_manager_id = fields.Many2one('project.task.time.manager')
user_id = fields.Many2one('res.user', string='Timesheet User')
# Informational Fields
user_confirmed = fields.Boolean('User Confirmed Time')
cron_confirmed = fields.Boolean('Cron Confirmed Time')
date_start = fields.Date('Time Started')
date_end = fields.Date('Time Finished')
duration = fields.Float('Time Duration', compute='_compute_time_line_duration')
def _compute_time_line_duration(self):
for line in self:
if line.date_start and line.date_end:
time = line.date_start + line.date_end
line.duration = float(time.hours)
else:
line.duration = 0

View File

@@ -0,0 +1,17 @@
from odoo import api, fields, models
from logging import getLogger as Logs
_logger = Logs(__name__)
class ResUsers(models.Model):
_inherit = 'res.users'
@api.model
def _get_tasks_grouped_by_project(self):
self.ensure_one()
res = {}
for task in self.env['project.task'].search([('user_id', '=', self.id)]):
res.setdefault(task.project_id, []).append(task)
_logger.warn('-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-')
_logger.warn(res)
return res

View File

@@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"manage_project_task_time_manager","manage project time manager","model_project_task_time_manager","base.group_user",1,1,1,1
"manage_project_task_time_manager_line","manage project time manager line","model_project_task_time_manager_line","base.group_user",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 manage_project_task_time_manager manage project time manager model_project_task_time_manager base.group_user 1 1 1 1
3 manage_project_task_time_manager_line manage project time manager line model_project_task_time_manager_line base.group_user 1 1 1 1

View File

@@ -0,0 +1,11 @@
odoo.define('sale_timesheet_manager.main', function (require) {
"use strict";
$(document).ready(function () {
console.log('Sale Time Manager Loaded');
});
});

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
</odoo>

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="dashboard">
<t t-call="portal.portal_layout">
<div id="dashboard_container">
<t t-call="sale_timesheet_manager.assigned_tasks"/>
</div>
</t>
</template>
<!-- Main Dashboard -->
<template id="assigned_tasks">
<div class="row">
<div class="col-lg-12 mt-2 mb-4 mb-0">
<div class="card">
<h3 class="m-3">Assigned Tasks</h3>
<!-- TODO: Add quick-search on-type filtering for list -->
<div class="position-absolute mr-4" style="right: 0; top: 50%; transform: translateY(-50%);">
<a href="#" class="btn float-right">
<i class="fa fa-search text-muted"></i>
</a>
</div>
</div>
</div>
</div>
<div id="task_container" class="card-container d-inline-block w-100"
style="overflow-x: scroll; white-space: nowrap;">
<t t-foreach="projects" t-as="project">
<t t-set="tasks" t-value="projects[project]"/>
<span class="task-container"
style="width: 320px; display: inline-block; position: relative; vertical-align: top;">
<div>
<div>
<div class="ml-4">
<strong class="card-title">
<span t-esc="project.name"/>
</strong>
</div>
</div>
</div>
<div>
<t t-foreach="tasks" t-as="task">
<div>
<div>
<div class="task-card card m-2 p-1" t-att-data-record-id="task.id">
<!-- Task Title and ID -->
<a href="#" t-att-title="task.name" class="card-header"
style="overflow: hidden;">
<strong class="card-title">
<span t-esc="task.name"/>
</strong>
</a>
<!-- Conditionally colored star system -->
<t t-if="task.priority">
<t t-set="priority" t-value="task.priority"/>
<span class="task-priority-stars float-right"
style="position: absolute; left: 50%; bottom: 5px; transform: translate(-50%, -50%);">
<t t-foreach="range(int(priority))" t-as="star">
<t t-if="star == 0">
<i class="fa fa-star text-muted"></i>
</t>
<t t-if="star == 1">
<i class="fa fa-star text-primary"></i>
</t>
<t t-if="star &gt;= 2">
<i class="fa fa-star text-warning"></i>
</t>
</t>
</span>
</t>
<!-- Task Actions -->
<div class="card-body text-center">
<span>
<a href="#" class="btn m-1">
<i class="fa fa-2x fa-play-circle text-success"></i>
</a>
<a href="#" class="btn btn-warning m-1 d-none">
<i class="fa fa-2x fa-pause-circle"></i>
</a>
<a href="#" class="btn btn-danger m-1 d-none">
<i class="fa fa-2x fa-stop-circle"></i>
</a>
</span>
</div>
<!-- Task info -->
<div class="card-footer">
<a href="#" class="text-info">
<i class="fa fa-list-ul mr-1"></i>
Entries
</a>
<span class="float-right text-muted">
H
<t t-esc="task.id"/>
</span>
</div>
</div>
</div>
</div>
</t>
</div>
</span>
</t>
</div>
</template>
</odoo>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="web_assets_frontend" inherit_id="website.assets_frontend">
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/sale_timesheet_manager/static/src/js/sale_time_manager.js"/>
</xpath>
</template>
</odoo>