first push message
@@ -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}}"<{{object.company_id.email}}></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}}"<{{object.company_id.email}}></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
|
||||
|
@@ -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
|
||||
|
|
After Width: | Height: | Size: 349 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 902 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 10 MiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 486 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 247 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 251 KiB |