diff --git a/module/task/js/batchcreate.ui.js b/module/task/js/batchcreate.ui.js
index 92b17bf40d..bb139ef6d2 100644
--- a/module/task/js/batchcreate.ui.js
+++ b/module/task/js/batchcreate.ui.js
@@ -156,37 +156,46 @@ $(document).off('change', '#formSettingBtn input[value=story]').on('change', '#f
function checkBatchEstStartedAndDeadline(event)
{
+ if(taskDateLimit != 'limit') return;
+
const $currentRow = $(event.target).closest('tr');
const field = $(event.target).closest('.form-batch-control').data('name');
const estStarted = $currentRow.find('[name^=estStarted]').val();
const deadline = $currentRow.find('[name^=deadline]').val();
+ const level = $currentRow.attr('data-level');
- if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
+ let $nextRow = $currentRow.next();
+ while($nextRow.length)
{
- const $estStartedTd = $currentRow.find('td[data-name=estStarted]');
- if($estStartedTd.find('.date-tip').length == 0 || $estStartedTd.find('.date-tip .form-tip').length > 0)
- {
- $estStartedTd.find('.date-tip').remove();
+ let nextLevel = $nextRow.attr('data-level');
+ if(nextLevel <= level) break;
- let $datetip = $('
');
- $datetip.append('' + overParentEstStartedLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $estStartedTd.append($datetip);
- }
+ if(field == 'estStarted') $nextRow.find('td[data-name=estStarted]').find('[id^=estStarted]').zui('datepicker').render({disabled: estStarted == ''});
+ if(field == 'deadline') $nextRow.find('td[data-name=deadline]').find('[id^=deadline]').zui('datepicker').render({disabled: deadline == ''});
+
+ $nextRow = $nextRow.next();
}
+ if($currentRow.find('td[data-name=name]').find('input[name^=name]').val() == '') return;
+
+ const $estStartedTd = $currentRow.find('td[data-name=estStarted]');
+ $estStartedTd.find('.date-tip').remove();
+ if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
+ {
+ let $datetip = $('');
+ $datetip.append('' + overParentEstStartedLang + '
');
+ $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
+ $estStartedTd.append($datetip);
+ }
+
+ const $deadlineTd = $currentRow.find('td[data-name=deadline]');
+ $deadlineTd.find('.date-tip').remove();
if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline)
{
- const $deadlineTd = $currentRow.find('td[data-name=deadline]');
- if($deadlineTd.find('.date-tip').length == 0 || $deadlineTd.find('.date-tip .form-tip').length > 0)
- {
- $deadlineTd.find('.date-tip').remove();
-
- let $datetip = $('');
- $datetip.append('' + overParentDeadlineLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $deadlineTd.append($datetip);
- }
+ let $datetip = $('');
+ $datetip.append('' + overParentDeadlineLang + '
');
+ $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
+ $deadlineTd.append($datetip);
}
}
@@ -281,6 +290,27 @@ window.handleRenderRow = function($row, index)
const $preAssignedTo = $prevRow.find('input[name^=assignedTo]').zui('picker');
if($preAssignedTo != undefined) $assignedTo.render({items: $preAssignedTo.options.items});
})
+
+ if(taskDateLimit == 'limit')
+ {
+ let disabledStarted = false;
+ let disabledDeadline = false;
+ if(parentID)
+ {
+ disabledStarted = parentEstStarted == '';
+ disabledDeadline = parentDeadline == '';
+ }
+ else if(parentID == 0 && level > 0)
+ {
+ const $prevLevelRow = $row.prev('tr[data-level="' + (level - 1) + '"]');
+ const $prevLevelStarted = $prevLevelRow.find('td[data-name=estStarted]').find('[id^=estStarted]');
+ const $prevLevelDeadline = $prevLevelRow.find('td[data-name=deadline]').find('[id^=deadline]');
+ disabledStarted = $prevLevelStarted.val() == '' || $prevLevelStarted.prop('disabled');
+ disabledDeadline = $prevLevelDeadline.val() == '' || $prevLevelDeadline.prop('disabled');
+ }
+ $row.find('td[data-name=estStarted]').find('[id^=estStarted]').on('inited', function(e, info) { info[0].render({disabled: disabledStarted}); })
+ $row.find('td[data-name=deadline]').find('[id^=deadline]').on('inited', function(e, info) { info[0].render({disabled: disabledDeadline}); })
+ }
};
$(function()
diff --git a/module/task/js/batchedit.ui.js b/module/task/js/batchedit.ui.js
index 4193418ff6..8125bed055 100644
--- a/module/task/js/batchedit.ui.js
+++ b/module/task/js/batchedit.ui.js
@@ -8,6 +8,8 @@ window.renderRowData = function($row, index, row)
members[teamAccount] = users[teamAccount];
});
+ $row.attr('data-parent', row.parent);
+
let taskMembers = [];
if(row.mode != '' && teams[row.id] != undefined)
{
@@ -26,15 +28,25 @@ window.renderRowData = function($row, index, row)
const taskUsers = [];
let disabled = false;
$row.find('.form-batch-input[data-name="assignedTo"]').empty();
- if(teams[row.id] != undefined && ((row.mode == 'linear' && row.status != 'done') || taskMembers[currentUser] == undefined))
- {
- disabled = true;
- }
+ if(teams[row.id] != undefined && ((row.mode == 'linear' && row.status != 'done') || taskMembers[currentUser] == undefined)) disabled = true;
if(row.status == 'closed') disabled = true;
if(row.assignedTo && taskMembers[row.assignedTo] == undefined) taskMembers[row.assignedTo] = users[row.assignedTo];
for(let account in taskMembers) taskUsers.push({value: account, text: taskMembers[account]});
+ if(parentTasks[row.parent] != undefined && taskDateLimit == 'limit')
+ {
+ const parentTask = parentTasks[row.parent];
+ $row.find('[id^="estStarted"]').on('inited', function(e, info)
+ {
+ if(parentTask.estStarted == '') info[0].render({disabled: true});
+ });
+ $row.find('[id^="deadline"]').on('inited', function(e, info)
+ {
+ if(parentTask.deadline == '') info[0].render({disabled: true});
+ });
+ }
+
$row.find('[data-name="assignedTo"]').find('.picker-box').on('inited', function(e, info)
{
const $assignedTo = info[0];
@@ -46,13 +58,7 @@ window.renderRowData = function($row, index, row)
$assignedTo.render({items: taskUsers, disabled: disabled, toolbar: pickerToolbar});
});
- if(row.status == 'wait')
- {
- $row.find('[data-name="status"]').find('.picker-box').on('inited', function(e, info)
- {
- info[0].render({items: noPauseStatusList});
- });
- }
+ if(row.status == 'wait') $row.find('[data-name="status"]').find('.picker-box').on('inited', function(e, info) { info[0].render({items: noPauseStatusList}); });
if(teams[row.id] != undefined || row.isParent > 0)
{
@@ -173,51 +179,69 @@ window.statusChange = function(event)
function checkBatchEstStartedAndDeadline(event)
{
- if(parentTasks.length == 0) return true;
+ if(taskDateLimit != 'limit') return;
const $currentRow = $(event.target).closest('tr');
const taskID = $currentRow.find('[name^=id]').val();
const parentID = tasks[taskID].parent;
- if(typeof parentTasks[parentID] == 'undefined' || !parentTasks[parentID]) return true;
- const parentTask = parentTasks[parentID];
const field = $(event.target).closest('.form-batch-control').data('name');
const estStarted = $currentRow.find('[name^=estStarted]').val();
const deadline = $currentRow.find('[name^=deadline]').val();
+ const parentTask = parentTasks[parentID] ? parentTasks[parentID] : {estStarted: '', deadline: ''};
if(field == 'estStarted')
{
- let parentEstStarted = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').length == 0 ? parentTask.estStarted : $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').val();
- if(estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
- {
- const $estStartedTd = $currentRow.find('td[data-name=estStarted]');
- if($estStartedTd.find('.date-tip').length == 0 || $estStartedTd.find('.date-tip .form-tip').length > 0)
- {
- $estStartedTd.find('.date-tip').remove();
+ const $estStartedTd = $currentRow.find('td[data-name=estStarted]');
+ $estStartedTd.find('.date-tip').remove();
- let $datetip = $('');
- $datetip.append('' + overParentEstStartedLang.replace('%s', parentEstStarted) + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $estStartedTd.append($datetip);
+ const $childrenEstStarted = $(event.target).closest('tbody').find('tr[data-parent="' + taskID + '"]').find('[name^=estStarted]');
+ $childrenEstStarted.each(function(){$(this).zui('datePicker').render({disabled: estStarted.length == 0});});
+ if(estStarted.length > 0)
+ {
+ let $datetip = $('');
+ let parentEstStarted = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').length == 0 ? parentTask.estStarted : $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').val();
+ if(parentEstStarted.length > 0 && estStarted < parentEstStarted) $datetip.append('' + overParentEstStartedLang.replace('%s', parentEstStarted) + '
');
+
+ let childEstStarted = childrenDateLimit[taskID] ? childrenDateLimit[taskID].estStarted : '';
+ $childrenEstStarted.each(function()
+ {
+ if(childEstStarted.length == 0 || ($(this).val().length > 0 && $(this).val() < childEstStarted)) childEstStarted = $(this).val();
+ });
+ if(childEstStarted.length > 0 && estStarted > childEstStarted)
+ {
+ $datetip.append('' + overChildEstStartedLang.replace('%s', childEstStarted) + '' + ignoreLang + '
');
+ $datetip.off('click', '.ignore-child').on('click', '.ignore-child', function(e){ignoreTip(e)});
}
+ $estStartedTd.append($datetip);
}
}
if(field == 'deadline')
{
- let parentDeadline = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').length == 0 ? parentTask.deadline : $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').val();
- if(deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline)
- {
- const $deadlineTd = $currentRow.find('td[data-name=deadline]');
- if($deadlineTd.find('.date-tip').length == 0 || $deadlineTd.find('.date-tip .form-tip').length > 0)
- {
- $deadlineTd.find('.date-tip').remove();
+ const $deadlineTd = $currentRow.find('td[data-name=deadline]');
+ $deadlineTd.find('.date-tip').remove();
- let $datetip = $('');
- $datetip.append('' + overParentDeadlineLang.replace('%s', parentDeadline) + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $deadlineTd.append($datetip);
+ const $childrenDeadline = $(event.target).closest('tbody').find('tr[data-parent="' + taskID + '"]').find('[name^=deadline]');
+ $childrenDeadline.each(function(){$(this).zui('datePicker').render({disabled: deadline.length == 0});});
+ if(deadline.length > 0)
+ {
+ let $datetip = $('');
+
+ let parentDeadline = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').length == 0 ? parentTask.deadline : $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').val();
+ if(parentDeadline.length > 0 && deadline > parentDeadline) $datetip.append('' + overParentDeadlineLang.replace('%s', parentDeadline) + '
');
+
+ let childDeadline = childrenDateLimit[taskID] ? childrenDateLimit[taskID].deadline : '';
+ $childrenDeadline.each(function()
+ {
+ if(childDeadline.length == 0 || ($(this).val().length > 0 && $(this).val() > childDeadline)) childDeadline = $(this).val();
+ });
+ if(childDeadline.length > 0 && deadline < childDeadline)
+ {
+ $datetip.append('' + overChildDeadlineLang.replace('%s', childDeadline) + '' + ignoreLang + '
');
+ $datetip.off('click', '.ignore-child').on('click', '.ignore-child', function(e){ignoreTip(e)});
}
+ $deadlineTd.append($datetip);
}
}
}
diff --git a/module/task/js/create.ui.js b/module/task/js/create.ui.js
index d4e59bb2b4..57a0a5ea99 100644
--- a/module/task/js/create.ui.js
+++ b/module/task/js/create.ui.js
@@ -644,8 +644,17 @@ overParentEstStartedLang = '';
overParentDeadlineLang = '';
window.getParentEstStartedAndDeadline = function()
{
- const parent = $('[name=parent]').val();
- if(!parent) return;
+ const $parent = $('[name=parent]');
+ const parent = $parent.val();
+ if(!parent)
+ {
+ if(taskDateLimit != 'limit') return;
+
+ const $form = $parent.closest('form');
+ $form.find('[name=estStarted]').zui('datePicker').render({disabled: false});
+ $form.find('[name=deadline]').zui('datePicker').render({disabled: false});
+ return;
+ }
const link = $.createLink('task', 'ajaxGetTaskEstStartedAndDeadline', 'taskID=' + parent);
$.getJSON(link, function(data)
@@ -662,6 +671,8 @@ window.getParentEstStartedAndDeadline = function()
window.checkEstStartedAndDeadline = function(event)
{
+ if(taskDateLimit != 'limit') return;
+
const parent = $('[name=parent]').val();
if(!parent) return;
@@ -672,31 +683,28 @@ window.checkEstStartedAndDeadline = function(event)
const $deadline = $form.find('[name=deadline]');
const deadline = $deadline.val();
+ const $estStartedDiv = $estStarted.closest('.form-group-wrapper');
+ if(field == 'estStarted') $estStartedDiv.find('.date-tip').remove();
if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
{
- const $estStartedDiv = $estStarted.closest('.form-group');
- if($estStartedDiv.find('.date-tip').length == 0 || $estStartedDiv.find('.date-tip .form-tip').length > 0)
- {
- $estStartedDiv.find('.date-tip').remove();
-
- let $datetip = $('');
- $datetip.append('' + overParentEstStartedLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $estStartedDiv.append($datetip);
- }
+ let $datetip = $('');
+ $datetip.append('' + overParentEstStartedLang + '
');
+ $estStartedDiv.append($datetip);
}
+ const $deadlineDiv = $deadline.closest('.form-group-wrapper');
+ if(field == 'deadline') $deadlineDiv.find('.date-tip').remove();
if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline)
{
- const $deadlineDiv = $deadline.closest('.form-group');
- if($deadlineDiv.find('.date-tip').length == 0 || $deadlineDiv.find('.date-tip .form-tip').length > 0)
- {
- $deadlineDiv.find('.date-tip').remove();
-
- let $datetip = $('');
- $datetip.append('' + overParentDeadlineLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $deadlineDiv.append($datetip);
- }
+ let $datetip = $('');
+ $datetip.append('' + overParentDeadlineLang + '
');
+ $deadlineDiv.append($datetip);
}
+
+ let $estStartedPicker = $estStarted.zui('datePicker');
+ let $deadlinePicker = $deadline.zui('datePicker');
+ $estStartedPicker.render({disabled: parentEstStarted == ''});
+ $deadlinePicker.render({disabled: parentDeadline == ''});
+ if(parentEstStarted == '') $estStartedPicker.$.setValue('');
+ if(parentDeadline == '') $deadlinePicker.$.setValue('');
}
diff --git a/module/task/js/edit.ui.js b/module/task/js/edit.ui.js
index cbc33d8a55..86435cd4c4 100644
--- a/module/task/js/edit.ui.js
+++ b/module/task/js/edit.ui.js
@@ -413,8 +413,17 @@ window.setStoryModule = function()
getParentEstStartedAndDeadline = function()
{
- const parent = $('[name=parent]').val();
- if(!parent) return;
+ const $parent = $('[name=parent]');
+ const parent = $parent.val();
+ if(!parent)
+ {
+ if(taskDateLimit != 'limit') return;
+
+ const $form = $parent.closest('form');
+ $form.find('[name=estStarted]').zui('datePicker').render({disabled: false});
+ $form.find('[name=deadline]').zui('datePicker').render({disabled: false});
+ return;
+ }
const link = $.createLink('task', 'ajaxGetTaskEstStartedAndDeadline', 'taskID=' + parent);
$.getJSON(link, function(data)
@@ -431,6 +440,11 @@ getParentEstStartedAndDeadline = function()
function checkEstStartedAndDeadline(event)
{
+ if(taskDateLimit != 'limit') return;
+
+ const parent = $('[name=parent]').val();
+ if(!parent) return;
+
const $form = $(event.target).closest('form');
const field = $(event.target).attr('name')
const $estStarted = $form.find('[name=estStarted]');
@@ -438,31 +452,40 @@ function checkEstStartedAndDeadline(event)
const $deadline = $form.find('[name=deadline]');
const deadline = $deadline.val();
- if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
+ const $estStartedDiv = $estStarted.closest('.form-group');
+ if(field == 'estStarted' && estStarted.length > 0)
{
- const $estStartedDiv = $estStarted.closest('.form-group');
- if($estStartedDiv.find('.date-tip').length == 0 || $estStartedDiv.find('.date-tip .form-tip').length > 0)
- {
- $estStartedDiv.find('.date-tip').remove();
+ $estStartedDiv.find('.date-tip').remove();
+ let $datetip = $('');
+ if(parentEstStarted.length > 0 && estStarted < parentEstStarted) $datetip.append('' + overParentEstStartedLang + '
');
- let $datetip = $('');
- $datetip.append('' + overParentEstStartedLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $estStartedDiv.append($datetip);
+ if(childDateLimit['estStarted'].length > 0 && estStarted > childDateLimit['estStarted'])
+ {
+ $datetip.append('' + overChildEstStartedLang + '' + ignoreLang + '
');
+ $datetip.off('click', '.ignore-child').on('click', '.ignore-child', function (e) { ignoreTip(e) });
}
+ $estStartedDiv.append($datetip);
}
- if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline)
+ const $deadlineDiv = $deadline.closest('.form-group');
+ if(field == 'deadline' && deadline.length > 0)
{
- const $deadlineDiv = $deadline.closest('.form-group');
- if($deadlineDiv.find('.date-tip').length == 0 || $deadlineDiv.find('.date-tip .form-tip').length > 0)
- {
- $deadlineDiv.find('.date-tip').remove();
+ $deadlineDiv.find('.date-tip').remove();
+ let $datetip = $('');
+ if(parentDeadline.length > 0 && deadline > parentDeadline) $datetip.append('' + overParentDeadlineLang + '
');
- let $datetip = $('');
- $datetip.append('' + overParentDeadlineLang + '' + ignoreLang + '
');
- $datetip.off('click', '.ignore-date').on('click', '.ignore-date', function(e){ignoreTip(e)});
- $deadlineDiv.append($datetip);
+ if(childDateLimit['deadline'].length > 0 && deadline < childDateLimit['deadline'])
+ {
+ $datetip.append('' + overChildDeadlineLang + '' + ignoreLang + '
');
+ $datetip.off('click', '.ignore-child').on('click', '.ignore-child', function (e) { ignoreTip(e) });
}
+ $deadlineDiv.append($datetip);
}
+
+ let $estStartedPicker = $estStarted.zui('datePicker');
+ let $deadlinePicker = $deadline.zui('datePicker');
+ $estStartedPicker.render({disabled: parentEstStarted == ''});
+ $deadlinePicker.render({disabled: parentDeadline == ''});
+ if(parentEstStarted == '') $estStartedPicker.$.setValue('');
+ if(parentDeadline == '') $deadlinePicker.$.setValue('');
}
diff --git a/module/task/lang/de.php b/module/task/lang/de.php
index ba80d1888f..8feeb4a82b 100644
--- a/module/task/lang/de.php
+++ b/module/task/lang/de.php
@@ -437,6 +437,8 @@ $lang->task->overEsEndDate = 'The %s schedule end time has exceeded, please mo
$lang->task->overParentEsStarted = 'StartDate is less than the parent task\'s startDate: %s';
$lang->task->overParentDeadline = 'Deadline is greater than the parent task\'s deadline: %s';
+$lang->task->overChildEstStarted = "Existed child task's startDate is less than the task's startDate: %s";
+$lang->task->overChildDeadline = "Existed child task's deadline is greater than the task's deadline: %s";
$lang->task->disabledHint = new stdclass();
$lang->task->disabledHint->assignedConfirmStoryChange = 'Changes can only be confirmed by the assignee.';
diff --git a/module/task/lang/en.php b/module/task/lang/en.php
index 3eaf16fa8c..a9d6796b81 100755
--- a/module/task/lang/en.php
+++ b/module/task/lang/en.php
@@ -437,6 +437,8 @@ $lang->task->overEsEndDate = 'The %s schedule end time has exceeded, please mo
$lang->task->overParentEsStarted = 'StartDate is less than the parent task\'s startDate: %s';
$lang->task->overParentDeadline = 'Deadline is greater than the parent task\'s deadline: %s';
+$lang->task->overChildEstStarted = "Existed child task's startDate is less than the task's startDate: %s";
+$lang->task->overChildDeadline = "Existed child task's deadline is greater than the task's deadline: %s";
$lang->task->disabledHint = new stdclass();
$lang->task->disabledHint->assignedConfirmStoryChange = 'Changes can only be confirmed by the assignee.';
diff --git a/module/task/lang/fr.php b/module/task/lang/fr.php
index 2a33173f1b..a85ea8bcb5 100644
--- a/module/task/lang/fr.php
+++ b/module/task/lang/fr.php
@@ -437,6 +437,8 @@ $lang->task->overEsEndDate = 'The %s schedule end time has exceeded, please mo
$lang->task->overParentEsStarted = 'StartDate is less than the parent task\'s startDate: %s';
$lang->task->overParentDeadline = 'Deadline is greater than the parent task\'s deadline: %s';
+$lang->task->overChildEstStarted = "Existed child task's startDate is less than the task's startDate: %s";
+$lang->task->overChildDeadline = "Existed child task's deadline is greater than the task's deadline: %s";
$lang->task->disabledHint = new stdclass();
$lang->task->disabledHint->assignedConfirmStoryChange = 'Changes can only be confirmed by the assignee.';
diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php
index d09ef454f5..164a921461 100755
--- a/module/task/lang/zh-cn.php
+++ b/module/task/lang/zh-cn.php
@@ -437,6 +437,8 @@ $lang->task->overEsEndDate = '已超出%s计划结束时间,请先修改%s
$lang->task->overParentEsStarted = '任务的预计开始日期小于了父任务的预计开始日期:%s';
$lang->task->overParentDeadline = '任务的截止日期大于了父任务的截止日期:%s';
+$lang->task->overChildEstStarted = '存在子任务的预计开始日期小于了该任务的预计开始日期:%s';
+$lang->task->overChildDeadline = '存在子任务的截止日期超出了该任务的截止日期:%s';
$lang->task->disabledHint = new stdclass();
$lang->task->disabledHint->assignedConfirmStoryChange = '只有指派人才能确认变更';
diff --git a/module/task/model.php b/module/task/model.php
index cab61faf1e..24ea5bd5ce 100755
--- a/module/task/model.php
+++ b/module/task/model.php
@@ -749,26 +749,29 @@ class taskModel extends model
* @param int $executionID
* @param string $estStarted
* @param string $deadline
- * @param string $prefix
+ * @param int|null $rowID
* @access public
* @return false|void
*/
- public function checkEstStartedAndDeadline(int $executionID, string $estStarted, string $deadline, string $prefix = '')
+ public function checkEstStartedAndDeadline(int $executionID, string $estStarted, string $deadline, int|null $rowID = null)
{
+ $beginIndex = $rowID === null ? 'estStarted' : "estStarted[$rowID]";
+ $endIndex = $rowID === null ? 'deadline' : "deadline[$rowID]";
+
$execution = $this->loadModel('execution')->getByID($executionID);
if(empty($execution) || empty($this->config->limitTaskDate)) return false;
if(empty($execution->multiple)) $this->lang->execution->common = $this->lang->project->common;
if(!empty($estStarted) && !helper::isZeroDate($estStarted))
{
- if($estStarted < $execution->begin) dao::$errors['estStarted'] = $prefix . sprintf($this->lang->task->error->beginLtExecution, $this->lang->execution->common, $execution->begin);
- if($estStarted > $execution->end) dao::$errors['estStarted'] = $prefix . sprintf($this->lang->task->error->beginGtExecution, $this->lang->execution->common, $execution->end);
+ if($estStarted < $execution->begin) dao::$errors[$beginIndex] = sprintf($this->lang->task->error->beginLtExecution, $this->lang->execution->common, $execution->begin);
+ if($estStarted > $execution->end) dao::$errors[$beginIndex] = sprintf($this->lang->task->error->beginGtExecution, $this->lang->execution->common, $execution->end);
}
if(!empty($deadline) && !helper::isZeroDate($deadline))
{
- if($deadline > $execution->end) dao::$errors['deadline'] = $prefix . sprintf($this->lang->task->error->endGtExecution, $this->lang->execution->common, $execution->end);
- if($deadline < $execution->begin) dao::$errors['deadline'] = $prefix . sprintf($this->lang->task->error->endLtExecution, $this->lang->execution->common, $execution->begin);
+ if($deadline > $execution->end) dao::$errors[$endIndex] = sprintf($this->lang->task->error->endGtExecution, $this->lang->execution->common, $execution->end);
+ if($deadline < $execution->begin) dao::$errors[$endIndex] = sprintf($this->lang->task->error->endLtExecution, $this->lang->execution->common, $execution->begin);
}
}
@@ -821,6 +824,11 @@ class taskModel extends model
$tasks = $this->dao->select('estStarted, realStarted, deadline')->from(TABLE_TASK)->where('parent')->eq($taskID)->andWhere('status')->ne('cancel')->andWhere('deleted')->eq(0)->fetchAll();
if(empty($tasks)) return !dao::isError();
+ /* Initialize task data and update it. */
+ $parent = $this->fetchById($taskID);
+ $taskDateLimit = $this->dao->select('taskDateLimit')->from(TABLE_PROJECT)->where('id')->eq($parent->project)->fetch('taskDateLimit');
+ if($taskDateLimit == 'limit') return !dao::isError();
+
/* Compute the earliest estStarted, the earliest realStarted and the latest deadline. */
$earliestEstStarted = '';
$earliestRealStarted = '';
@@ -832,9 +840,6 @@ class taskModel extends model
if(!helper::isZeroDate($task->deadline) && (empty($latestDeadline) || $latestDeadline < $task->deadline)) $latestDeadline = $task->deadline;
}
- /* Initialize task data and update it. */
- $parent = $this->fetchById($taskID);
-
$newTask = array();
if(!empty($earliestEstStarted) && !helper::isZeroDate($parent->estStarted) && $parent->estStarted > $earliestEstStarted) $newTask['estStarted'] = $earliestEstStarted;
if(!empty($earliestRealStarted) && !helper::isZeroDate($parent->realStarted) && $parent->realStarted > $earliestRealStarted) $newTask['realStarted'] = $earliestRealStarted;
@@ -3251,6 +3256,21 @@ class taskModel extends model
if($postData->type == 'task')
{
+ $project = $this->dao->select('id,taskDateLimit')->from(TABLE_PROJECT)->where('id')->eq($oldObject->project)->fetch();
+ if($project->taskDateLimit == 'limit')
+ {
+ $parentTasks = $this->dao->select('id,estStarted,deadline')->from(TABLE_TASK)->where('id')->in($oldObject->path)->andWhere('id')->ne($oldObject->id)->fetchAll('id');
+ foreach(array_reverse(array_filter(explode(',', $oldObject->path))) as $taskID)
+ {
+ if(!isset($parentTasks[$taskID])) continue;
+
+ $parentTask = $parentTasks[$taskID];
+ if(!helper::isZeroDate($parentTask->estStarted) && $parentTask->estStarted > $postData->startDate) dao::$errors[] = sprintf($this->lang->task->overParentEsStarted, $parentTask->estStarted);
+ if(!helper::isZeroDate($parentTask->deadline) && $parentTask->deadline < $postData->endDate) dao::$errors[] = sprintf($this->lang->task->overParentDeadline, $parentTask->deadline);
+ }
+ if(dao::isError()) return false;
+ }
+
$oldObject->estStarted = $postData->startDate;
$oldObject->deadline = $postData->endDate;
unset($oldObject->openedDate);
@@ -3728,7 +3748,7 @@ class taskModel extends model
*/
public function getChildTasksByList(array $taskIdList): array|false
{
- $childTasks = $this->dao->select('id,parent')->from(TABLE_TASK)->where('parent')->in($taskIdList)->andWhere('deleted')->eq('0')->fetchGroup('parent', 'id');
+ $childTasks = $this->dao->select('id,parent,path,estStarted,deadline')->from(TABLE_TASK)->where('parent')->in($taskIdList)->andWhere('deleted')->eq('0')->fetchGroup('parent', 'id');
$nonStoryChildTasks = $this->dao->select('id,parent')->from(TABLE_TASK)->where('parent')->in($taskIdList)->andWhere('story')->eq('0')->andWhere('deleted')->eq('0')->fetchGroup('parent', 'id');
return array($childTasks, $nonStoryChildTasks);
}
diff --git a/module/task/ui/batchcreate.html.php b/module/task/ui/batchcreate.html.php
index 9a22456b53..9227d8db9f 100644
--- a/module/task/ui/batchcreate.html.php
+++ b/module/task/ui/batchcreate.html.php
@@ -24,6 +24,7 @@ jsVar('overParentEstStartedLang', isset($parentTask) ? sprintf($lang->task->over
jsVar('overParentDeadlineLang', isset($parentTask) ? sprintf($lang->task->overParentDeadline, $parentTask->deadline) : '');
jsVar('taskHasConsumed', $taskConsumed > 0);
jsVar('langAddChildTask', $lang->task->addChildTask);
+jsVar('taskDateLimit', $project->taskDateLimit);
/* zin: Set variables to define picker options for form. */
$storyItem = '';
diff --git a/module/task/ui/batchedit.html.php b/module/task/ui/batchedit.html.php
index 86b24479ba..8a159656c3 100644
--- a/module/task/ui/batchedit.html.php
+++ b/module/task/ui/batchedit.html.php
@@ -27,6 +27,7 @@ jsVar('moduleGroup', $moduleGroup);
jsVar('executionID', $executionID);
jsVar('childTasks', $childTasks);
jsVar('nonStoryChildTasks', $nonStoryChildTasks);
+jsVar('childrenDateLimit', $childrenDateLimit);
jsVar('tasks', $tasks);
jsVar('noPauseStatusList', $noPauseStatusList);
jsVar('stories', $stories);
@@ -37,7 +38,10 @@ jsVar('noSprintPairs', $noSprintPairs);
jsVar('ignoreLang', $lang->project->ignore);
jsVar('overParentEstStartedLang', $lang->task->overParentEsStarted);
jsVar('overParentDeadlineLang', $lang->task->overParentDeadline);
+jsVar('overChildEstStartedLang', $lang->task->overChildEstStarted);
+jsVar('overChildDeadlineLang', $lang->task->overChildDeadline);
jsVar('manageTeamMemberText', $lang->execution->manageTeamMember);
+jsVar('taskDateLimit', empty($project) ? '' : $project->taskDateLimit);
/* ====== Define the page structure with zin widgets ====== */
formBatchPanel
diff --git a/module/task/ui/create.html.php b/module/task/ui/create.html.php
index abdb3a52e7..0ef6939158 100644
--- a/module/task/ui/create.html.php
+++ b/module/task/ui/create.html.php
@@ -29,6 +29,7 @@ jsVar('showFields', $showFields);
jsVar('canViewStory', common::hasPriv('execution', 'storyView'));
jsVar('ignoreLang', $lang->project->ignore);
jsVar('assignedToOptions', $assignedToOptions);
+jsVar('taskDateLimit', $project->taskDateLimit);
if(!empty($task->team))
{
diff --git a/module/task/ui/edit.html.php b/module/task/ui/edit.html.php
index 9cdf5f6a75..7769704b66 100644
--- a/module/task/ui/edit.html.php
+++ b/module/task/ui/edit.html.php
@@ -34,9 +34,13 @@ jsVar('leftNotEmpty', sprintf($lang->error->gt, $lang->task->left, '0'));
jsVar('requiredFields', $config->task->edit->requiredFields);
jsVar('+parentEstStarted', !empty($parentTask) ? $parentTask->estStarted : '');
jsVar('+parentDeadline', !empty($parentTask) ? $parentTask->deadline : '');
+jsVar('childDateLimit', $childDateLimit);
jsVar('ignoreLang', $lang->project->ignore);
jsVar('+overParentEstStartedLang', !empty($parentTask) ? sprintf($lang->task->overParentEsStarted, $parentTask->estStarted) : '');
jsVar('+overParentDeadlineLang', !empty($parentTask) ? sprintf($lang->task->overParentDeadline, $parentTask->deadline) : '');
+jsVar('+overChildEstStartedLang', sprintf($lang->task->overChildEstStarted, $childDateLimit['estStarted']));
+jsVar('+overChildDeadlineLang', sprintf($lang->task->overChildDeadline, $childDateLimit['deadline']));
+jsVar('taskDateLimit', empty($project) ? '' : $project->taskDateLimit);
$confirmSyncTip = '';
if(!empty($syncChildren) && !empty($task->children)) $confirmSyncTip = sprintf($lang->task->syncStoryToChildrenTip, 'ID' . implode(', ID', $syncChildren));
@@ -463,6 +467,7 @@ detailBody
datePicker
(
set::name('estStarted'),
+ set::disabled(!empty($parentTask) && helper::isZeroDate($parentTask->estStarted) ? true : false),
on::change('checkEstStartedAndDeadline'),
helper::isZeroDate($task->estStarted) ? null : set::value($task->estStarted)
)
@@ -477,6 +482,7 @@ detailBody
datePicker
(
set::name('deadline'),
+ set::disabled(!empty($parentTask) && helper::isZeroDate($parentTask->deadline) ? true : false),
on::change('checkEstStartedAndDeadline'),
helper::isZeroDate($task->deadline) ? null : set::value($task->deadline)
)
diff --git a/module/task/zen.php b/module/task/zen.php
index 36c66d88a9..ec75c3915c 100644
--- a/module/task/zen.php
+++ b/module/task/zen.php
@@ -70,6 +70,7 @@ class taskZen extends task
$this->view->showFields = $this->config->task->custom->createFields;
$this->view->gobackLink = (isset($output['from']) && $output['from'] == 'global') ? $this->createLink('execution', 'task', "executionID={$executionID}") : '';
$this->view->execution = $execution;
+ $this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->storyID = $storyID;
$this->view->blockID = helper::isAjaxRequest('modal') ? $this->loadModel('block')->getSpecifiedBlockID('my', 'assigntome', 'assigntome') : 0;
$this->view->hideStory = $this->task->isNoStoryExecution($execution);
@@ -195,6 +196,7 @@ class taskZen extends task
$this->view->title = $execution->name . $this->lang->hyphen . $this->lang->task->batchEdit;
$this->view->execution = $execution;
+ $this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->modules = $this->tree->getTaskOptionMenu($executionID, 0, !empty($this->config->task->allModule) ? 'allModule' : '');
}
else
@@ -247,6 +249,18 @@ class taskZen extends task
}
list($childTasks, $nonStoryChildTasks) = $this->task->getChildTasksByList(array_keys($tasks));
+ $childrenDateLimit = array();
+ foreach($childTasks as $parent => $children)
+ {
+ $childDateLimit = array('estStarted' => '', 'deadline' => '');
+ foreach($children as $child)
+ {
+ if(!helper::isZeroDate($child->estStarted) && (empty($childDateLimit['estStarted']) || $childDateLimit['estStarted'] > $child->estStarted)) $childDateLimit['estStarted'] = $child->estStarted;
+ if(!helper::isZeroDate($child->deadline) && (empty($childDateLimit['deadline']) || $childDateLimit['deadline'] < $child->deadline)) $childDateLimit['deadline'] = $child->deadline;
+ }
+ $childrenDateLimit[$parent] = $childDateLimit;
+ }
+
$storyPairs = $this->story->getExecutionStoryPairs($executionID, 0, 'all', '', 'full', 'active', 'story', false);;
$storyList = $this->story->getByList(array_keys($storyPairs));
$stories = array();
@@ -268,6 +282,7 @@ class taskZen extends task
$this->view->moduleGroup = $moduleGroup;
$this->view->childTasks = $childTasks;
$this->view->nonStoryChildTasks = $nonStoryChildTasks;
+ $this->view->childrenDateLimit = $childrenDateLimit;
$this->view->stories = $stories;
$this->view->parentTasks = $this->task->getByIdList($parentTaskIdList);
$this->view->noSprintPairs = $this->loadModel('project')->getProjectExecutionPairs();
@@ -301,7 +316,7 @@ class taskZen extends task
$executions = !empty($task->project) ? $this->execution->getByProject($task->project, 'all', 0, true) : array();
/* Get task members. */
- $taskMembers = array();
+ $taskMembers = $this->view->members;
if(!empty($task->team))
{
foreach($task->members as $teamAccount)
@@ -310,10 +325,6 @@ class taskZen extends task
$taskMembers[$teamAccount] = $this->view->members[$teamAccount];
}
}
- else
- {
- $taskMembers = $this->view->members;
- }
/* Get execution stories. */
$moduleID = $task->module;
@@ -324,29 +335,34 @@ class taskZen extends task
}
$stories = $this->story->getExecutionStoryPairs($this->view->execution->id, 0, 'all', $moduleID, 'full', 'active', 'story', false);
- $syncChildren = array();
+ $syncChildren = array();
+ $childDateLimit = array('estStarted' => '', 'deadline' => '');
if(!empty($task->children))
{
foreach($task->children as $child)
{
if(empty($child->story)) $syncChildren[] = $child->id;
+ if(!helper::isZeroDate($child->estStarted) && (empty($childDateLimit['estStarted']) || $childDateLimit['estStarted'] > $child->estStarted)) $childDateLimit['estStarted'] = $child->estStarted;
+ if(!helper::isZeroDate($child->deadline) && (empty($childDateLimit['deadline']) || $childDateLimit['deadline'] < $child->deadline)) $childDateLimit['deadline'] = $child->deadline;
}
}
if($this->view->execution->multiple) $manageLink = common::hasPriv('execution', 'manageMembers') ? $this->createLink('execution', 'manageMembers', "execution={$this->view->execution->id}") : '';
if(!$this->view->execution->multiple) $manageLink = common::hasPriv('project', 'manageMembers') ? $this->createLink('project', 'manageMembers', "projectID={$this->view->execution->project}") : '';
- $this->view->title = $this->lang->task->edit . 'TASK' . $this->lang->hyphen . $this->view->task->name;
- $this->view->stories = $this->story->addGradeLabel($stories);
- $this->view->tasks = $tasks;
- $this->view->taskMembers = $taskMembers;
- $this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed', "{$task->openedBy},{$task->canceledBy},{$task->closedBy}");
- $this->view->showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
- $this->view->modules = $this->tree->getTaskOptionMenu($task->execution, 0, $this->view->showAllModule ? 'allModule' : '');
- $this->view->executions = $executions;
- $this->view->syncChildren = $syncChildren;
- $this->view->parentTask = !empty($task->parent) ? $this->task->getById($task->parent) : null;
- $this->view->manageLink = $manageLink;
+ $this->view->title = $this->lang->task->edit . 'TASK' . $this->lang->hyphen . $this->view->task->name;
+ $this->view->stories = $this->story->addGradeLabel($stories);
+ $this->view->tasks = $tasks;
+ $this->view->taskMembers = $taskMembers;
+ $this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed', "{$task->openedBy},{$task->canceledBy},{$task->closedBy}");
+ $this->view->showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
+ $this->view->modules = $this->tree->getTaskOptionMenu($task->execution, 0, $this->view->showAllModule ? 'allModule' : '');
+ $this->view->executions = $executions;
+ $this->view->syncChildren = $syncChildren;
+ $this->view->childDateLimit = $childDateLimit;
+ $this->view->parentTask = !empty($task->parent) ? $this->task->getById($task->parent) : null;
+ $this->view->manageLink = $manageLink;
+ $this->view->project = $task->project ? $this->loadModel('project')->fetchById($task->project) : null;
$this->display();
}
@@ -437,6 +453,7 @@ class taskZen extends task
$this->view->title = $this->lang->task->batchCreate;
$this->view->execution = $execution;
+ $this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->modules = $modules;
$this->view->parent = $taskID;
$this->view->storyID = $storyID;
@@ -558,12 +575,13 @@ class taskZen extends task
->stripTags($this->config->task->editor->edit['id'], $this->config->allowedTags)
->get();
+ $project = $this->loadModel('project')->fetchById($oldTask->project);
+ $parents = $this->getParentEstStartedAndDeadline(array($task->parent));
+ $this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null);
+
$team = $this->post->team ? array_filter($this->post->team) : array();
- if($task->mode && empty($team))
- {
- dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
- return false;
- }
+ if($task->mode && empty($team)) dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
+ if(dao::isError()) return false;
return $this->loadModel('file')->processImgURL($task, $this->config->task->editor->edit['id'], (string)$this->post->uid);
}
@@ -1006,6 +1024,31 @@ class taskZen extends task
$this->setMenu($this->view->execution->id);
}
+ /**
+ * 检查任务的开始时间和截止时间是否合法。
+ * Check if the start and end time of the task is legal.
+ *
+ * @param object $task
+ * @param bool $isDateLimit
+ * @param object $parent
+ * @param int|null $rowID
+ * @access public
+ * @return void
+ */
+ public function checkLegallyDate(object $task, bool $isDateLimit, object|null $parent, int|null $rowID = null): void
+ {
+ $beginIndex = $rowID === null ? 'estStarted' : "estStarted[$rowID]";
+ $endIndex = $rowID === null ? 'deadline' : "deadline[$rowID]";
+
+ $beginIsZeroDate = helper::isZeroDate($task->estStarted);
+ $endIsZeroDate = helper::isZeroDate($task->deadline);
+ if(!$beginIsZeroDate and !$endIsZeroDate and $task->deadline < $task->estStarted) dao::$errors[$endIndex] = $this->lang->task->error->deadlineSmall;
+
+ if(!$isDateLimit || empty($parent)) return;
+ if(!$beginIsZeroDate && !helper::isZeroDate($parent->estStarted) && $task->estStarted < $parent->estStarted) dao::$errors[$beginIndex] = sprintf($this->lang->task->overParentEsStarted, $parent->estStarted);
+ if(!$endIsZeroDate && !helper::isZeroDate($parent->deadline) && $task->deadline > $parent->deadline) dao::$errors[$endIndex] = sprintf($this->lang->task->overParentDeadline, $parent->deadline);
+ }
+
/**
* 检查传入的创建数据是否符合要求。
* Check if the input post meets the requirements.
@@ -1018,17 +1061,9 @@ class taskZen extends task
protected function checkCreateTask(object $task, array $team): bool
{
/* Check if the estimate is positive. */
- if($task->estimate < 0)
- {
- dao::$errors['estimate'] = $this->lang->task->error->recordMinus;
- return false;
- }
-
- if($this->post->multiple && empty($team))
- {
- dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
- return false;
- }
+ if($task->estimate < 0) dao::$errors['estimate'] = $this->lang->task->error->recordMinus;
+ if($this->post->multiple && empty($team)) dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
+ if(dao::isError()) return false;
/* If the task start and end date must be between the execution start and end date, check if the task start and end date accord with the conditions. */
if(!empty($this->config->limitTaskDate))
@@ -1037,12 +1072,9 @@ class taskZen extends task
if(dao::isError()) return false;
}
- /* Check start and end date. */
- if(!helper::isZeroDate($task->deadline) && $task->estStarted > $task->deadline)
- {
- dao::$errors['deadline'] = $this->lang->task->error->deadlineSmall;
- return false;
- }
+ $project = $this->dao->findById($task->project)->from(TABLE_PROJECT)->fetch();
+ $parents = $this->getParentEstStartedAndDeadline(array($task->parent));
+ $this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null);
return !dao::isError();
}
@@ -1060,26 +1092,26 @@ class taskZen extends task
{
/* Set required fields. */
$requiredFields = $this->config->task->create->requiredFields;
- $execution = $this->loadModel('execution')->getById($executionID);
+ $execution = $this->loadModel('execution')->fetchById($executionID);
if($this->task->isNoStoryExecution($execution)) $requiredFields = str_replace(',story,', ',', ',' . $requiredFields . ',');
$requiredFields = array_filter(explode(',', $requiredFields));
+ $levels = array();
+ $project = $this->loadModel('project')->fetchById($execution->project);
+ $parentIdList = array_filter(array_column($tasks, 'parent', 'parent'));
+ $parents = $this->getParentEstStartedAndDeadline($parentIdList);
foreach($tasks as $rowIndex => $task)
{
- if(mb_strlen($task->name) > 255)
- {
- dao::$errors["name[$rowIndex]"] = sprintf($this->lang->task->error->length, 255);
- }
+ $levels[$task->level] = $rowIndex;
+
+ if(mb_strlen($task->name) > 255) dao::$errors["name[$rowIndex]"] = sprintf($this->lang->task->error->length, 255);
if(!empty($this->post->estimate[$rowIndex]) and !preg_match("/^[0-9]+(.[0-9]+)?$/", (string)$this->post->estimate[$rowIndex]))
{
dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->estimateNumber;
}
/* If the task start and end date must be between the execution start and end date, check if the task start and end date accord with the conditions. */
- if(!empty($this->config->limitTaskDate))
- {
- $this->task->checkEstStartedAndDeadline($executionID, (string)$task->estStarted, (string)$task->deadline);
- }
+ if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($executionID, (string)$task->estStarted, (string)$task->deadline);
/* Check start and end date. */
if(!helper::isZeroDate($task->deadline) && $task->deadline < $task->estStarted)
@@ -1087,24 +1119,21 @@ class taskZen extends task
dao::$errors["deadline[$rowIndex]"] = $this->lang->task->error->deadlineSmall;
}
+ $parentTask = isset($parents[$task->parent]) ? $parents[$task->parent] : null;
+ if($task->level > 0 && isset($levels[$task->level - 1])) $parentTask = zget($tasks, $levels[$task->level - 1], null);
+ $this->checkLegallyDate($task, $project->taskDateLimit == 'limit', $parentTask, $rowIndex);
+
/* Check if the estimate is positive. */
- if($task->estimate < 0)
- {
- dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->recordMinus;
- }
+ if($task->estimate < 0) dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->recordMinus;
/* Check if the required fields are empty. */
foreach($requiredFields as $field)
{
- if(empty($task->$field))
- {
- dao::$errors[$field . "[$rowIndex]"] = sprintf($this->lang->error->notempty, $this->lang->task->$field);
- }
+ if(empty($task->$field)) dao::$errors[$field . "[$rowIndex]"] = sprintf($this->lang->error->notempty, $this->lang->task->$field);
}
}
- if(dao::isError()) return false;
- return true;
+ return !dao::isError();
}
/**
@@ -1118,6 +1147,10 @@ class taskZen extends task
*/
protected function checkBatchEditTask(array $tasks, array $oldTasks): bool
{
+ $oldTask = reset($oldTasks);
+ $project = $this->loadModel('project')->fetchById($oldTask->project);
+ $parentIdList = array_filter(array_column($tasks, 'parent', 'parent'));
+ $parents = $this->getParentEstStartedAndDeadline($parentIdList);
foreach($tasks as $taskID => $task)
{
$oldTask = $oldTasks[$taskID];
@@ -1131,11 +1164,12 @@ class taskZen extends task
if($task->consumed < 0 ) dao::$errors["consumed[{$taskID}]"] = (array)sprintf($this->lang->task->error->recordMinus, $this->lang->task->consumedThisTime);
if($task->left < 0) dao::$errors["left[$taskID]"] = (array)sprintf($this->lang->task->error->recordMinus, $this->lang->task->leftAB);
- if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($oldTask->execution, (string)$task->estStarted, (string)$task->deadline, "task:{$taskID} ");
+ if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($oldTask->execution, (string)$task->estStarted, (string)$task->deadline, $taskID);
if($task->status == 'cancel') continue;
if($task->status == 'done' && !$task->consumed) dao::$errors["consumed[{$taskID}]"] = (array)sprintf($this->lang->error->notempty, $this->lang->task->consumedThisTime);
- if(!empty($task->deadline) && $task->estStarted > $task->deadline) dao::$errors["deadline[{$taskID}]"] = (array)$this->lang->task->error->deadlineSmall;
+
+ $this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null, $taskID);
}
return !dao::isError();
}
@@ -1485,11 +1519,11 @@ class taskZen extends task
unset($task->team);
}
- if($task->isParent)
+ if($task->isParent && strpos($task->name, "[{$this->lang->task->parentAB}]") === false)
{
$task->name = '[' . $this->lang->task->parentAB . '] ' . $task->name;
}
- elseif($task->parent > 0)
+ elseif($task->parent > 0 && strpos($task->name, "[{$this->lang->task->childrenAB}]") === false)
{
$task->name = '[' . $this->lang->task->childrenAB . '] ' . $task->name;
}
@@ -2047,4 +2081,39 @@ class taskZen extends task
return $options;
}
+
+ /**
+ * 获取父任务的开始时间和截止时间。
+ * Get the start and end time of the parent task.
+ *
+ * @param array $parentIdList
+ * @access protected
+ * @return array
+ */
+ protected function getParentEstStartedAndDeadline(array $parentIdList): array
+ {
+ $pathPairs = $this->dao->select('id,path')->from(TABLE_TASK)->where('id')->in($parentIdList)->fetchPairs();
+ if(empty($pathPairs)) return array();
+
+ $allParentIdList = array_filter(array_unique(explode(',', implode(',', $pathPairs))));
+ $allParents = $this->dao->select('id,estStarted,deadline')->from(TABLE_TASK)->where('id')->in($allParentIdList)->fetchAll('id');
+ $parents = array();
+ foreach($pathPairs as $parentID => $path)
+ {
+ $parent = new stdClass();
+ $parent->estStarted = null;
+ $parent->deadline = null;
+ foreach(array_reverse(array_filter(explode(',', $path))) as $taskID)
+ {
+ if(!isset($allParents[$taskID])) continue;
+
+ $task = $allParents[$taskID];
+ if(empty($parent->estStarted) && !helper::isZeroDate($task->estStarted)) $parent->estStarted = $task->estStarted;
+ if(empty($parent->deadline) && !helper::isZeroDate($task->deadline)) $parent->deadline = $task->deadline;
+ if(!empty($parent->estStarted) && !empty($parent->deadline)) break;
+ }
+ $parents[$parentID] = $parent;
+ }
+ return $parents;
+ }
}