first push message

This commit is contained in:
2026-07-01 14:41:49 +07:00
parent 6667dec2bf
commit 58b5f46cc4
2951 changed files with 316619 additions and 0 deletions
@@ -0,0 +1,28 @@
Odoo Proprietary License v1.0
This software and associated files (the "Software") may only be used (executed,
modified, executed after modifications) if you have purchased a valid license
from the authors, typically via Odoo Apps, or if you have received a written
agreement from the authors of the Software (see the COPYRIGHT file).
You may develop Odoo modules that use the Software as a library (typically
by depending on it, importing it and using its resources), but without copying
any source code or material from the Software. You may distribute those
modules under the license of your choice, provided that this license is
compatible with the terms of the Odoo Proprietary License (For example:
LGPL, MIT, or proprietary licenses similar to this one).
It is forbidden to publish, distribute, sublicense, or sell copies of the Software
or modified copies of the Software.
The above copyright notice and this permission notice must be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
18.0.0.1 | 04/02/2025
- [IMP] Task timer functionality to allow multiple users.
- [FIX] Minutes and Hours not store on page refresh and duration not count proper.
=> 18.0.0.2 :- Date 14 Feb 2025
:- Removed the required in Project Stage Checklist.
=> 18.0.0.3 :- Date 14 Feb 2025
:- Add all the fields in the company and include the related fields in the settings to resolve the issue.
=> 18.0.0.4 :- Date 19 Feb 2025
:- Fix the issue When a task is created, it displays the missing record of the sale order
=> 18.0.0.5 :- Date 26 Mar 2025
:- Fix the issue When a task is created, its Raise the Access Error
=> 18.0.0.6 :- Date 26 Mar 2025
:- Fix the issue When a task is created, its Raise the Access Error in staging database
=> 18.0.0.7 :- Date 27 Mar 2025
:- Fix the issue create a subtask using button it displays the missing record error
=> 18.0.0.8 | 11 Aug, 2025
:- Improved xpath's and fixed error while installation.
@@ -0,0 +1,4 @@
# -- coding: utf-8 --
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizard
@@ -0,0 +1,58 @@
# -- coding: utf-8 --
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
{
'name': 'All In One Project Management System - All in one PMS',
'version': '19.0.0.0',
'category': 'Project',
'summary': 'Sub Project Access control project delegation project subtask timer project multi assignee daily task update mass stage update project stage project task priority task overdue email project checklist project priority task send by email import task history ',
'description': """
This odoo app helps user to manage project for all need. User can create subtask for a task, User can start, pause and stop task timer, get automatic email notification on daily and weekly task updates, attach and manage documents for project and task, set priority, mass update task, assign task to multiple users, print project and task PDF report, get email notification on before due date and after due date, manage different stages project dynamically, create meeting from task and view, set user to automatically assign on specific stage, add sequence for project and task, also get email notification on task timesheet limit remainder, create task from sale order, and import task from xls or csv file.
""",
'author': 'BROWSEINFO',
'website': "https://www.browseinfo.com/demo-request?app=bi_all_in_one_project_management_system&version=18&edition=Community",
"price": 99,
"currency": 'EUR',
'depends': ['base', 'project', 'calendar', 'sale_management', 'hr_timesheet'],
'data': [
'security/sub_project_security.xml',
'security/ir.model.access.csv',
'security/multi_user_assign_security.xml',
'views/project_task_view.xml',
'views/sub_project_view.xml',
'views/project_view.xml',
'data/data_view.xml',
'data/task_template_view.xml',
'data/remider_alert_mail_data.xml',
'data/remider_alert_cron.xml',
'views/ir_attachment.xml',
'views/view_task_form.xml',
'data/mail_data.xml',
'wizard/mass_update_task_wiz.xml',
'report/project_task_report.xml',
'report/task_details_view.xml',
'report/project_details_view.xml',
'views/res_settings_views.xml',
'wizard/create_task_view.xml',
'wizard/update_project_stage_view.xml',
'views/task.xml',
],
'assets': {
'web.assets_backend': [
'bi_all_in_one_project_management_system/static/src/js/**/*',
'bi_all_in_one_project_management_system/static/src/xml/timer_view.xml',
],
},
'demo': [],
'test': [],
'installable': True,
'auto_install': False,
'live_test_url': 'https://www.browseinfo.com/demo-request?app=bi_all_in_one_project_management_system&version=18&edition=Community',
"images": ['static/description/Banner.gif'],
'license': 'OPL-1',
}
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="scheduler_task_update_daily" model="ir.cron">
<field name="name">Email Task Update daily</field>
<field name="model_id" ref="model_res_users"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="priority">5</field>
<field name="state">code</field>
<field name="code">model.task_update_email()</field>
</record>
<record id="scheduler_task_update_weekly" model="ir.cron">
<field name="name">Email Task Update weekly</field>
<field name="model_id" ref="model_res_users"/>
<field name="interval_number">1</field>
<field name="interval_type">weeks</field>
<field name="priority">5</field>
<field name="state">code</field>
<field name="code">model.weekly_task_update_email()</field>
</record>
<record model="ir.cron" id="cron_post_process_ship_status_tx">
<field name="name">Project Status Cron</field>
<field name="model_id" ref="project.model_project_task"/>
<field name="state">code</field>
<field name="code">model._cron_post_deadline()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<!-- <field name="numbercall">-1</field> -->
<field name="active" eval="True"/>
<!-- <field name="doall" eval="False"/> -->
</record>
<record model="ir.cron" id="availability_create_cron">
<field name="name">Task First Reminder</field>
<field name="model_id" ref="model_project_task"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="state">code</field>
<field name="code">model._cron_task_reminder()</field>
</record>
<record model="ir.cron" id="availability_create_second_cron">
<field name="name">Task Second Reminder</field>
<field name="model_id" ref="model_project_task"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="state">code</field>
<field name="code">model._cron_task_second_reminder()</field>
</record>
<record id="scheduler_timesheet_notification" model="ir.cron">
<field name="name">Task Timesheet Limit Reminder Notification</field>
<field name="model_id" ref="model_project_task"/>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="priority">5</field>
<field name="state">code</field>
<field name="code">model.task_timesheet_reminder()</field>
</record>
<!-- Sequences for Project -->
<record id="seq_project_" model="ir.sequence">
<field name="name">Project</field>
<field name="code">project.project</field>
<field name="prefix">PRJ-</field>
<field name="padding">4</field>
</record>
<!-- Sequences for Project Task -->
<record id="seq_project_task" model="ir.sequence">
<field name="name">Project Task</field>
<field name="code">project.task</field>
<field name="prefix">Task-</field>
<field name="padding">4</field>
</record>
</data>
</odoo>
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="mail_template_task" model="mail.template">
<field name="name">Task: Send by email</field>
<field name="model_id" ref="project.model_project_task"/>
<field name="subject">{{object.name}} </field>
<field name="email_from">{{(object.user_ids[0].email_formatted or user.email_formatted)}}</field>
<field name="partner_to">{{object.partner_id.id}}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p style="margin: 0px; padding: 0px; font-size: 12px;">
Hello,
<br/>
Task <b><span t-esc="object.name"/></b> sent with an attachment.
<br/><br/>
</p>
</div>
</field>
<field name="lang">{{object.partner_id.lang}}</field>
<field name="auto_delete" eval="True"/>
</record>
<record id="email_template_edi_task_reminder1" model="mail.template">
<field name="name">Task Reminder...!!</field>
<field name="subject">Task Deadline Is Close {{object.name or 'n/a' }}</field>
<field name="model_id" ref="bi_all_in_one_project_management_system.model_project_task" />
<field name="auto_delete" eval="True" />
<field name="body_html" type="html">
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Hello</p>
<p> This email is to remind you,that you have a task whose deadline is close. The task name is <t t-out="object.name"></t> </p>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th style="background-color:#9b9da0 !important;">Task</th>
<th style="background-color:#9b9da0 !important;">Project</th>
<th style="background-color:#9b9da0 !important;">Deadline</th>
<th style="background-color:#9b9da0 !important;">Assigned To</th>
</tr>
</thead>
<tbody>
<t t-foreach="object.user_ids" t-as="user">
<tr>
<td><span><t t-out="object.name"></t></span></td>
<td><span><t t-out="object.project_id.name"></t></span></td>
<td><span><t t-out="object.date_deadline.date()"></t></span></td>
<td><span><t t-out="user.name"></t></span></td>
</tr>
</t>
</tbody>
</table>
</div>
</field>
</record>
<record id="email_template_task_timesheet_reminder" model="mail.template">
<field name="name">Task Timesheet Limit Reminder</field>
<field name="subject">Task Timesheet Limit Reminder for {{object.name or 'n/a' }}</field>
<field name="email_to">{{(user.email or '')}}</field>
<field name="model_id" ref="bi_all_in_one_project_management_system.model_project_task" />
<field name="auto_delete" eval="True" />
<field name="body_html"><![CDATA[
<p>Task Timesheet Limit Reminder !!!</p>
<p>-----You are more hours spend than initial hours-----</p>
<p>Task name : <t t-out="object.name"></p>
<p>Task status : <t t-out="object.stage_id.name"></p>
<p>Total initial hours : <t t-out="object.allocated_hours"></p>
<p>Total spend hours : <t t-out="object.total_hours_spent"></p>
]]>
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Scheduler for Delay Task Start Notification-->
<record id="ir_cron_remider_alert" model="ir.cron">
<field name="name">Remider: Project Delay Task Start Notification</field>
<field name="model_id" ref="project.model_project_task"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="state">code</field>
<field name="code">model._run_delay_start_notification()</field>
</record>
<!-- Scheduler for Delay Task Deadline/Overdue Notification-->
<record id="ir_cron_remider_alert_delay" model="ir.cron">
<field name="name">Remider: Project Delay Task Deadline/Overdue Notification</field>
<field name="model_id" ref="project.model_project_task" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="state">code</field>
<field name="code">model._run_delay_deadline_notification()</field>
</record>
</data>
</odoo>
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="email_template_edi_remainder_delay_start_notification" model="mail.template">
<field name="name">Task Start Reminder Email</field>
<field name="email_to">"{{object.company_id.name}}"&lt;{{object.company_id.email}}&gt;</field>
<field name="subject">Reminder Alert:Projetct Delay Task Start Notification</field>
<field name="model_id" ref="bi_all_in_one_project_management_system.model_project_task"/>
<field name="auto_delete" eval="True" />
<field name="body_html"><![CDATA[
<div class="page">
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Dear Employee <t t-foreach="object.user_ids" t-as="user"><t t-out="user.name"/>,</t> </p>
<p> Delay Start Task List </p>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th style="background-color:#9b9da0 !important; width:500px;">Task</th>
<th style="background-color:#9b9da0 !important; width:500px;">Project</th>
<th style="background-color:#9b9da0 !important; width:500px;">Start Date</th>
</tr>
</thead>
<tbody>
<tr>
<td><span> <t t-out="object.name"></t> </span></td>
<td><span> <t t-out="object.project_id.name"></t> </span></td>
<td><span> <t t-out="object.start_date"></t> </span></td>
</tr>
</tbody>
</table>
<p> Thank You </p>
</div>
</div>
]]>
</field>
</record>
<record id="email_template_edi_remainder_delay_overdue_notification" model="mail.template">
<field name="name">Deadline Reminder Email</field>
<field name="email_to">"{{object.company_id.name}}"&lt;{{object.company_id.email}}&gt;</field>
<field name="subject">Reminder Alert:Project Delay Task Deadline/Overdue Notification</field>
<field name="model_id" ref="bi_all_in_one_project_management_system.model_project_task"/>
<field name="auto_delete" eval="True" />
<field name="body_html"><![CDATA[
<div class="page">
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Dear Employee <t t-foreach="object.user_ids" t-as="user"><t t-out="user.name"/>,</t></p>
<p> Overdue Task List </p>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th style="background-color:#9b9da0 !important;">Task</th>
<th style="background-color:#9b9da0 !important;">Project</th>
<th style="background-color:#9b9da0 !important;">Deadline</th>
</tr>
</thead>
<tbody>
<tr>
<td><span> <t t-out="object.name"></t> </span></td>
<td><span> <t t-out="object.project_id.name"></t> </span></td>
<td><span> <t t-out="object.date_deadline"></t> </span></td>
</tr>
</tbody>
</table>
<p> Thank You </p>
</div>
</div>
]]>
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,64 @@
<?xml version="1.0" ?>
<odoo>
<record id="email_template_task_update" model="mail.template">
<field name="name">Task update Reminder</field>
<field name="subject">Task update Reminder</field>
<field name="email_from">{{(user.email_formatted or '')}}</field>
<field name="model_id" ref="bi_all_in_one_project_management_system.model_res_users" />
<field name="auto_delete" eval="True" />
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Hello</p>
<p> This email is to remind about your assigned task</p>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th style="background-color:#9b9da0 !important;">Name</th>
<th style="background-color:#9b9da0 !important;">Deadline</th>
<th style="background-color:#9b9da0 !important;">Stage</th>
<th style="background-color:#9b9da0 !important;">Overdue Time</th>
</tr>
</thead>
<tbody>
<t t-foreach="object.assign_update_ids" t-as="task">
<tr>
<td><span><t t-out="task.name"></span></td>
<td><span><t t-out="task.date_deadline"></span></td>
<td><span><t t-out="task.stage_id.name"></span></td>
<td><span><t t-out="task.dueday"></span></td>
</tr>
</t>
</tbody>
</table>
</div>
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p> This email is to remind about your created task</p>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th style="background-color:#9b9da0 !important;">Name</th>
<th style="background-color:#9b9da0 !important;">Deadline</th>
<th style="background-color:#9b9da0 !important;">Stage</th>
<th style="background-color:#9b9da0 !important;">Overdue Time</th>
</tr>
</thead>
<tbody>
<t t-foreach="object.created_task_ids" t-as="task">
<tr>
<td><span><t t-out="task.name"></span></td>
<td><span><t t-out="task.date_deadline"></span></td>
<td><span><t t-out="task.stage_id.name"></span></td>
<td><span><t t-out="task.dueday"></span></td>
</tr>
</t>
</tbody>
</table>
</div>
]]>
</field>
</record>
</odoo>
@@ -0,0 +1,9 @@
# -- coding: utf-8 --
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from . import project
from . import task_update
from . import project_task
from . import res
from . import task
from . import project_custom_checklist
@@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError,ValidationError
from datetime import datetime, timedelta,date
from odoo.tools.safe_eval import safe_eval
class ProjectInherit(models.Model):
_inherit = "project.project"
_description = "Projects"
sub_project_ids = fields.One2many('sub.project', 'project_id', string="Sub Projects")
sub_task_count = fields.Integer(compute="compute_sub_task_count")
privacy_visibility = fields.Selection([
('followers', 'Invited employees'),
('employees', 'All employees'),
('portal', 'Portal users and all employees'),
],
string='Visibility', required=True,
default='followers',
help="Defines the visibility of the tasks of the project:\n"
"- Invited employees: employees may only see the followed project and tasks.\n"
"- All employees: employees may see all project and tasks.\n"
"- Portal users and all employees: employees may see everything."
" Portal users may see project and tasks followed by.\n"
" them or by someone of their company.")
task_auto_assign_ids = fields.One2many('task.auto.assign', 'project_id')
task_sequence_id = fields.Many2one('ir.sequence',string="Task Entry Sequence")
seq1 = fields.Char("Number")
seq2 = fields.Char(string="Add Prefix")
order_id = fields.Many2one('sale.order', string="Sale Order ")
sale_order_ids = fields.Many2many('sale.order', 'sales_order_project_project_rel', string="Sale Orders")
def compute_sub_task_count(self):
for rec in self:
rec.sub_task_count = len(rec.sub_project_ids)
def unlink(self):
for remove in self:
if len(remove.sub_project_ids) > 0:
raise UserError(_("Sorry !!! You cannot delete this project, because it has sub project(s)"))
return super(ProjectInherit, self).unlink()
@api.model
def default_get(self, fields):
res = super(ProjectInherit, self).default_get(fields)
stages_lines = []
task_ids = self.env['project.task.type'].search([])
stages = [x.id for x in task_ids]
stages_lines += [(0, 0, {'type_ids': stages})]
project_stage_ids = self.env['project.task.type'].search([('dft_for_new_project', '=', True)])
stage_list = []
if project_stage_ids:
for stage in project_stage_ids:
values = {}
values.update({'stage_id': stage.id, 'user_ids': stage.dft_assign_user_id.id})
stage_list.append((0, 0, values))
res.update({'task_auto_assign_ids': stage_list})
return res
# validation on blank user and stage.
@api.constrains('task_auto_assign_ids')
def onchange_auto_assign_ids(self):
if self.task_auto_assign_ids:
for each in self.task_auto_assign_ids:
if not each.user_ids or not each.stage_id:
raise ValidationError(_(" Please enter valid Users and Stages.!"))
# if default new is true then new created project is automatically add.
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
project_obj = self.env['project.project'].search(
[('task_auto_assign_ids.stage_id.dft_for_new_project', '=', True)])
plist = []
for vals in vals_list:
vals['seq1'] = self.env['ir.sequence'].next_by_code('project.project') or _('New')
record = super(ProjectInherit, self).create(vals)
plist.append(record.id)
for p in project_obj:
plist.append(p.id)
stage_obj = self.env['project.task.type'].search([('dft_for_new_project', '=', True)])
for stage in stage_obj:
stage.write({'project_ids': [(6, 0, plist)]})
return record
class SubProject(models.Model):
_name = 'sub.project'
_description = "Sub Projects"
user_id = fields.Many2one('res.users', "Project Manager")
partner_id = fields.Many2one('res.partner', string='Customer')
project_id = fields.Many2one('project.project', string='Project', store=True)
p_project_id = fields.Many2one('project.project', string='Project ', store=True)
@api.onchange('p_project_id')
def set_sub_project_vals(self):
if self.p_project_id:
self.user_id = self.p_project_id.user_id
self.partner_id = self.p_project_id.partner_id
def unlink(self):
for remove in self:
if len(remove.p_project_id.task_ids) > 0:
raise UserError(_("Sorry !!! You cannot delete this project, because it has Task(s)"))
return super(SubProject, self).unlink()
class CalenderEvent(models.Model):
_inherit = 'calendar.event'
task_id = fields.Many2one('project.task', string="Task", readonly=True)
project_id = fields.Many2one('project.project',string="Project")
task_count = fields.Integer('Tasks', compute='_compute_task',)
# count task
@api.depends('task_id')
def _compute_task(self):
self.task_count = self.env['project.task'].search_count([('meeting_id','=',self.id)])
class MeetingDate(models.TransientModel):
_name = 'meeting.date'
_description = "Create Meeting from Task"
start_date = fields.Datetime('Meeting Date', required=True)
def get_data(self):
task_obj= self.env['project.task'].browse(self._context.get('active_ids'))
calendar_obj = self.env['calendar.event'].create({'name':"Meeting from : "+task_obj.name , 'start':str(self.start_date),'duration':1, 'stop':self.start_date + timedelta(hours=1),'task_id':task_obj.id, 'project_id':task_obj.project_id.id})
task_obj.write({'meeting_id':calendar_obj.id})
class TaskAutoAssign(models.Model):
_name = "task.auto.assign"
_description = "Task Auto Assign"
project_id = fields.Many2one('project.project', string='Project')
stage_id = fields.Many2one('project.task.type')
user_ids = fields.Many2one('res.users')
_sql_constraints = [('project_stage_uniq', 'unique (project_id,stage_id,user_ids)',
'Duplicate entry is not allowed !')]
# add project in stages from project using add stages and user
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
project_obj = self.env['project.project'].search([])
stage_obj = self.env['project.task.type'].search([])
get_stage = vals.get('stage_id')
plist = []
record = super(TaskAutoAssign, self).create(vals)
proj_list = []
for each in project_obj:
if each.task_auto_assign_ids:
proj_list.append(each.id)
for p in proj_list:
for stage in stage_obj:
if stage.id == get_stage:
plist.append(p)
stage.write({'project_ids': [(6, 0, plist)]})
return record
class SaleOrderInherit(models.Model):
_inherit = 'sale.order'
@api.depends('project_id', 'task_ids')
def _total_task_count(self):
for order in self:
order.task_count = len(order.task_ids)
task_count = fields.Integer('Task Count', compute='_total_task_count',)
project_id = fields.Many2one('project.project', string="Project", readonly=True, copy=False)
task_ids = fields.One2many('project.task', 'order_id', string="Tasks ", readonly=True, copy=False)
order_task_created = fields.Boolean(string='Order Task Created', default=False, copy=False)
def add_task(self):
action = self.env.ref('bi_all_in_one_project_management_system.action_task_create_create').sudo().read()[0]
return action
def action_view_project(self):
domain = []
if self.order_task_created:
domain = [('id','in',self.task_ids.ids)]
action = self.with_context(active_id=self.project_id.id).env.ref('bi_all_in_one_project_management_system.act_project_project_2_project_task_my').sudo().read()[0]
if action.get('context'):
eval_context = self.env['ir.actions.actions']._get_eval_context()
eval_context.update({'active_id': self.project_id.id,'search_default_my_tasks' : 1})
action['context'] = safe_eval(action['context'], eval_context)
action['domain'] = [('is_empty','=',False)] + domain
return action
class CrmTeam(models.Model):
_inherit = 'crm.team'
order_project_id = fields.Many2one('project.project', string="Order Related Project")
@@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
class ProjectChecklist(models.Model):
_name = "project.checklist"
_description = "Project checklist"
sequence = fields.Integer(string="Sequence")
name = fields.Char(string="Name")
description = fields.Char(string="Description")
class ProjectChecklistLine(models.Model):
_name = "project.checklist.line"
_description = "Project checklist Line"
sequence = fields.Integer(string="Sequence")
name = fields.Char(string="Name")
checklist_id = fields.Many2one('project.checklist', string="name")
project_id = fields.Many2one("project.project", string="project id")
description = fields.Char(string="Description")
date = fields.Date(default=fields.Date.today)
state = fields.Selection([
('new', 'New'),
('complete', 'Complete'),
('cancel', 'Cancel')], string='State',
copy=False, default="new")
@api.onchange('checklist_id')
def _onchange_checklist_id(self):
for checklist in self:
description = ''
if checklist.checklist_id:
description = checklist.checklist_id.description
checklist.update({
'description' : description
})
def action_complete(self):
for rec in self:
rec.state = 'complete'
checklist_len = len(rec.project_id.checklist_line_ids)
completed_progress = len(rec.project_id.checklist_line_ids.filtered(lambda x : x.state == 'complete'))
rec.project_id.write({
'checklist_progress' : (completed_progress * 100) / (checklist_len or 1)
})
def action_cancel(self):
for rec in self:
rec.state = 'cancel'
checklist_len = len(rec.project_id.checklist_line_ids)
completed_progress = len(rec.project_id.checklist_line_ids.filtered(lambda x : x.state == 'complete'))
rec.project_id.write({
'checklist_progress' : (completed_progress * 100) / (checklist_len or 1)
})
def unlink(self):
for rec in self:
project = rec.project_id
project.checklist_line_ids -= rec
checklist_len = len(project.checklist_line_ids)
completed_progress = len(project.checklist_line_ids.filtered(lambda x: x.state == 'complete'))
project.write({
'checklist_progress': (completed_progress * 100) / max((checklist_len or 1), 1)
})
return super(ProjectChecklistLine, self).unlink()
class ProjectChecklistTemplate(models.Model):
_name = "project.checklist.template"
_description = "Project Checklist Template"
_rec_name = "template_name"
sequence = fields.Integer(string="Sequence")
template_name = fields.Char(string="Name")
checklist_ids = fields.Many2many('project.checklist', string="Checklist Template")
class ProjectProject(models.Model):
_inherit = "project.project"
checklist_progress = fields.Integer(string='Checklist Progress', store=True, default=0.0)
checklist_template = fields.Many2many('project.checklist.template', string='Project Checklist template')
checklist_line_ids = fields.One2many('project.checklist.line', 'project_id', string='Checklist')
@api.onchange('checklist_template')
def onchange_checklist_template(self):
if self.checklist_template:
checklist = []
for i in self.checklist_template:
for j in i.checklist_ids:
checklist.append((0,0,{
'checklist_id' : j._origin.id,
'description' : j.description,
'project_id' : self.id,
}))
self.update({'checklist_line_ids':False})
self.update({"checklist_line_ids" : checklist})
@api.constrains('checklist_template')
def _check_checklist_templated(self):
if not self.checklist_template:
for lines in self.checklist_line_ids:
lines.unlink()
@@ -0,0 +1,929 @@
# -*- coding: utf-8 -*-
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from datetime import datetime, timedelta,date
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from odoo import SUPERUSER_ID
from dateutil.relativedelta import relativedelta
from odoo.http import request
import time
class ProjectTaskUserTime(models.Model):
_name = 'project.task.user.time'
_description = 'Task User Time Tracking'
user_id = fields.Many2one('res.users', string='User', required=True)
task_id = fields.Many2one('project.task', string='Task', required=True)
start_time = fields.Datetime(string='Start Time')
end_time = fields.Datetime(string='End Time')
duration = fields.Float(string='Duration', compute='_compute_duration')
@api.depends('start_time', 'end_time')
def _compute_duration(self):
for record in self:
if record.start_time and record.end_time:
duration = record.end_time - record.start_time
record.duration = duration.total_seconds() / 3600.0
else:
record.duration = 0.0
class Projecttask(models.Model):
_name = "project.task"
_inherit = ["project.task", 'mail.thread']
task_stage = fields.Boolean(string='Task Complete',compute='check_task_completed')
date_start= fields.Datetime(string="Date")
def task_timesheet_reminder(self):
current_user = self.env['res.users'].search([('active', '=', True)])
for user in current_user:
task_obj = self.env['project.task'].search([('user_ids','=',user.id)])
for task in task_obj:
if task.total_hours_spent > task.allocated_hours:
template_id = self.env['ir.model.data']._xmlid_lookup(
'bi_all_in_one_project_management_system.email_template_task_timesheet_reminder')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template(task.ids,
[
'subject',
'body_html',
'email_from',
'email_to',
'partner_to',
'email_cc',
'reply_to',
'scheduled_date',
'res_id',
])
for res_id, val in list(values.items()):
val['email_from'] = user.email
val['res_id'] = False
val['author_id'] = user.partner_id.id
mail_mail_obj = self.env['mail.mail']
mail_create_id = mail_mail_obj.sudo().create(val)
if mail_create_id:
mail_create_id.sudo().send()
return True
@api.model
def _cron_task_reminder(self):
sus_id = self.env['res.partner'].browse(SUPERUSER_ID)
for task in self.env['project.task'].search([]):
for tasks_id in self.env['res.config.settings'].sudo().search([],order="id desc", limit=1):
if task.date_deadline != False:
if task.reminder != False:
reminder_date = task.date_deadline
today = datetime.now().date()
if tasks_id.first_date != today:
# if tasks_id.second_date == today:
if task:
template_id = self.env['ir.model.data']._xmlid_lookup(
'bi_all_in_one_project_management_system.email_template_edi_task_reminder1')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template(task.ids,
[
'subject',
'body_html',
'email_from',
'email_to',
'partner_to',
'email_cc',
'reply_to',
'scheduled_date',
'res_id',
])
for res_id, val in list(values.items()):
emails = {user.email for user in task.user_ids if user.email}
val['email_from'] = sus_id.email
val['email_to'] = ','.join(emails) if emails else '' # Handle case with no emails
val['res_id'] = False
val['author_id'] = self.env['res.users'].sudo().browse(request.env.uid).partner_id.id
mail_mail_obj = self.env['mail.mail']
msg_id = mail_mail_obj.sudo().create(val)
if msg_id:
msg_id.sudo().send()
return True
@api.model
def _cron_task_second_reminder(self):
su_id = self.env['res.partner'].browse(SUPERUSER_ID)
for task in self.env['project.task'].search([]):
for tasks_id in self.env['res.config.settings'].sudo().search([],order="id desc", limit=1):
if task.date_deadline != False:
if task.reminder != False:
reminder_date = task.date_deadline
today = datetime.now().date()
if tasks_id.second_date == today:
if task:
template_id = self.env['ir.model.data']._xmlid_lookup(
'bi_all_in_one_project_management_system.email_template_edi_task_reminder1')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template(task.ids,
[
'subject',
'body_html',
'email_from',
'email_to',
'partner_to',
'email_cc',
'reply_to',
'scheduled_date',
'res_id',
])
for res_id, val in list(values.items()):
emails = {user.email for user in task.user_ids if user.email}
val['email_from'] = su_id.email
val['email_to'] = ','.join(emails) if emails else '' # Handle case with no emails
val['res_id'] = False
val['author_id'] = self.env['res.users'].sudo().browse(request.env.uid).partner_id.id
mail_mail_obj = self.env['mail.mail']
msg_id = mail_mail_obj.sudo().create(val)
if msg_id:
msg_id.sudo().send()
return True
def _cron_post_deadline(self):
for task in self.search([('is_task_done','=',False)]):
schedule_task = '3 Tomorrow'
today = datetime.now()
next_day = today + timedelta(days=1)
last_day_of_week = today + timedelta(days=5 - today.weekday())
start_day_of_next_week = last_day_of_week + timedelta(days=1)
last_day_of_next_week = start_day_of_next_week + timedelta(days=6)
later_day = last_day_of_next_week + timedelta(days=1)
if task.date_deadline:
if task.is_task_done:
schedule_task = '7 Done'
elif task.date_deadline < today:
schedule_task = '1 Overdue'
elif task.date_deadline == today:
schedule_task = '2 Today'
elif task.date_deadline == next_day:
schedule_task = '3 Tomorrow'
elif task.date_deadline > next_day and task.date_deadline <= last_day_of_week:
schedule_task = '4 This Week'
elif task.date_deadline >= start_day_of_next_week and task.date_deadline <= last_day_of_next_week:
schedule_task = '5 Next Week'
elif task.date_deadline > last_day_of_next_week:
schedule_task = '6 Later'
task.update({
'schedule_task' : schedule_task
})
def set_task_done(self):
res = {}
for task in self:
if task.is_task_done == False:
stage_id = self.env['project.task.type'].search([('name','=','Done'),('project_ids','in',self.project_id.ids)],limit=1)
task.write({
'is_task_done' : True,
'stage_id' : stage_id.id
})
task.message_post(body=_("The Task is Set to Done"))
else:
stage_id = self.env['project.task.type'].search([('name','=','New')])
for stage in stage_id:
task.write({
'is_task_done' : False,
'stage_id' : stage.id,
})
task.message_post(body=_("The Task is Set to New"))
@api.depends('date_deadline','is_task_done')
def check_schedule(self):
for task in self:
schedule_task = '3 Tomorrow'
today = datetime.now()
next_day = today + timedelta(days=1)
last_day_of_week = today + timedelta(days=5 - today.weekday())
start_day_of_next_week = last_day_of_week + timedelta(days=1)
last_day_of_next_week = start_day_of_next_week + timedelta(days=6)
later_day = last_day_of_next_week + timedelta(days=1)
if task.date_deadline:
if task.is_task_done:
schedule_task = '7 Done'
elif task.date_deadline < today:
schedule_task = '1 Overdue'
elif task.date_deadline == today:
schedule_task = '2 Today'
elif task.date_deadline == next_day:
schedule_task = '3 Tomorrow'
elif task.date_deadline > next_day and task.date_deadline <= last_day_of_week:
schedule_task = '4 This Week'
elif task.date_deadline >= start_day_of_next_week and task.date_deadline <= last_day_of_next_week:
schedule_task = '5 Next Week'
elif task.date_deadline > last_day_of_next_week:
schedule_task = '6 Later'
task.update({
'schedule_task' : schedule_task
})
is_task_done = fields.Boolean(string='Task Done',default=False)
schedule_task = fields.Selection([
('1 Overdue','Overdue'),
('2 Today','Today'),
('3 Tomorrow','Tomorrow'),
('4 This Week','This Week'),
('5 Next Week','Next Week'),
('6 Later','Later'),
('7 Done','Done')], default='3 Tomorrow', compute="check_schedule", store=True)
user_ids = fields.Many2many('res.users', relation='project_task_user_rel', column1='task_id', column2='user_id')
task_completed = fields.Boolean(string="Task Completed")
start_date = fields.Date(string='Start Date', index=True, copy=False, tracking=True)
state_type_name = fields.Char('Status ', related="stage_id.name")
user_in_subtask = fields.Many2one('res.users','Current User', default=lambda self: self.env.user)
subtask_check = fields.Boolean(string="Subtask", default=False)
subtask_count = fields.Integer(string='Count')
done_stage_id = fields.Boolean('Is Done',default=False,store=True)
todo_stage_id = fields.Boolean('Is ToDo',default=False,store=True)
cancel_stage_id = fields.Boolean('Is Cancel',default=False,store=True)
wiz_id = fields.Many2one('subtask.wizard', string="Wiz Parent Id")
task_parent_id = fields.Many2one('project.task', string="Parent Id")
subtask_ids = fields.One2many('project.task', 'task_parent_id', string="Subtask ")
des = fields.Char('Task Description')
is_subtask = fields.Boolean('Is a subtask')
is_task_done = fields.Boolean(string='Task Done',default=False)
schedule_task = fields.Selection([
('1 Overdue','Overdue'),
('2 Today','Today'),
('3 Tomorrow','Tomorrow'),
('4 This Week','This Week'),
('5 Next Week','Next Week'),
('6 Later','Later'),
('7 Done','Done')], default='3 Tomorrow', compute="check_schedule", store=True)
meeting_id = fields.Many2one('calendar.event', string="Meeting", readonly=True)
meeting_count = fields.Integer('Meeting ',compute='_compute_meeting')
seq3 = fields.Char('Number')
order_id = fields.Many2one('sale.order', string="Sale Order ")
order_task_created = fields.Boolean(string='Order Task Created', default=False, copy=False)
is_empty = fields.Boolean(string='Empty Task')
task_product_ids = fields.Many2many('product.template',string='Products')
task_product_id = fields.Many2one('product.template',string='Product')
reminder = fields.Boolean(string='Reminder')
task_Start = fields.Boolean(string='Task Start', default=False, readonly=True)
end_time = fields.Datetime(
string='End Date ',
)
start_time = fields.Datetime(
string='Start Date ',
)
time_left = fields.Float(string='Real Timer')
priority = fields.Selection(selection_add= [
('0', 'Normal'),
('1', 'Important'),
('2', 'Good'),
('3', 'Excellent'),
])
user_times = fields.One2many('project.task.user.time', 'task_id', string='User Times')
task_Start = fields.Boolean(string='Task Start', default=False, readonly=True, compute='_compute_task_start')
@api.depends('user_times.task_id')
def _compute_task_start(self):
for task in self:
current_user = self.env.user
user_time = self.env['project.task.user.time'].search([
('task_id', '=', task.id),
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
task.task_Start = bool(user_time)
def start_task_button(self):
allow_multi_task = self.env.company.allow_multi_task
current_user = self.env.user
user_time = self.env['project.task.user.time'].search([
('task_id', '=', self.id),
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
if allow_multi_task:
if self.env.user.has_group(
'bi_all_in_one_project_management_system.all_project_user') or self.env.user.has_group(
'bi_all_in_one_project_management_system.group_project_manager'):
if not user_time:
self.env['project.task.user.time'].create({
'task_id': self.id,
'user_id': current_user.id,
'start_time': datetime.now(),
})
self.task_Start = True
elif self.env.user.has_group('project.group_project_user'):
if self.env.user in self.user_ids:
if not user_time:
self.env['project.task.user.time'].create({
'task_id': self.id,
'user_id': current_user.id,
'start_time': datetime.now(),
})
self.task_Start = True
else:
raise UserError(_("User can only start his own task"))
else:
active_user_task = self.env['project.task.user.time'].search([
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
if active_user_task:
raise ValidationError(_('You can start only one task.....'))
else:
if self.env.user.has_group(
'bi_all_in_one_project_management_system.all_project_user') or self.env.user.has_group(
'bi_all_in_one_project_management_system.group_project_manager'):
if not user_time:
self.env['project.task.user.time'].create({
'task_id': self.id,
'user_id': current_user.id,
'start_time': datetime.now(),
})
self.task_Start = True
elif self.env.user.has_group('project.group_project_user'):
if self.env.user in self.user_ids:
if not user_time:
self.env['project.task.user.time'].create({
'task_id': self.id,
'user_id': current_user.id,
'start_time': datetime.now(),
})
self.task_Start = True
else:
raise UserError(_("User can only start his own task"))
# count meeting
@api.depends('meeting_id')
def _compute_meeting(self):
for rec in self:
rec.meeting_count = self.env['calendar.event'].search_count([('task_id','=',rec.id)])
@api.onchange('stage_id')
def _get_project_stage(self):
if self.stage_id.name == 'Done':
self.task_completed = True
else:
self.task_completed = False
if self.project_id:
for each in self.project_id.task_auto_assign_ids:
if self.stage_id.id == each.stage_id.id and self.project_id.id == each.project_id.id:
self.user_ids = each.user_ids
@api.model
def _run_delay_deadline_notification(self):
su_id = self.env['res.partner'].browse(SUPERUSER_ID)
for task in self.env['project.task'].search([('date_deadline', '!=', None), ('user_ids', '!=', None),('stage_id','not in','Done'),('stage_id','not in','Cancelled')]):
for tasks_id in self.env['res.config.settings'].sudo().search([],order="id desc", limit=1):
count_day = tasks_id.delay_count
reminder_date = task.date_deadline + relativedelta(days=count_day)
today = datetime.now()
if reminder_date < today and tasks_id.delay_notification:
if task:
template_id = self.env['ir.model.data']._xmlid_lookup('bi_all_in_one_project_management_system.email_template_edi_remainder_delay_overdue_notification')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template([task.id],('subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to'),)[task.id]
values['email_from'] = su_id.email
values['res_id'] = False
values['author_id'] = self.env['res.users'].browse(request.env.uid).partner_id.id
mail_mail_obj = self.env['mail.mail']
msg_id = mail_mail_obj.sudo().create(values)
if msg_id:
msg_id.sudo().send()
return True
@api.model
def _run_delay_start_notification(self):
su_id = self.env['res.partner'].browse(SUPERUSER_ID)
for task in self.env['project.task'].search([('start_date', '!=', None),('stage_id','not in','In Progress'),('stage_id','not in','Done'),('stage_id','not in','Cancelled')]):
for tasks_id in self.env['res.config.settings'].sudo().search([],order="id desc", limit=1):
count_day = tasks_id.start_count
reminder_date = task.start_date + relativedelta(days=count_day)
today = datetime.now().date()
if reminder_date < today and tasks_id.start_notification:
if task:
template_id = self.env['ir.model.data']._xmlid_lookup('bi_all_in_one_project_management_system.email_template_edi_remainder_delay_start_notification')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template([task.id],('subject', 'body_html', 'email_from','email_to','partner_to', 'email_cc', 'reply_to'))[task.id]
values['email_from'] = su_id.email
values['email_to'] = task.user_ids.email
values['res_id'] = False
values['author_id'] = self.env['res.users'].browse(request.env.uid).partner_id.id
mail_mail_obj = self.env['mail.mail']
msg_id = mail_mail_obj.sudo().create(values)
if msg_id:
msg_id.sudo().send()
return True
@api.model
def default_get(self, field_vals):
res = super(Projecttask, self).default_get(field_vals);
if 'recurrence_id' not in res and 'recurrence_id' in field_vals:
res['recurrence_id'] = False
project = self.env["project.project"].browse(res.get("project_id",[]));
if project:
res["priority"] = project.priority
current_stage = self._context.get('default_stage_id')
current_project = self._context.get('default_project_id')
project = self.env['project.project'].search([('id', '=', current_project)])
project_stage_ids = self.env['project.task.type'].search([('dft_for_new_project', '=', True)])
if project_stage_ids:
for rec in project_stage_ids:
if rec.id == current_stage:
res.update({'user_ids': [(4, rec.dft_assign_user_id.id)]})
if project.task_auto_assign_ids:
for stage in project.task_auto_assign_ids:
if stage.stage_id.id == current_stage:
res.update({'user_ids': [(4, stage.user_ids.id)]})
else:
pass
order_id = self.env['sale.order'].browse(self._context.get('active_id'))
if not order_id.exists():
order_id = False
if order_id:
team_id = order_id.team_id
project = self.env['project.project'].search([('id', '=', current_project)])
date = fields.Date.context_today(self)
date_deadline = date + relativedelta(days=1)
if project :
res.update({
'project_id': project.id,
'date_deadline': date_deadline
})
return res
def _find_mail_template(self, force_confirmation_template=False):
template_id = False
template_id = self.env['ir.model.data']._xmlid_to_res_id('bi_all_in_one_project_management_system.mail_template_task', raise_if_not_found=False)
return template_id
def action_send_task(self):
''' Opens a wizard to compose an email, with relevant mail template loaded by default '''
self.ensure_one()
template_id = self._find_mail_template()
lang = self.env.context.get('lang')
template = self.env['mail.template'].browse(template_id)
attachments = self.env['ir.attachment'].search([('res_model','=','project.task'),('res_id','=',self.id)])
attachments_ids=[]
for attachment in attachments:
attachments_ids.append(attachment.id)
ctx = {
'default_model': 'project.task',
'default_res_ids': self.ids,
'default_use_template': bool(template_id),
'default_template_id': template_id,
'default_attachment_ids': ([(6,0,attachments_ids)]),
'default_composition_mode': 'comment',
'custom_layout': "mail.mail_notification_paynow",
'force_email': True,
}
return {
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(False, 'form')],
'view_id': False,
'target': 'new',
'context': ctx,
}
@api.depends('wiz_id.subtask_lines')
def sub_task_found(self):
for each in self:
each.subtask_count = 0
def action_done(self):
done_stage_search = self.env['res.config.settings'].search([], limit=1, order="id desc").done_stage_ckecklist
if not done_stage_search:
raise UserError('You can not move stage, Please Configure Done stage.')
else:
self.write({'stage_id' : done_stage_search.id,'done_stage_id' : True,'todo_stage_id' : False})
def action_cancel(self):
cancel_stage_search = self.env['res.config.settings'].search([], limit=1, order="id desc").cancel_stage_ckecklist
if not cancel_stage_search:
raise UserError('You can not move stage, Please Configure Cancel stage.')
else:
self.write({'stage_id' : cancel_stage_search.id,'cancel_stage_id' : True})
def action_todo(self):
todo_stage_search = self.env['res.config.settings'].search([], limit=1, order="id desc").todo_stage_ckecklist
if not todo_stage_search:
raise UserError('You can not move stage, Please To Do stage.')
else:
self.write({'stage_id' : todo_stage_search.id, 'todo_stage_id' : True,'done_stage_id' : False})
@api.depends('stage_id', 'stage_id.task_completed')
def check_task_completed(self):
for rec in self:
if rec.stage_id and rec.stage_id.task_completed:
rec.task_stage = True
rec.date_deadline = False
else:
rec.task_stage = False
@api.depends('stage_id')
def _inverse_task_stage(self):
for rec in self:
if rec.stage_id and rec.stage_id.task_completed:
rec.task_stage = True
rec.date_deadline = False
else:
rec.task_stage = False
@api.onchange('task_stage')
def _inverse_task_completed(self):
for rec in self:
if rec.task_stage and rec.project_id and rec.project_id.type_id.task_completed:
rec.date_deadline = False
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
project = vals.get('project_id')
project_id = self.env['project.project'].browse(project)
task_sq_code = project_id.task_sequence_id.code
if project_id.seq2 and project_id.task_sequence_id:
combine_seq = project_id.seq2 + "/" + str(self.env['ir.sequence'].sudo().next_by_code(task_sq_code))
else:
combine_seq = self.env['ir.sequence'].sudo().next_by_code('project.task') or _('New')
vals.update({'seq3': combine_seq})
rec = super(Projecttask, self).create(vals_list)
for p_task in self:
for task in p_task.subtask_ids:
if task.state_type_name:
msg = task.state_type_name + ':' + task.name
else:
msg = task.name
p_task.message_post(body=msg)
return rec
def write(self, vals):
old_ids = []
if 'project_id' in vals:
project = vals.get('project_id')
project_id = self.env['project.project'].browse(project)
task_sq_code = project_id.task_sequence_id.code
if project_id.seq2 and project_id.task_sequence_id:
combine_seq = project_id.seq2 + "/" + str(self.env['ir.sequence'].next_by_code(task_sq_code))
else:
combine_seq = self.env['ir.sequence'].next_by_code('project.task') or _('New')
vals.update({'seq3': combine_seq})
for p_task in self:
for task in p_task.subtask_ids:
if task.state_type_name:
msg = task.state_type_name + ':' + task.name
else:
msg = task.name
p_task.message_post(body=msg)
for j in p_task.tag_ids:
old_ids.append(j.name)
res = super(Projecttask, self).write(vals)
for task in self:
if 'stage_id' in vals and vals['stage_id'] and task.stage_id.name == 'Done':
task.date_deadline = False
elif 'stage_id' in vals and vals['stage_id'] and task.stage_id.name != 'Done':
task.date_deadline = task.date_start
for obj in self:
new_ids = []
if vals.get('tag_ids', False):
all_ids = vals.get('tag_ids')
if all_ids[0][1] == False:
for i in all_ids[0][2]:
tag_obj = self.env['project.tags'].search([('id', '=', i)]).name
new_ids.append(tag_obj)
final_new = str(new_ids)[1:-1]
final_old = str(old_ids)[1:-1]
obj.message_post(body=_("Tags added: %s --> %s ") % (final_old, final_new))
else:
for i in all_ids:
tag_obj = self.env['project.tags'].search([('id', '=', i[1])]).name
new_ids.append(tag_obj)
final_new = str(new_ids)[1:-1]
final_old = str(old_ids)[1:-1]
obj.message_post(body=_("Tags added: %s --> %s ") % (final_old, final_new))
if vals.get('stage_id'):
task_type_search = self.env['res.config.settings'].search([], limit=1, order="id desc").warning_child_task
if task_type_search:
if vals.get('stage_id'):
stage_name = self.env['project.task.type'].browse(vals.get('stage_id')).name
if self.is_task_done == False:
vals.update({
'state' : '1_done'
})
else:
vals.update({
'state' : '04_waiting_normal',
})
if stage_name in ['Archive']:
vals.update({
'active':False
})
if vals.get('task_product_id'):
new_task_product_id = self.env['product.template'].browse(int(vals.get('task_product_id')))
old_task_product_id = self.task_product_id
new_task_product_id.write({
'task_ids' : [(4, self.id)],
'project_id' : self.project_id.id
})
if old_task_product_id:
old_task_product_id.write({
'task_ids' : [(3, self.id)],
})
return res
# @api.returns('self', lambda value: value.id)
def copy(self, default=None):
rec = super(Projecttask, self).copy(default)
return rec
def action_get_attachment_view(self):
self.ensure_one()
res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment')
res['domain'] = [('res_model', '=', 'project.task'), ('res_id', 'in', self.ids)]
res['context'] = {'default_res_model': 'project.task', 'default_res_id': self.id}
return res
def duplicate_task(self):
for task in self:
task.copy(default={
'name' : task.name + ' (Copy)',
'stage_id' : task.stage_id and task.stage_id.id,
})
@api.onchange('parent_id')
def button_disable(self):
if self.parent_id:
self.is_subtask = True
self.task_parent_id = self.parent_id.id
self.project_id = self.parent_id.project_id
else:
self.is_subtask = False
self.task_parent_id = self.id
# self.project_id = False
class subtask_wizard(models.Model):
_name = 'subtask.wizard'
_description = "Subtask Wizard"
subtask_lines = fields.One2many('project.task', 'wiz_id', string="Task Line")
def create_subtask(self):
list_of_stage = []
project_task_id = self.env['project.task'].browse(self._context.get('active_id'))
for stage in project_task_id.project_id.type_ids:
stage_ids = self.env['project.task.type'].search([('id', '=', stage.id)])
list_of_stage.append(stage_ids.id)
for task in self.subtask_lines:
task.task_parent_id = self._context.get('active_id')
task.description = task.des
task.is_subtask = True
task.project_id = project_task_id.project_id.id
task.subtask_check = True
if task.state_type_name:
msg = task.state_type_name + ' ' +':' + ' ' + task.name
else:
msg = 'new' + ' ' +':' + ' ' + task.name
for t in task.task_parent_id:
t.sudo().message_post(body=msg)
return True
class ProjectTaskType(models.Model):
_inherit= 'project.task.type'
task_completed = fields.Boolean(string='Task Completed')
dft_assign_user_id = fields.Many2one('res.users', string="Default Assigned User")
dft_for_new_project = fields.Boolean(string="Default for New Project")
class Project(models.Model):
_inherit = 'project.project'
priority = fields.Selection([
('0', 'Normal'),
('1', 'Important'),
('2', 'Good'),
('3', 'Excellent'),
], index=True, string="Priority")
@api.onchange('priority')
def onchange_priority(self):
project_task = self.env['project.task'].search([('project_id','in',self.ids)])
for i in project_task:
i.priority=self.priority
class AnalyticLine(models.Model):
_inherit = 'account.analytic.line'
end_time = fields.Datetime(
string='End Date',
)
start_time = fields.Datetime(
string='Start Date',
)
class Calculate_time(models.TransientModel):
_name = 'project.task.timer.wizard'
_description="Calculate Time"
description=fields.Char(string='Description')
end_time = fields.Datetime(string='End Date', readonly=True)
start_time = fields.Datetime(string='Start Date', readonly=True)
duration = fields.Float(string='Duration',readonly=True)
@api.model
def default_get(self, fields):
res = super(Calculate_time, self).default_get(fields)
active_ids = self.env.context.get('active_ids')
current_task_id = self.env['project.task'].browse(active_ids[0])
current_user = self.env.user
user_time = self.env['project.task.user.time'].search([
('task_id', '=', current_task_id.id),
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
if user_time:
diff = datetime.now() - user_time.start_time
hours, remainder = divmod(diff.total_seconds(), 3600)
minutes, _ = divmod(remainder, 60)
time_float = hours + minutes / 60.0
res.update({
'start_time': user_time.start_time,
'end_time': datetime.now(),
'duration': time_float,
})
return res
def end_task_kanban(self):
active_ids = self.env.context.get('active_ids')
current_task_id = self.env['project.task'].browse(active_ids)
current_user = self.env.user
for task in current_task_id:
user_time = self.env['project.task.user.time'].search([
('task_id', '=', task.id),
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
if user_time:
user_time.end_time = datetime.now()
user_time._compute_duration()
task.task_Start = False
if self.env.user.has_group(
'bi_all_in_one_project_management_system.all_project_user') or self.env.user.has_group(
'bi_all_in_one_project_management_system.group_project_manager'):
timesheet = self.env['account.analytic.line']
vals = {
'date': user_time.start_time,
'name': self.description,
'project_id': task.project_id.id,
'task_id': task.id,
'unit_amount': user_time.duration,
'end_time': user_time.end_time,
}
timesheet.create(vals)
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
else:
raise ValidationError("You cannot end this task.")
else:
raise ValidationError("No active task found to end for this user")
def end_task(self):
active_ids = self.env.context.get('active_ids')
current_task_id = self.env['project.task'].browse(active_ids)
current_user = self.env.user
for task in current_task_id:
user_time = self.env['project.task.user.time'].search([
('task_id', '=', task.id),
('user_id', '=', current_user.id),
('end_time', '=', False)
], limit=1)
if user_time:
user_time.end_time = datetime.now()
user_time._compute_duration()
task.task_Start = False
if self.env.user.has_group(
'bi_all_in_one_project_management_system.all_project_user') or self.env.user.has_group(
'bi_all_in_one_project_management_system.group_project_manager'):
timesheet = self.env['account.analytic.line']
vals = {
'date': user_time.start_time,
'name': self.description,
'project_id': task.project_id.id,
'task_id': task.id,
'unit_amount': user_time.duration,
'end_time': user_time.end_time,
}
timesheet.create(vals)
elif self.env.user.has_group('project.group_project_user'):
if self.env.user in current_task_id.user_ids:
timesheet = self.env['account.analytic.line']
vals = {
'date': user_time.start_time,
'name': self.description,
'project_id': task.project_id.id,
'task_id': task.id,
'unit_amount': user_time.duration,
'end_time': user_time.end_time,
}
timesheet.create(vals)
else:
raise ValidationError("You can not end this task.")
@@ -0,0 +1,85 @@
# -- coding: utf-8 --
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from odoo import fields , models , api , _
from ast import literal_eval
from odoo.exceptions import UserError
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from datetime import datetime, timedelta
from odoo import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
start_notification = fields.Boolean(string='Delay Task Start Notification', default=False)
delay_notification = fields.Boolean(string='Delay Task Deadline/Overdue Notification', default=False)
start_count = fields.Integer(string='Delay Day(s)', default=0)
delay_count = fields.Integer(string='Delay Deadline Day(s)', default=0)
done_stage_ckecklist = fields.Many2one('project.task.type', 'Done Stage')
todo_stage_ckecklist = fields.Many2one('project.task.type', 'To Do Stage')
cancel_stage_ckecklist = fields.Many2one('project.task.type', 'Cancel Stage')
warning_child_task = fields.Many2one('project.task.type',
'Prevent stage to change until all tasks on the same stage')
first_reminder = fields.Float(string='First Reminder (Days)')
second_reminder = fields.Float(string='Second Reminder (Days)')
first_date = fields.Date(compute='convert_first_date')
second_date = fields.Date(compute='convert_second_date')
allow_multi_task = fields.Boolean(string='Allow Multi Task')
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
_description="Res config Settings"
start_notification = fields.Boolean(string='Delay Task Start Notification', related='company_id.start_notification', default=False, readonly=False)
delay_notification = fields.Boolean(string='Delay Task Deadline/Overdue Notification', related='company_id.delay_notification', default=False, readonly=False)
start_count = fields.Integer(string='Delay Day(s)', related='company_id.start_count', default=0, readonly=False)
delay_count = fields.Integer(string='Delay Deadline Day(s)', related='company_id.delay_count', default=0, readonly=False)
done_stage_ckecklist = fields.Many2one('project.task.type', string='Done Stage', related='company_id.done_stage_ckecklist', readonly=False)
todo_stage_ckecklist = fields.Many2one('project.task.type', string='To Do Stage', related='company_id.todo_stage_ckecklist' , readonly=False)
cancel_stage_ckecklist = fields.Many2one('project.task.type', string='Cancel Stage', related='company_id.cancel_stage_ckecklist', readonly=False)
warning_child_task = fields.Many2one('project.task.type', string='Prevent stage to change until all tasks on the same stage', related='company_id.warning_child_task' , readonly=False)
first_reminder = fields.Float(string='First Reminder (Days)', related='company_id.first_reminder', readonly=False)
second_reminder = fields.Float(string='Second Reminder (Days)', related='company_id.second_reminder', readonly=False)
first_date = fields.Date(string='First Reminder Date', related='company_id.first_date', readonly=False)
second_date = fields.Date(string='Second Reminder Date', related='company_id.second_date', readonly=False)
allow_multi_task = fields.Boolean(string='Allow Multi Task', related='company_id.allow_multi_task', readonly=False)
def validate_date(self):
if self.first_reminder > self.second_reminder:
return True
else:
raise UserError(_('First Reminder(Days) should be greater than Second Reminder(Days)'))
return False
@api.onchange('first_reminder')
def convert_first_date(self):
self.first_date = None
for tasks in self.env['project.task'].search([]):
if tasks.date_deadline !=False:
reminder_date = datetime.strptime(tasks.date_deadline.strftime("%Y/%m/%d %H:%M:%S"),"%Y/%m/%d %H:%M:%S")
first = reminder_date - timedelta(days=self.first_reminder)
then = datetime.strptime(str(first), '%Y-%m-%d %H:%M:%S').date()
today = datetime.now().date()
if then == today:
self.first_date = then
@api.onchange('second_reminder')
def convert_second_date(self):
self.second_date = None
for proj_task in self.env['project.task'].search([]):
if proj_task.date_deadline !=False:
reminders_date = datetime.strptime(proj_task.date_deadline.strftime("%Y/%m/%d %H:%M:%S"),"%Y/%m/%d %H:%M:%S")
second = reminders_date - timedelta(days=self.second_reminder)
now = datetime.strptime(str(second), '%Y-%m-%d %H:%M:%S').date()
today = datetime.now().date()
if now == today:
self.second_date = now
@@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
import time
import tempfile
import binascii
import xlrd
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
from datetime import date, datetime
from odoo.exceptions import ValidationError
from odoo import models, fields, exceptions, api, _
import logging
_logger = logging.getLogger(__name__)
import io
from io import StringIO
try:
import csv
except ImportError:
_logger.debug('Cannot `import csv`.')
try:
import xlwt
except ImportError:
_logger.debug('Cannot `import xlwt`.')
try:
import cStringIO
except ImportError:
_logger.debug('Cannot `import cStringIO`.')
try:
import base64
except ImportError:
_logger.debug('Cannot `import base64`.')
class import_task(models.TransientModel):
_name = "import.task"
_description = "Import Task"
file = fields.Binary('File')
import_option = fields.Selection([('csv', 'CSV File'), ('xls', 'XLS File')], string='Select', default='csv')
def create_task(self, values):
project_task_obj = self.env['project.task']
project_id = self.find_project(values.get('project_id'))
user_ids = self.find_user(values.get('user_ids'))
tag_ids = self.find_tags(values.get('tag_ids'))
if values.get('date_deadline') != '':
deadline_date = self.find_deadline_date(values.get('date_deadline'))
else:
deadline_date = False
vals = {
'name': values.get('name'),
'project_id': project_id.id,
'user_ids': [(6, 0, [x.id for x in user_ids])],
'tag_ids': [(6, 0, [x.id for x in tag_ids])],
'date_deadline': deadline_date,
'description': values.get('description'),
}
res = project_task_obj.create(vals)
return res
def find_project(self, name):
project_obj = self.env['project.project']
project_search = project_obj.search([('name', '=', name)])
if project_search:
return project_search
else:
project_id = project_obj.create({
'name': name})
return project_id
def find_tags(self, name):
project_tags_obj = self.env['project.tags']
project_tags_search = project_tags_obj.search([('name', '=', name)])
if project_tags_search:
return project_tags_search
else:
raise ValidationError(_(' "%s" Tags is not available.') % name)
def find_user(self, name):
user_obj = self.env['res.users']
user_search = user_obj.search([('name', '=', name)])
if user_search:
return user_search
else:
raise ValidationError(_(' "%s" User is not available.') % name)
def find_deadline_date(self, date):
project_task_obj = self.env['project.task']
DATETIME_FORMAT = "%Y-%m-%d"
if date:
try:
i_date = datetime.strptime(date, DATETIME_FORMAT)
return i_date
except Exception:
raise ValidationError(_('Wrong Date Format. Date Should be in format YYYY-MM-DD.'))
def import_task(self):
if self.import_option == 'csv':
print('if called=============')
keys = ['name', 'project_id', 'user_ids', 'tag_ids', 'date_deadline', 'description']
try:
csv_data = base64.b64decode(self.file)
data_file = io.StringIO(csv_data.decode("utf-8"))
data_file.seek(0)
file_reader = []
csv_reader = csv.reader(data_file, delimiter=',')
file_reader.extend(csv_reader)
except Exception:
raise exceptions.ValidationError(_("Please select CSV/XLS file or You have selected invalid file "))
values = {}
for i in range(len(file_reader)):
field = list(map(str, file_reader[i]))
values = dict(zip(keys, field))
if values:
if i == 0:
continue
else:
res = self.create_task(values)
elif self.import_option == 'xls':
print('elif called============')
try:
fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xls")
fp.write(base64.b64decode(self.file))
fp.seek(0)
workbook = xlrd.open_workbook(fp.name)
sheet = workbook.sheet_by_index(0)
except Exception:
raise ValidationError(_("Please select CSV/XLS file or You have selected invalid file "))
for row_no in range(1, sheet.nrows): # skip header
line = [str(cell.value).strip() for cell in sheet.row(row_no)]
# Safely handle date field
date_string = False
if line[4]:
try:
if isinstance(sheet.cell(row_no, 4).value, (int, float)):
# Excel date number
a1_as_datetime = datetime(
*xlrd.xldate_as_tuple(sheet.cell(row_no, 4).value, workbook.datemode))
date_string = a1_as_datetime.strftime('%Y-%m-%d')
else:
# String date (manual input)
date_string = datetime.strptime(line[4], '%Y-%m-%d').strftime('%Y-%m-%d')
except Exception:
raise ValidationError(_('Wrong Date Format. Date Should be in format YYYY-MM-DD.'))
values = {
'name': line[0],
'project_id': line[1],
'user_ids': line[2],
'tag_ids': line[3],
'date_deadline': date_string,
'description': line[5] if len(line) > 5 else '',
}
self.create_task(values)
return True
else:
raise ValidationError(_("Invalid import option selected."))
@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
from odoo import SUPERUSER_ID
from odoo import api, fields, models, _
from datetime import datetime, timedelta ,date
import calendar
class ResUsers(models.Model):
_inherit = "res.users"
assign_update_ids = fields.One2many('task.update','assign_task_id')
created_task_ids = fields.One2many('task.update','create_task_id')
def task_update_email(self):
superuser_id = self.env['res.partner'].browse(SUPERUSER_ID)
user_ids = self.env['res.users'].search([('active','=',True),('share','=',False)])
for user in user_ids:
create_task_ids = self.env['task.update']
task_list_ids = self.env['task.update']
task_ids = self.env['project.task'].search(['|',('user_ids','=',user.id),('create_uid','=',user.id)])
if task_ids:
for task in task_ids.filtered(lambda x : x.user_ids == user):
today = datetime.now().date()
overdue_days = ''
if task.date_deadline:
days=today -task.date_deadline.date()
overdue_days=days.days
task_list_ids += self.env['task.update'].create({'name':task.name,
'date_deadline':task.date_deadline,
'stage_id':task.stage_id.id,
'dueday':overdue_days,
'assign_task_id' : user.id
})
for task in task_ids.filtered(lambda x : x.create_uid == user):
today = datetime.now().date()
overdue_days = ''
if task.date_deadline:
days=today -task.date_deadline.date()
overdue_days=days.days
create_task_ids += self.env['task.update'].create({'name':task.name,
'date_deadline':task.date_deadline,
'stage_id':task.stage_id.id,
'dueday':overdue_days,
'create_task_id' : user.id
})
user.sudo().write({
'created_task_ids' : [(6,0,create_task_ids.ids)],
'assign_update_ids' : [(6,0,task_list_ids.ids)]
})
template_id = self.env['ir.model.data']._xmlid_lookup(
'bi_all_in_one_project_management_system.email_template_task_update')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template([user.id],('subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'scheduled_date'))[user.id]
values['email_to'] = user.partner_id.email
values['res_id'] = False
values['author_id'] = user.partner_id.id
mail_mail_obj = self.env['mail.mail']
mail_create_id = mail_mail_obj.sudo().create(values)
if mail_create_id:
mail_create_id.sudo().send()
return True
def weekly_task_update_email(self):
superuser_id = self.env['res.partner'].browse(SUPERUSER_ID)
user_ids = self.env['res.users'].search([('active', '=', True), ('share', '=', False)])
for user in user_ids:
create_task_ids = self.env['task.update']
task_list_ids = self.env['task.update']
task_ids = self.env['project.task'].search(['|', ('user_ids', '=', user.id), ('create_uid', '=', user.id)])
if task_ids:
today = datetime.now().date() # Get today's date
for task in task_ids.filtered(lambda x: x.user_ids == user):
overdue_days = ''
if task.date_deadline:
days = today - task.date_deadline.date() # Ensure date comparison
overdue_days = days.days
task_list_ids += self.env['task.update'].create({
'name': task.name,
'date_deadline': task.date_deadline,
'stage_id': task.stage_id.id,
'dueday': overdue_days,
'assign_task_id': user.id
})
for task in task_ids.filtered(lambda x: x.create_uid == user):
overdue_days = ''
if task.date_deadline:
days = today - task.date_deadline.date() # Ensure date comparison
overdue_days = days.days
create_task_ids += self.env['task.update'].create({
'name': task.name,
'date_deadline': task.date_deadline,
'stage_id': task.stage_id.id,
'dueday': overdue_days,
'create_task_id': user.id
})
user.sudo().write({
'created_task_ids': [(6, 0, create_task_ids.ids)],
'assign_update_ids': [(6, 0, task_list_ids.ids)]
})
template_id = self.env['ir.model.data']._xmlid_lookup(
'bi_all_in_one_project_management_system.email_template_task_update')[1]
email_template_obj = self.env['mail.template'].browse(template_id)
if template_id:
values = email_template_obj._generate_template(
[user.id], ('subject', 'body_html', 'email_from', 'email_to',
'partner_to', 'email_cc', 'reply_to', 'scheduled_date')
)[user.id]
values['email_to'] = user.partner_id.email
values['res_id'] = False
values['author_id'] = user.partner_id.id
mail_mail_obj = self.env['mail.mail']
mail_create_id = mail_mail_obj.sudo().create(values)
if mail_create_id:
mail_create_id.sudo().send()
return True
class Task_updates(models.Model):
_name='task.update'
_description="Task Updates"
name=fields.Char(string='name')
date_deadline=fields.Date(string='Date Deadline')
stage_id = fields.Many2one('project.task.type', string='Stage')
dueday=fields.Char(string='Overdue')
assign_task_id=fields.Many2one('res.users')
create_task_id=fields.Many2one('res.users')
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="project_details">
<t t-call="web.external_layout">
<div class="page">
<br/>
<t t-foreach="docs" t-as="o">
<center>
<h2><span t-field="o.name" /></h2>
</center>
<table class="table table-bordered">
<t t-foreach="o.user_id" t-as="user">
<tr>
<th>Project Manager :</th>
<td>
<span t-field="user.name" />
</td>
</tr>
</t>
<tr>
<th>Customer :</th>
<td>
<span t-field="o.partner_id.name" />
</td>
</tr>
</table>
<h2 style="text-align : center;font-style:normal; background-color:grey" >
Task
</h2><br/>
<table style="border-collapse: collapse;width: 100%;border: 3px solid #000;" class="table table-condensed">
<tr >
<td><strong >Task Name</strong> </td>
<td><strong >Allocated Hours</strong></td>
<td><strong >Spent Hours</strong></td>
<td><strong >Remaining Hours</strong></td>
<td><strong >Deadline</strong></td>
<td><strong >Assign To</strong></td>
<td><strong >Assign date</strong></td>
<td><strong >Stages</strong></td>
</tr>
<t t-foreach="o.task_ids" t-as="p">
<tr>
<td><span t-field="p.name" /></td>
<td><span t-field="p.allocated_hours" /></td>
<td><span t-field="p.effective_hours" /></td>
<td><span t-field="p.remaining_hours" /></td>
<td><span t-field="p.date_deadline" /></td>
<td><t t-foreach="o.user_id" t-as="user"><span t-field="user.name" /></t></td>
<td><span t-field="p.date_assign" /></td>
<td><span t-field="p.stage_id" /></td>
</tr>
</t>
</table>
</t>
</div>
</t>
</template>
<template id="custom_project_details_report">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="bi_all_in_one_project_management_system.project_details" />
</t>
</t>
</template>
</odoo>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_task_details" model="ir.actions.report">
<field name="name">Task Details</field>
<field name="model">project.task</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">bi_all_in_one_project_management_system.custom_task_details_report</field>
<field name="report_file">bi_all_in_one_project_management_system.custom_task_details_report</field>
<field name="binding_model_id" ref="project.model_project_task"/>
<field name="binding_type">report</field>
</record>
<record id="action_project_details" model="ir.actions.report">
<field name="name">Project Details</field>
<field name="model">project.project</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">bi_all_in_one_project_management_system.custom_project_details_report</field>
<field name="report_file">bi_all_in_one_project_management_system.custom_project_details_report</field>
<field name="binding_model_id" ref="project.model_project_project"/>
<field name="binding_type">report</field>
</record>
</odoo>
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="task_details">
<t t-call="web.external_layout">
<div class="page">
<br/>
<t t-foreach="docs" t-as="o">
<center>
<h2><span t-field="o.name" /></h2>
</center>
<table style="border-collapse: collapse;width: 100%;border: 1px solid #000;" class="table table-condensed">
<tr style="padding: 8px;text-align: left;border: 3px solid #000;height:50%;">
<td><strong >Project: </strong> </td>
<td><span t-field="o.project_id.name"/></td>
<td></td>
<td><strong >Assigned to: </strong> </td>
<td><t t-foreach="o.user_ids" t-as="user"><span t-field="user.name"/><span>,</span></t></td>
</tr>
<tr style="padding: 8px;text-align: left;border: 3px solid #000;height:0%;">
<td><strong >Partner: </strong> </td>
<td><span t-field="o.partner_id" /></td>
<td></td>
<td><strong >Assigning Date: </strong> </td>
<td><span t-field="o.date_assign" /></td>
</tr>
<tr style="padding: 8px;text-align: left;border: 3px solid #000;height:50%;">
<td><strong >Partner email: </strong> </td>
<td><span t-field="o.partner_id.email" /></td>
<td></td>
<td><strong >Deadline: </strong> </td>
<td><span t-field="o.date_deadline" /></td>
</tr>
</table>
<h2 style="text-align : center;font-style:normal;">
Timesheet
</h2><br/>
<table style="border-collapse: collapse;width: 100%;border: 3px solid #000;" class="table table-condensed">
<tr style="padding: 8px;text-align: left;border: 3px solid #000;height:50%;">
<td><strong >Allocated Hours : </strong> </td>
<td><span t-field="o.allocated_hours"/></td>
<td><strong >Progress: </strong> </td>
<td><span t-field="o.progress" /></td>
</tr>
<tr >
<td><strong >Date</strong> </td>
<td><strong >Employee</strong> </td>
<td><strong >Description</strong> </td>
<td><strong >Duration (Hours)</strong> </td>
</tr>
<t t-foreach="o.timesheet_ids" t-as="a">
<tr >
<td><span t-field="a.date" /></td>
<td><span t-field="a.employee_id" /></td>
<td><span t-field="a.name" /></td>
<td><span t-field="a.unit_amount" /></td>
</tr>
</t>
</table>
<div class="text-right">
<tr>
<th><strong>Hours Spent :</strong></th>
<td>
<span t-field="o.effective_hours" />
</td>
</tr>
</div>
<div class="text-right">
<th><strong>Remaining Hours :</strong></th>
<td>
<span t-field="o.remaining_hours" />
</td>
</div>
</t>
</div>
</t>
</template>
<template id="custom_task_details_report">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="bi_all_in_one_project_management_system.task_details" />
</t>
</t>
</template>
</odoo>
@@ -0,0 +1,18 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sub_project,sub.project,bi_all_in_one_project_management_system.model_sub_project,,1,1,1,1
access_task_update,access_task_update,bi_all_in_one_project_management_system.model_task_update,,1,1,1,1
access_mass_update_task_wiz,mass.update.task.wiz,bi_all_in_one_project_management_system.model_mass_update_task_wiz,,1,1,1,1
access_project_task,subtask.wizard,model_subtask_wizard,project.group_project_user,1,1,1,1
access_task_on_partner,subtask.wizard on partners,model_subtask_wizard,base.group_user,1,0,0,0
access_subtask_wizard,subtask.wizard,model_subtask_wizard,,1,1,1,1
access_meeting_date,access.meeting.date,model_meeting_date,base.group_user,1,1,1,1
access_auto_assign,access_auto_assign_task,model_task_auto_assign,,1,1,1,1
access_sale_task_create,access_sale_task_create,model_sale_task_create,,1,1,1,1
import_task,import_task,model_import_task,,1,1,1,1
access_project_task_timer_wizard,access_project_task_timer_wizard,model_project_task_timer_wizard,,1,1,1,1
access_project_checklist,access_project_checklist,model_project_checklist,base.group_user,1,1,1,1,
access_project_checklist_template,access_project_checklist_template,model_project_checklist_template,base.group_user,1,1,1,1,
access_project_checklist_line,access_project_checklist_line,model_project_checklist_line,base.group_user,1,1,1,1
access_mass_update_project_stage,mass.update.project.stage,bi_all_in_one_project_management_system.model_mass_update_project_stage,,1,1,1,1
access_project_task_user_time_user,access_project_task_user_time_user,model_project_task_user_time,base.group_user,1,1,1,1
access_project_task_user_time_manager,access_project_task_user_time_manager,model_project_task_user_time,project.group_project_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sub_project sub.project bi_all_in_one_project_management_system.model_sub_project 1 1 1 1
3 access_task_update access_task_update bi_all_in_one_project_management_system.model_task_update 1 1 1 1
4 access_mass_update_task_wiz mass.update.task.wiz bi_all_in_one_project_management_system.model_mass_update_task_wiz 1 1 1 1
5 access_project_task subtask.wizard model_subtask_wizard project.group_project_user 1 1 1 1
6 access_task_on_partner subtask.wizard on partners model_subtask_wizard base.group_user 1 0 0 0
7 access_subtask_wizard subtask.wizard model_subtask_wizard 1 1 1 1
8 access_meeting_date access.meeting.date model_meeting_date base.group_user 1 1 1 1
9 access_auto_assign access_auto_assign_task model_task_auto_assign 1 1 1 1
10 access_sale_task_create access_sale_task_create model_sale_task_create 1 1 1 1
11 import_task import_task model_import_task 1 1 1 1
12 access_project_task_timer_wizard access_project_task_timer_wizard model_project_task_timer_wizard 1 1 1 1
13 access_project_checklist access_project_checklist model_project_checklist base.group_user 1 1 1 1
14 access_project_checklist_template access_project_checklist_template model_project_checklist_template base.group_user 1 1 1 1
15 access_project_checklist_line access_project_checklist_line model_project_checklist_line base.group_user 1 1 1 1
16 access_mass_update_project_stage mass.update.project.stage bi_all_in_one_project_management_system.model_mass_update_project_stage 1 1 1 1
17 access_project_task_user_time_user access_project_task_user_time_user model_project_task_user_time base.group_user 1 1 1 1
18 access_project_task_user_time_manager access_project_task_user_time_manager model_project_task_user_time project.group_project_manager 1 1 1 1
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="project_task_user_rule" model="ir.rule">
<field name="name">Project User Rule</field>
<field name="model_id" ref="model_project_task"/>
<field name="global" eval="True"/>
<field name="domain_force">[('user_ids','in',user.id)]</field>
<field name="groups" eval="[(4,ref('project.group_project_manager'))]"/>
</record>
</data>
</odoo>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<function name="write" model="ir.model.data">
<function name="search" model="ir.model.data">
<value eval="[('module', '=', 'project')]"/>
</function>
<value eval="{'noupdate': False}"/>
</function>
<record id="all_project_user" model="res.groups">
<field name="name">Project Co - Ordinator</field>
<field name="implied_ids" eval="[(4, ref('project.group_project_user'))]"/>
<field name="privilege_id" ref="project.res_groups_privilege_project"/>
</record>
<record id="project.group_project_manager" model="res.groups">
<field name="name">Administrator</field>
<field name="privilege_id" ref="project.res_groups_privilege_project"/>
<field name="implied_ids" eval="[(4, ref('bi_all_in_one_project_management_system.all_project_user'))]"/>
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record>
<record model="ir.rule" id="project_project_project_co_ordinator_rule">
<field name="name">Project: project co ordinator: Own</field>
<field name="model_id" ref="project.model_project_project"/>
<field name="domain_force">['|','|',('user_id','=',user.id),('user_id','=', False),('task_ids.user_ids','in',user.id)]</field>
<field name="groups" eval="[(4,ref('bi_all_in_one_project_management_system.all_project_user'))]"/>
</record>
<record id="project_task_type_read_access" model="ir.rule">
<field name="name">Task Stage: read access</field>
<field name="model_id" ref="project.model_project_task_type"/>
<field name="domain_force">['|',('user_id','=',user.id),('user_id','=', False)]</field>
<field name="groups" eval="[(4, ref('bi_all_in_one_project_management_system.all_project_user'))]"/>
</record>
</data>
</odoo>
@@ -0,0 +1 @@
,ds,krutik,14.10.2025 16:08,file:///home/ds/.config/libreoffice/4;
@@ -0,0 +1 @@
,herita,herita-HP-ProDesk-600-G1-SFF,02.06.2022 00:48,file:///home/herita/.config/libreoffice/4;
@@ -0,0 +1,4 @@
Task Name,Project Id,User,Tag,Deadline Date,Description
Data Flow System,Office Design,Mitchell Admin,Experiment,2017-02-03,System Data Flow
Budget Planning,Research & Development,Mitchell Admin,Experiment,2017-05-12,Planning For Budget Planning
User Improvement,Office Design,Mitchell Admin,Bug,2000-11-11,User Improvement
1 Task Name Project Id User Tag Deadline Date Description
2 Data Flow System Office Design Mitchell Admin Experiment 2017-02-03 System Data Flow
3 Budget Planning Research & Development Mitchell Admin Experiment 2017-05-12 Planning For Budget Planning
4 User Improvement Office Design Mitchell Admin Bug 2000-11-11 User Improvement
Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 108 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Some files were not shown because too many files have changed in this diff Show More