Kanchan Chauhan
9 years ago
23 changed files with 1089 additions and 4 deletions
@ -0,0 +1,29 @@ |
|||
{% if doc.status=="Open" %} |
|||
<div class="web-list-item"> |
|||
<a class="no-decoration" href="/projects?project={{ doc.name }}"> |
|||
<div class="row"> |
|||
<div class="col-xs-4"> |
|||
|
|||
{{ doc.name }} |
|||
</div> |
|||
<div class="col-xs-4"> |
|||
{% if doc.percent_complete %} |
|||
<div class="progress" style="margin-bottom: 0!important;"> |
|||
<div class="progress-bar progress-bar-warning" role="progressbar" |
|||
aria-valuenow="{{ doc.percent_complete|round|int }}" |
|||
aria-valuemin="0" aria-valuemax="100" style="width:{{ doc.percent_complete|round|int }}%;"> |
|||
{{ doc.percent_complete|round|int }}% Complete |
|||
</div> |
|||
</div> |
|||
{% else %} |
|||
<span class="indicator {{ "red" if doc.status=="Open" else "darkgrey" }}"> |
|||
{{ doc.status }}</span> |
|||
{% endif %} |
|||
</div> |
|||
<div class="col-xs-4 text-right small text-muted"> |
|||
{{ frappe.utils.pretty_date(doc.modified) }} |
|||
</div> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
{% endif %} |
@ -0,0 +1,20 @@ |
|||
<div class="project-search"> |
|||
<input type="text" id="project-search" |
|||
placeholder="Search..."><span style="position:relative; |
|||
left:-19px;"> <a href="/projects?project={{doc.name}}" class="octicon octicon-x text-extra-muted" title="Clear" ></a> </span> |
|||
</div> |
|||
|
|||
<script> |
|||
frappe.ready(function() { |
|||
var thread = null; |
|||
function findResult(t) { |
|||
window.location.href="/projects?project={{doc.name}}&q=" + t; |
|||
} |
|||
|
|||
$("#project-search").keyup(function() { |
|||
clearTimeout(thread); |
|||
var $this = $(this); thread = setTimeout(function(){findResult($this.val())}, 1000); |
|||
}); |
|||
$(".form-search").on("submit", function() { return false; }); |
|||
}); |
|||
</script> |
@ -0,0 +1,132 @@ |
|||
/* CSS used here will be applied after bootstrap.css */ |
|||
|
|||
.underline { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
.page-container .indicator { |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.page-container .project-item { |
|||
padding-top: 5px; |
|||
padding-bottom: 5px; |
|||
} |
|||
|
|||
.row-header { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
padding-bottom: 8px; |
|||
border-bottom: 2px solid #d1d8dd; |
|||
margin: 0!important; |
|||
} |
|||
|
|||
.content_display{ |
|||
padding: 5px; |
|||
} |
|||
|
|||
.task-btn, .issue-btn, .timelog-btn{ |
|||
padding: 8px; |
|||
} |
|||
|
|||
.timeline-centered { |
|||
position: relative; |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.timeline-centered:before { |
|||
content: ''; |
|||
position: absolute; |
|||
display: block; |
|||
width: 4px; |
|||
background: #f5f5f6; |
|||
/*left: 50%;*/ |
|||
top: 20px; |
|||
bottom: 20px; |
|||
margin-left: 30px; |
|||
} |
|||
|
|||
.timeline-centered .timeline-entry { |
|||
position: relative; |
|||
/*width: 50%; |
|||
float: right;*/ |
|||
margin-top: 5px; |
|||
margin-left: 30px; |
|||
margin-bottom: 10px; |
|||
clear: both; |
|||
} |
|||
|
|||
|
|||
|
|||
.timeline-centered .timeline-entry.left-aligned .timeline-entry-inner .timeline-label:after { |
|||
left: auto; |
|||
right: 0; |
|||
margin-left: 0; |
|||
margin-right: -9px; |
|||
-moz-transform: rotate(180deg); |
|||
-o-transform: rotate(180deg); |
|||
-webkit-transform: rotate(180deg); |
|||
-ms-transform: rotate(180deg); |
|||
transform: rotate(180deg); |
|||
} |
|||
|
|||
.timeline-centered .timeline-entry .timeline-entry-inner { |
|||
position: relative; |
|||
margin-left: -20px; |
|||
} |
|||
|
|||
|
|||
.timeline-centered .timeline-entry .timeline-entry-inner .timeline-icon { |
|||
background: #fff; |
|||
color: #737881; |
|||
display: block; |
|||
width: 40px; |
|||
height: 40px; |
|||
-webkit-background-clip: padding-box; |
|||
-moz-background-clip: padding; |
|||
background-clip: padding-box; |
|||
-webkit-border-radius: 20px; |
|||
-moz-border-radius: 20px; |
|||
border-radius: 20px; |
|||
text-align: center; |
|||
-moz-box-shadow: 0 0 0 5px #f5f5f6; |
|||
-webkit-box-shadow: 0 0 0 5px #f5f5f6; |
|||
box-shadow: 0 0 0 5px #f5f5f6; |
|||
line-height: 40px; |
|||
font-size: 15px; |
|||
float: left; |
|||
} |
|||
|
|||
|
|||
.timeline-centered .timeline-entry .timeline-entry-inner .timeline-icon.bg-warning { |
|||
background-color: #fad839; |
|||
color: #fff; |
|||
} |
|||
|
|||
|
|||
.timeline-centered .timeline-entry .timeline-entry-inner .timeline-label { |
|||
position: relative; |
|||
background: #f5f5f6; |
|||
padding: 1em; |
|||
margin-left: 60px; |
|||
-webkit-background-clip: padding-box; |
|||
-moz-background-clip: padding; |
|||
background-clip: padding-box; |
|||
-webkit-border-radius: 3px; |
|||
-moz-border-radius: 3px; |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.timeline-centered .timeline-entry .timeline-entry-inner .timeline-label:after { |
|||
content: ''; |
|||
display: block; |
|||
position: absolute; |
|||
width: 0; |
|||
height: 0; |
|||
border-style: solid; |
|||
border-width: 9px 9px 9px 0; |
|||
border-color: transparent #f5f5f6 transparent transparent; |
|||
left: 0; |
|||
top: 10px; |
|||
margin-left: -9px; |
|||
} |
@ -0,0 +1,31 @@ |
|||
{% for issue in doc.issues %} |
|||
<div class='issue'> |
|||
<div class='row project-item'> |
|||
<div class='col-xs-9'> |
|||
<a class="no-decoration" href="/issues-view?name={{ issue.name}}"> |
|||
<span class="indicator {{ "green" if issue.status=="Open" else "darkgrey" }}"> |
|||
{% if issue.status == "Closed" %} |
|||
{{ issue.subject }} resolved {{ frappe.utils.pretty_date(issue.resolution_date) }} |
|||
{% else %} |
|||
{{ issue.subject }} raised on {{ issue.opening_date }} |
|||
{% endif %} |
|||
</span> |
|||
</a> |
|||
</div> |
|||
<div class='col-xs-3'> |
|||
<div class='pull-right'> |
|||
{% if issue.todo %} |
|||
<span class="avatar avatar-small" title="{{ issue.todo.owner }}"> <img src="{{ issue.todo.user_image }}"></span> |
|||
{% else %} |
|||
<span class="avatar avatar-small avatar-empty"></span> |
|||
{% endif %} |
|||
</div> |
|||
<div class='pull-right' style='padding-right:10px;'> |
|||
{% if issue.status != "Closed" %} |
|||
<span class="text-extra-muted "> <i class="octicon octicon-x issue-x" title="Close" id = "{{issue.name}}"></i> </span> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
@ -0,0 +1,30 @@ |
|||
{% for task in doc.tasks %} |
|||
<div class='task'> |
|||
<div class='row project-item'> |
|||
<div class='col-xs-9'> |
|||
<a class="no-decoration" href="/tasks-view?name={{ task.name}}"> |
|||
<span class="indicator {{ "green" if task.status=="Open" else "darkgrey" }}"> |
|||
{% if task.status == "Closed" %} |
|||
{{ task.subject }} completed on {{ task.closing_date }} |
|||
{% else %} |
|||
{{ task.subject }} |
|||
{% endif %} |
|||
</span> |
|||
</a> |
|||
</div> |
|||
<div class='col-xs-3'> |
|||
<div class='pull-right'> |
|||
{% if task.todo %} <span class="avatar avatar-small" title="{{ task.todo.owner }}"> <img src="{{ task.todo.user_image }}"></span> |
|||
{% else %} |
|||
<span class="avatar avatar-small avatar-empty"></span> |
|||
{% endif %} |
|||
</div> |
|||
<div class='pull-right' style='padding-right:10px;'> |
|||
{% if task.status != "Closed" %} |
|||
<span class="text-extra-muted "> <i class="octicon octicon-x task-x" title="Close" id = "{{task.name}}"></i> </span> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
@ -0,0 +1,20 @@ |
|||
{% for timelog in doc.timelogs %} |
|||
<div class='timelog'> |
|||
<div class='row project-item'> |
|||
<div class='col-xs-9'> |
|||
<a class="no-decoration" href="/timelog_info?timelog={{ timelog.name}}"> |
|||
<span class="indicator {{ "green" if timelog.status=="Draft" else "blue" if timelog.status=="Submitted" else "darkgrey"}}"> |
|||
|
|||
{{ timelog.title }}: From {{ frappe.format_date(timelog.from_time) }} to {{ frappe.format_date(timelog.to_time) }} |
|||
|
|||
</span> |
|||
</a> |
|||
</div> |
|||
<div class='col-xs-3'> |
|||
<div class='pull-right'> |
|||
<span class="avatar avatar-small" title="{{ timelog.modified_by }}"> <img src="{{ timelog.user_image }}"></span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
@ -0,0 +1,44 @@ |
|||
{% for timeline in doc.timelines %} |
|||
<div class='timeline'> |
|||
<div class="row project-item"> |
|||
<div class="col-xs-12"> |
|||
<span class="avatar avatar-small" title="{{ timeline.modified_by }}"> <img src="{{ timeline.user_image }}"></span> |
|||
<span class='indicator'> |
|||
{{timeline.reference_name}} {{timeline.subject }} |
|||
</span> |
|||
<span class='indicator pull-right'> |
|||
{{ frappe.utils.pretty_date(timeline.creation) }} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
|||
<!-- <div class="timeline-centered"> |
|||
{% for timeline in doc.timelines %} |
|||
|
|||
|
|||
|
|||
<article class="timeline-entry"> |
|||
|
|||
<div class="timeline-entry-inner"> |
|||
|
|||
<div class="timeline-icon bg-warning"> |
|||
<i class="entypo-camera"></i> |
|||
</div> |
|||
|
|||
<div class="timeline-label"> |
|||
<span class="avatar avatar-small" title="{{ timeline.modified_by }}"> <img src="{{ timeline.user_image }}"></span> |
|||
<span class='indicator'> |
|||
{{timeline.reference_name}} {{timeline.subject }} |
|||
</span> |
|||
<span class='indicator pull-right'> |
|||
{{ frappe.utils.pretty_date(timeline.creation) }} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
|
|||
</article> |
|||
|
|||
|
|||
{% endfor %} |
|||
</div> --> |
@ -0,0 +1,2 @@ |
|||
|
|||
<div>{{doc.terms}}</div> |
@ -0,0 +1,98 @@ |
|||
{% extends "templates/web.html" %} |
|||
|
|||
{% block title %}{{ doc.project_name }}{% endblock %} |
|||
|
|||
{% block header %} |
|||
<h1 class= "title"> |
|||
{{ doc.project_name }} |
|||
</h1> |
|||
{% endblock %} |
|||
|
|||
{% block style %} |
|||
<style> |
|||
{% include "templates/includes/projects.css" %} |
|||
</style> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block page_content %} |
|||
|
|||
{% include 'templates/includes/project_search_box.html' %} |
|||
|
|||
{% if frappe.form_dict.q %} |
|||
<p class="text-muted"> <a href="/projects?project={{doc.name}}" class="text-muted"> |
|||
Filtered by "{{ frappe.form_dict.q }}" Clear</a></p> |
|||
{% else %} |
|||
<h3>{{ _("Activity Feed") }}</h3> |
|||
<div class='project-timeline'> |
|||
{% include "erpnext/templates/includes/projects/timeline.html" %} |
|||
</div> |
|||
{% if doc.timelines|length > 9 %} |
|||
<p><a class='more-timelines small underline'>{{ _("More") }}</a><p> |
|||
{% endif %} |
|||
{% endif %} |
|||
|
|||
{% if doc.tasks %} |
|||
<div class='project-tasks-section'> |
|||
<div> |
|||
<a class="btn btn-xs btn-primary pull-right" |
|||
href='/tasks?new=1&project={{ doc.project_name }}'>New</a> |
|||
<h3>{{ _("Tasks") }}</h3> |
|||
</div> |
|||
<div class="btn-group btn-toggle"> |
|||
<button class="btn btn-xs btn-open-tasks btn-primary active" style="float:left;">Open</button> |
|||
<button class="btn btn-xs btn-closed-tasks">Close</button> |
|||
</div> |
|||
<div class='project-tasks'> |
|||
{% include "erpnext/templates/includes/projects/project_tasks.html" %} |
|||
</div> |
|||
|
|||
{% if doc.tasks|length > 4 %} |
|||
<p><a id= 'more-tasks' class='more-tasks small underline'>{{ _("More") }}</a><p> |
|||
{% endif %} |
|||
</div> |
|||
{% else %} |
|||
<p class="text-muted">No tasks</p> |
|||
{% endif %} |
|||
|
|||
{% if doc.issues %} |
|||
<div class='project-issues-section'> |
|||
<div> |
|||
<a class="btn btn-xs btn-primary pull-right" |
|||
href='/issues?new=1&project={{ doc.project_name }}'>New</a> |
|||
<h3>{{ _("Issues") }}</h3> |
|||
</div> |
|||
<div class="btn-group btn-toggle"> |
|||
<button class="btn btn-xs btn-open-issues" style="float:left;">Open</button> |
|||
<button class="btn btn-xs btn-closed-issues">Close</button> |
|||
</div> |
|||
<div class='project-issues'> |
|||
{% include "erpnext/templates/includes/projects/project_issues.html" %} |
|||
</div> |
|||
|
|||
{% if doc.issues|length > 4 %} |
|||
<p><a id='more-issues' class='more-issues small underline'>{{ _("More") }}</a><p> |
|||
{% endif %} |
|||
</div> |
|||
{% else %} |
|||
<p class="text-muted">No Issues</p> |
|||
{% endif %} |
|||
|
|||
{% if doc.timelogs %} |
|||
<h3>{{ _("Time Logs") }}</h3> |
|||
<div class='project-timelogs'> |
|||
{% include "erpnext/templates/includes/projects/project_timelogs.html" %} |
|||
</div> |
|||
{% if doc.timelogs|length > 1 %} |
|||
<p><a class='more-timelogs small underline'>{{ _("More") }}</a><p> |
|||
{% endif %} |
|||
{% else %} |
|||
<p class="text-muted">No time logs</p> |
|||
{% endif %} |
|||
</div> |
|||
|
|||
<script> |
|||
{% include "erpnext/templates/pages/projects.js" %} |
|||
</script> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,204 @@ |
|||
frappe.ready(function() { |
|||
var reload_tasks = function(taskstatus) { |
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_tasks_html", |
|||
project: '{{ doc.name }}', |
|||
taskstatus: taskstatus, |
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$('.project-tasks').html(data.message); |
|||
|
|||
$('.project-tasks-section .btn-group .btn-primary').removeClass('btn-primary'); |
|||
$('.btn-'+ taskstatus +'-tasks').addClass( "btn-primary" ); |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
$('.btn-closed-tasks').click(function() { |
|||
reload_tasks('closed'); |
|||
}); |
|||
$('.btn-open-tasks').click(function() { |
|||
reload_tasks('open'); |
|||
}); |
|||
|
|||
var reload_issues = function(issuestatus) { |
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_issues_html", |
|||
project: '{{ doc.name }}', |
|||
issuestatus: issuestatus, |
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$('.project-issues').html(data.message); |
|||
|
|||
$('.project-issues-section .btn-group .btn-primary').removeClass('btn-primary'); |
|||
$('.btn-'+ issuestatus +'-issues').addClass( "btn-primary" ); |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
$('.btn-closed-issues').click(function() { |
|||
reload_issues('closed'); |
|||
}); |
|||
$('.btn-open-issues').click(function() { |
|||
reload_issues('open'); |
|||
}); |
|||
|
|||
var taskstart = 5; |
|||
$(".more-tasks").click(function() { |
|||
var task_status = $('.project-tasks-section .btn-group .btn-primary').hasClass('btn-closed-tasks') |
|||
? 'closed' : 'open'; |
|||
|
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_tasks_html", |
|||
project: '{{ doc.name }}', |
|||
start: taskstart, |
|||
taskstatus: task_status, |
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$(data.message).appendTo('.project-tasks'); |
|||
if(typeof data.message == 'undefined') { |
|||
$(".more-tasks").toggle(false); |
|||
} |
|||
taskstart = taskstart+5; |
|||
} |
|||
}); |
|||
|
|||
}); |
|||
|
|||
var issuestart = 2; |
|||
$(".more-issues").click(function() { |
|||
var issue_status = $('.project-issues-section .btn-group .btn-primary').hasClass('btn-closed-issues') |
|||
? 'closed' : 'open'; |
|||
|
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_issues_html", |
|||
project: '{{ doc.name }}', |
|||
start: issuestart, |
|||
issuestatus: issue_status, |
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$(data.message).appendTo('.project-issues'); |
|||
if(typeof data.message == 'undefined') |
|||
{ |
|||
$(".more-issues").toggle(false); |
|||
} |
|||
issuestart = issuestart+5; |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
var timelogstart = 2; |
|||
$(".more-timelogs").click(function() { |
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_timelogs_html", |
|||
project: '{{ doc.name }}', |
|||
start: timelogstart, |
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$(data.message).appendTo('.project-timelogs'); |
|||
if(typeof data.message == 'undefined') |
|||
{ |
|||
$(".more-timelogs").toggle(false); |
|||
} |
|||
timelogstart = timelogstart+2; |
|||
|
|||
} |
|||
}); |
|||
|
|||
}); |
|||
|
|||
var timelinestart = 10; |
|||
$(".more-timelines").click(function() { |
|||
$.ajax({ |
|||
method: "GET", |
|||
url: "/", |
|||
dataType: "json", |
|||
data: { |
|||
cmd: "erpnext.templates.pages.projects.get_timeline_html", |
|||
project: '{{ doc.name }}', |
|||
start: timelinestart, |
|||
|
|||
}, |
|||
dataType: "json", |
|||
success: function(data) { |
|||
$(data.message).appendTo('.project-timeline'); |
|||
if(typeof data.message == 'undefined') |
|||
{ |
|||
$(".more-timelines").toggle(false); |
|||
} |
|||
timelinestart = timelinestart+10; |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
$( ".project-tasks" ).on('click', '.task-x', function() { |
|||
var args = { |
|||
project: '{{ doc.name }}', |
|||
task_name: $(this).attr('id'), |
|||
} |
|||
frappe.call({ |
|||
btn: this, |
|||
type: "POST", |
|||
method: "erpnext.templates.pages.projects.set_task_status", |
|||
args: args, |
|||
callback: function(r) { |
|||
if(r.exc) { |
|||
if(r._server_messages) |
|||
frappe.msgprint(r._server_messages); |
|||
} else { |
|||
$(this).remove(); |
|||
} |
|||
} |
|||
}) |
|||
return false; |
|||
}); |
|||
|
|||
$( ".project-issues" ).on('click', '.issue-x', function() { |
|||
var args = { |
|||
project: '{{ doc.name }}', |
|||
issue_name: $(this).attr('id'), |
|||
} |
|||
frappe.call({ |
|||
btn: this, |
|||
type: "POST", |
|||
method: "erpnext.templates.pages.projects.set_issue_status", |
|||
args: args, |
|||
callback: function(r) { |
|||
if(r.exc) { |
|||
if(r._server_messages) |
|||
frappe.msgprint(r._server_messages); |
|||
} else { |
|||
$(this).remove(); |
|||
} |
|||
} |
|||
}) |
|||
return false; |
|||
}); |
|||
}); |
@ -0,0 +1,140 @@ |
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors |
|||
# License: GNU General Public License v3. See license.txt |
|||
|
|||
from __future__ import unicode_literals |
|||
import frappe |
|||
|
|||
from frappe import _ |
|||
|
|||
def get_context(context): |
|||
context.no_cache = 1 |
|||
|
|||
project = frappe.get_doc('Project', frappe.form_dict.project) |
|||
|
|||
project.has_permission('read') |
|||
|
|||
context.issues = frappe.get_all('Issue', filters={'project': project.project_name}, |
|||
fields=['subject', 'opening_date', 'resolution_date', 'status', 'name', 'resolution_details','modified','modified_by']) |
|||
|
|||
project.tasks = get_tasks(project.name, start=0, search=frappe.form_dict.get("q")) |
|||
|
|||
project.timelogs = get_timelogs(project.name, start=0, search=frappe.form_dict.get("q")) |
|||
|
|||
project.issues = get_issues(project.name, start=0, search=frappe.form_dict.get("q")) |
|||
|
|||
project.timelines = get_timeline(project.project_name, start=0) |
|||
|
|||
|
|||
|
|||
context.doc = project |
|||
|
|||
|
|||
def get_timeline(project, start=10): |
|||
issue_names = '({0})'.format(", ".join(["'{0}'".format(i.name) for i in get_issues(project)])) |
|||
|
|||
print issue_names |
|||
|
|||
timelines = frappe.db.sql("""select sender_full_name, |
|||
subject, communication_date, comment_type, name, creation, modified_by, reference_doctype, reference_name, |
|||
_liked_by, comment_type, _comments |
|||
from tabCommunication |
|||
where (reference_doctype='Project' and reference_name=%s) |
|||
or (timeline_doctype='Project' and timeline_name=%s) |
|||
or (reference_doctype='Issue' and reference_name IN {issue_names}) |
|||
order by modified DESC limit {start}, {limit}""".format( |
|||
issue_names=issue_names, start=start, limit=10), |
|||
(project, project), as_dict=True); |
|||
for timeline in timelines: |
|||
timeline.user_image = frappe.db.get_value('User', timeline.modified_by, 'user_image') |
|||
return timelines |
|||
|
|||
@frappe.whitelist() |
|||
def get_timeline_html(project, start=0): |
|||
return frappe.render_template("erpnext/templates/includes/projects/timeline.html", |
|||
{"doc": {"timelines": get_timeline(project, start)}}, is_path=True) |
|||
|
|||
def get_issue_list(project): |
|||
return [issue.name for issue in get_issues(project)] |
|||
|
|||
def get_tasks(project, start=0, search=None, taskstatus=None): |
|||
filters = {"project": project} |
|||
if search: |
|||
filters["subject"] = ("like", "%{0}%".format(search)) |
|||
if taskstatus: |
|||
filters = {"status": taskstatus} |
|||
tasks = frappe.get_all("Task", filters=filters, |
|||
fields=["name", "subject", "status", "exp_start_date", "exp_end_date", "priority"], |
|||
limit_start=start, limit_page_length=5) |
|||
|
|||
for task in tasks: |
|||
task.todo = frappe.get_all('ToDo',filters={'reference_name':task.name, 'reference_type':'Task'}, |
|||
fields=["assigned_by", "owner", "modified", "modified_by"]) |
|||
if task.todo: |
|||
task.todo=task.todo[0] |
|||
task.todo.user_image = frappe.db.get_value('User', task.todo.owner, 'user_image') |
|||
|
|||
return tasks |
|||
|
|||
@frappe.whitelist() |
|||
def get_tasks_html(project, start=0, taskstatus=None): |
|||
return frappe.render_template("erpnext/templates/includes/projects/project_tasks.html", |
|||
{"doc": {"tasks": get_tasks(project, start, taskstatus=taskstatus)}}, is_path=True) |
|||
|
|||
|
|||
def get_issues(project, start=0, search=None, issuestatus=None): |
|||
print issuestatus |
|||
filters = {"project": project} |
|||
if search: |
|||
filters["subject"] = ("like", "%{0}%".format(search)) |
|||
if issuestatus: |
|||
filters = {"status": issuestatus} |
|||
issues = frappe.get_all("Issue", filters=filters, |
|||
fields=["name", "subject", "status", "opening_date", "resolution_date", "resolution_details"], |
|||
order_by='modified desc', |
|||
limit_start=start, limit_page_length=5) |
|||
|
|||
for issue in issues: |
|||
issue.todo = frappe.get_all('ToDo',filters={'reference_name':issue.name, 'reference_type':'Issue'}, |
|||
fields=["assigned_by", "owner", "modified", "modified_by"]) |
|||
if issue.todo: |
|||
issue.todo=issue.todo[0] |
|||
issue.todo.user_image = frappe.db.get_value('User', issue.todo.owner, 'user_image') |
|||
|
|||
return issues |
|||
|
|||
@frappe.whitelist() |
|||
def get_issues_html(project, start=0, issuestatus=None): |
|||
print issuestatus |
|||
return frappe.render_template("erpnext/templates/includes/projects/project_issues.html", |
|||
{"doc": {"issues": get_issues(project, start, issuestatus=issuestatus)}}, is_path=True) |
|||
|
|||
def get_timelogs(project, start=0, search=None): |
|||
filters = {"project": project} |
|||
if search: |
|||
filters["title"] = ("like", "%{0}%".format(search)) |
|||
|
|||
timelogs = frappe.get_all('Time Log', filters=filters, |
|||
fields=['name','title','task','activity_type','from_time','to_time','hours','status','modified','modified_by'], |
|||
limit_start=start, limit_page_length=2) |
|||
for timelog in timelogs: |
|||
timelog.user_image = frappe.db.get_value('User', timelog.modified_by, 'user_image') |
|||
return timelogs |
|||
|
|||
@frappe.whitelist() |
|||
def get_timelogs_html(project, start=0): |
|||
return frappe.render_template("erpnext/templates/includes/projects/project_timelogs.html", |
|||
{"doc": {"timelogs": get_timelogs(project, start)}}, is_path=True) |
|||
|
|||
@frappe.whitelist() |
|||
def set_task_status(project, task_name): |
|||
task = frappe.get_doc("Task", task_name) |
|||
task.status = 'Closed' |
|||
task.save(ignore_permissions=True) |
|||
|
|||
@frappe.whitelist() |
|||
def set_issue_status(project, issue_name): |
|||
issue = frappe.get_doc("Issue", issue_name) |
|||
issue.status = 'Closed' |
|||
issue.save(ignore_permissions=True) |
|||
|
|||
|
@ -0,0 +1,149 @@ |
|||
{% extends "templates/web.html" %} |
|||
{% block title %} {{ doc.name }} {% endblock %} |
|||
{% block breadcrumbs %} |
|||
<div class="page-breadcrumbs" data-html-block="breadcrumbs"> |
|||
<ul class="breadcrumb"> |
|||
<li> |
|||
<span class="icon icon-angle-left"></span> |
|||
<a href="/projects?project={{ doc.project }}">{{ doc.project }}</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
{% endblock %} |
|||
{% block page_content %} |
|||
<div class="row"> |
|||
<div class=" col-sm-8 "> |
|||
<h1> {{ doc.subject }} </h1> |
|||
</div> |
|||
|
|||
<div class="col-sm-4"> |
|||
<div class="page-header-actions-block" data-html-block="header-actions"> |
|||
<button type="submit" class="btn btn-primary btn-sm btn-form-submit"> |
|||
Update</button> |
|||
<a href="tasks" class="btn btn-default btn-sm"> |
|||
Cancel</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="page-content-block"> |
|||
<form role="form" data-web-form="tasks"> |
|||
|
|||
<input type="hidden" name="web_form" value="tasks"> |
|||
<input type="hidden" name="doctype" value="Task"> |
|||
<input type="hidden" name="name" value="TASK00056"> |
|||
|
|||
<div class="row"> |
|||
<div class="col-sm-12" style="max-width: 500px;"> |
|||
<div class="form-group"> |
|||
<label for="project" class="control-label text-muted small">Project</label> |
|||
<input type="text" class="form-control" name="project" readonly value= "{{ doc.project }}"> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="subject" class="control-label text-muted small">Subject</label> |
|||
<input type="text" class="form-control" name="subject" readonly value="{{ doc.subject }}"> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="description" class="control-label text-muted small">Details</label> |
|||
<textarea class="form-control" style="height: 200px;" name="description">{{ doc.description }}</textarea> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="priority" class="control-label text-muted small">Priority</label> |
|||
<input type="text" class="form-control" name="priority" readonly value="{{ doc.priority }}"> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="exp_start_date" class="control-label text-muted small">Expected Start Date</label> |
|||
<input type="text" class="form-control hasDatepicker" name="exp_start_date" readonly value="{{ doc.exp_start_date }}"> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="exp_end_date" class="control-label text-muted small">Expected End Date</label> |
|||
<input type="text" class="form-control hasDatepicker" name="exp_end_date" readonly value="{{ doc.exp_end_date }}"> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="status" class="control-label text-muted small">Status</label> |
|||
<select class="form-control" name="status" id="status" data-label="Status" data-fieldtype="Select"> |
|||
<option value="Open" selected="selected"> |
|||
Open</option><option value="Working"> |
|||
Working</option><option value="Pending Review"> |
|||
Pending Review</option><option value="Overdue"> |
|||
Overdue</option><option value="Closed"> |
|||
Closed</option><option value="Cancelled"> |
|||
Cancelled</option> |
|||
</select> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
|
|||
<div class="comments"> |
|||
<h3>Comments</h3> |
|||
<div class="no-comment"> |
|||
{% for comment in comments %} |
|||
<p class="text-muted">{{comment.sender_full_name}} : {{comment.subject}} on {{comment.communication_date.strftime('%Y-%m-%d')}}</p> |
|||
{% endfor %} |
|||
</div> |
|||
<div class="comment-form-wrapper"> |
|||
<a class="add-comment btn btn-default btn-sm">Add Comment</a> |
|||
<div style="display: none;" id="comment-form"> |
|||
<p>Add Comment</p> |
|||
<form> |
|||
<fieldset> |
|||
<textarea class="form-control" name="comment" rows="5" placeholder="Comment"></textarea> |
|||
<p> |
|||
<button class="btn btn-primary btn-sm" id="submit-comment">Submit</button> |
|||
</p> |
|||
</fieldset> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<script> |
|||
frappe.ready(function() { |
|||
var n_comments = $(".comment-row").length; |
|||
$(".add-comment").click(function() { |
|||
$(this).toggle(false); |
|||
$("#comment-form").toggle(); |
|||
$("#comment-form textarea").val(""); |
|||
}) |
|||
$("#submit-comment").click(function() { |
|||
var args = { |
|||
comment_by_fullname: "test", |
|||
comment_by: "admin@localhost.com", |
|||
comment: $("[name='comment']").val(), |
|||
reference_doctype: "Task", |
|||
reference_name: "TASK00069", |
|||
comment_type: "Comment", |
|||
page_name: "tasks", |
|||
} |
|||
|
|||
frappe.call({ |
|||
btn: this, |
|||
type: "POST", |
|||
method: "frappe.templates.includes.comments.comments.add_comment", |
|||
args: args, |
|||
callback: function(r) { |
|||
if(r.exc) { |
|||
if(r._server_messages) |
|||
frappe.msgprint(r._server_messages); |
|||
} else { |
|||
$(r.message).appendTo("#comment-list"); |
|||
$(".no-comment, .add-comment").toggle(false); |
|||
$("#comment-form") |
|||
.replaceWith('<div class="text-muted">Thank you for your comment!</div>') |
|||
} |
|||
} |
|||
}) |
|||
|
|||
return false; |
|||
}) |
|||
}); |
|||
</script> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,14 @@ |
|||
from __future__ import unicode_literals |
|||
import frappe |
|||
|
|||
from frappe import _ |
|||
|
|||
def get_context(context): |
|||
context.no_cache = 1 |
|||
|
|||
task = frappe.get_doc('Task', frappe.form_dict.task) |
|||
|
|||
context.comments = frappe.get_all('Communication', filters={'reference_name': task.name, 'comment_type': 'comment'}, |
|||
fields=['subject', 'sender_full_name', 'communication_date']) |
|||
|
|||
context.doc = task |
@ -0,0 +1,48 @@ |
|||
{% extends "templates/web.html" %} |
|||
{% block title %} {{ doc.name }} {% endblock %} |
|||
{% block breadcrumbs %} |
|||
<div class="page-breadcrumbs" data-html-block="breadcrumbs"> |
|||
<ul class="breadcrumb"> |
|||
<li> |
|||
<span class="icon icon-angle-left"></span> |
|||
<a href="/projects?project={{ doc.project }}">{{ doc.project }}</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
{% block page_content %} |
|||
<div class=" col-sm-8 "> |
|||
<h1> {{ doc.name }} </h1> |
|||
</div> |
|||
|
|||
<div class="page-content-block"> |
|||
<div class="row"> |
|||
<div class="col-sm-12" style="max-width: 500px;"> |
|||
<label for="project" class="control-label text-muted small">Project</label> |
|||
<input type="text" class="form-control" name="project" readonly value= "{{ doc.project }}"> |
|||
|
|||
<label for="activity_type" class="control-label text-muted small">Activity Type</label> |
|||
<input type="text" class="form-control" name="activity_type" readonly value= "{{ doc.activity_type }}"> |
|||
|
|||
<label for="task" class="control-label text-muted small">Task</label> |
|||
<input type="text" class="form-control" name="task" readonly value= "{{ doc.task }}"> |
|||
|
|||
<label for="from_time" class="control-label text-muted small">From Time</label> |
|||
<input type="text" class="form-control" name="from_time" readonly value= "{{ doc.from_time }}"> |
|||
|
|||
<label for="to_time" class="control-label text-muted small">To Time</label> |
|||
<input type="text" class="form-control" name="to_time" readonly value= "{{ doc.to_time }}"> |
|||
|
|||
<label for="to_time" class="control-label text-muted small">Hours</label> |
|||
<input type="text" class="form-control" name="Hours" readonly value= "{{ doc.hours }}"> |
|||
|
|||
<label for="status" class="control-label text-muted small">Status</label> |
|||
<input type="text" class="form-control" name="status" readonly value= "{{ doc.status }}"> |
|||
|
|||
<label for="Note" class="control-label text-muted small">Note</label> |
|||
<textarea class="form-control" name="Hours" readonly> {{ doc.note }} </textarea> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
@ -0,0 +1,11 @@ |
|||
from __future__ import unicode_literals |
|||
import frappe |
|||
|
|||
from frappe import _ |
|||
|
|||
def get_context(context): |
|||
context.no_cache = 1 |
|||
|
|||
timelog = frappe.get_doc('Time Log', frappe.form_dict.timelog) |
|||
|
|||
context.doc = timelog |
Loading…
Reference in new issue