U
    a\`f                    @   s\   d dl Zd dlmZ d dlT d dlmZmZ d dlZd dl	T d dl
mZ G dd deZdS )    N)tzutc)*)ROUND_UP
ROUND_DOWN)CmfGanttTaskc                       s  e Zd Zedd ZedddZdd Zdd	 Zg dfd
dZ fddZ	e
dd Ze
dd Ze
dd Ze
dd ZdddZdddZdd Zdd Zdd  Zdd!d"Zd#d$ Zdd%d&Zdd'd(Zdd)d*Zd+d, Zd-d. Zdd/d0Zd1d2 Zd3d4 Zd5d6 Zd7d8 Zd9d: Z d;d< Z!dd=d>Z"dd?d@Z#dAdB Z$dCdD Z%dEdF Z&ddGdHZ'dIdJ Z(ddKdLZ)dMdN Z*dOdP Z+ddQdRZ,dSdT Z-ddUdVZ.dWdX Z/dYdZ Z0d[d\ Z1d]d^ Z2d_d` Z3dadb Z4dcdd Z5ddedfZ6ddgdhZ7didj Z8dkdl Z9dmdn Z:ddodpZ;ddqdrZ<dsdt Z=ddudvZ>dwdx Z?dydz Z@d{d| ZAd}d~ ZBdd ZCdd ZDdd ZEdd ZFdddZGdd ZHdd ZIdd ZJdddZKdddd fdd
ZLdd fdd
ZM fddZN fddZOdddZPdd ZQ  ZRS )r   c                 C   s   t  }ddddddddd	d
dddg}t jjd ||d}|rR|D ]}|| ||< q@||_|  t  |jdddd W 5 Q R X |S )Nsched_start_datesched_finish_datesched_duration
sched_cost
sched_workconstrain_start_typeconstrain_start_dateconstrain_finish_typeconstrain_finish_dateprojectgantt_projecttaskparent_task)parentr   fieldsFT)emitnotify	only_data)modelsr   getr   fix_null_variablescmfutildisable_notifysave)clsr   baseline
gantt_taskr   op_gantt_taskfield r$   (./modules/gantt/models/cmf_gantt_task.py_create_gantt_task_for_baseline   s*          
z,CmfGanttTask._create_gantt_task_for_baselineNc              	   C   sf   t  }|r| ||S |j  |j|_||_|  |j|_t	  |j
ddd W 5 Q R X |S )NF)r   r   )r   r   r&   r   loadr   _calc_projectr   r   r   r   )r   r   r    r!   r$   r$   r%   create_gantt_task    s    

zCmfGanttTask.create_gantt_taskc                 C   s   | j r| j jr| j j| _d S N)r   r   selfr$   r$   r%   r(   6   s    zCmfGanttTask._calc_projectc                 C   s    | j r| j jS | jr| jjS d S r*   )r   r"   r   r+   r$   r$   r%   _get_parent_gantt_task;   s    z#CmfGanttTask._get_parent_gantt_taskc                 C   sN   |d kr|   }| jjdkr g S tjjdd| jgdd| jgg|g |d}|S )Ntask.gantt_projectr   ==r   filterr   )save_preload_fieldsr   logic_prefixr   r   listr   )r,   
add_filterr   Zchild_gantt_tasksr$   r$   r%   _get_children_gantt_tasksA   s    

z&CmfGanttTask._get_children_gantt_tasksc                    s0   ddddddddd	d
dddddg}t   | S )Nr   r   r   r   zgantt_project.op_gantt_taskr   zparent_task.gantt_projectzparent_task.op_gantt_taskztask.logic_prefixztask.parent_task.logic_prefixztask.has_child_tasksztask.plan_start_dateztask.plan_end_dateztask.responsibleztask.executors)superr2   )r,   r   	__class__r$   r%   r2   \   s"    z CmfGanttTask.save_preload_fieldsc                 C   s2   | j jrd S | j | j d }t|jtdtdS )Nd   z1.11Zrounding)r
   is_null_perform_completeDecimalquantizer   r,   resr$   r$   r%   _perform_costr   s    zCmfGanttTask._perform_costc                 C   s:   | j jrd S | j | j d }t|jtdtd}t|S Nr:   1r;   )r	   r<   r=   r>   r?   r   intr@   r$   r$   r%   _perform_durationy   s
    zCmfGanttTask._perform_durationc                 C   s:   | j jrd S | j | j d }t|jtdtd}t|S rC   )r   r<   r=   r>   r?   r   rE   r@   r$   r$   r%   _perform_work   s
    zCmfGanttTask._perform_workc                 C   s   | j js| jjrdS |  }ttjt|jp.dd}tj	|}| j |krRdS |
| j j| jj}t|dkr|
| j j|}|| d }nd}|dkrd}t|jtdtd}t|S )Nr   Zsecondsr:   rD   r;   )r   r<   r   _get_calendardttimezone	timedeltarE   datetimenowZget_work_timedeltavaluer>   r?   r   )r,   calendarZtzrN   Zdelta_finish_time_secZdelta_now_time_secZperform_completerA   r$   r$   r%   r=      s$    
zCmfGanttTask._perform_completeFc                 C   s   |sd S d}|}| j dkr*| jr*d}d}n| jrd}| jj}| jdkrd||krdd}|}d|d}nN| jdkr||krd}|}d	|d}n&| jd
kr||k rd}|}d|d}d| d|d}|r|sd| jj d| jj d| d}t|dd |S )NF0-constTu   невозможно изменить дату начала, если зафиксированы дата окончания и длительность (   Фиксированное начало %d.%m.%Y3-after   Начало не раньше 4-before   Начало не позже    ограничение "uE   " конфликтует с плановой датой начала <   Конфликт планирования: В задаче "" () .abort)	r   const_durationr   rO   r   r   namecode	cmf_alertr,   estimated_datereturn_correct_dateZalertZcorrect_datetext
constraintZconstrain_dater$   r$   r%   check_correct_start_date   s6     z%CmfGanttTask.check_correct_start_datec                 C   s   |sd S d}|}| j dkr*| jr*d}d}n| jrd}| jj}| jdkrd||krdd}|}d|d}nN| jdkr||krd}|}d	|d}n&| jd
kr||k rd}|}d|d}d| d|d}|r|sd| jj d| jj d| d}t|dd |S )NFrQ   Tu   невозможно изменить дату окончания, если зафиксированы дата начала и длительностьrR   .   Фиксированное окончание rT   1-after%   Окончание не раньше 2-before#   Окончание не позже rY   uK   " конфликтует с плановой датой окончания rZ   r[   r\   r]   r^   )	r   r`   r   rO   r   r   ra   rb   rc   rd   r$   r$   r%   check_correct_finish_date   s6     z&CmfGanttTask.check_correct_finish_datec                 C   s   d\}}}|   rB| jjdkrB|   }||   | \}}}|}| jdkrr| j}|rj|rjt||}n|pp|}|}| j	dkr| j
}|r|rt||}n|p|}d}| jr| j}n|}|||fS )u  
        Получает из родительской задачи ограничения
        по дате начала, дате окончания и максимальной длительности.

        Returns:
            start_limit: Defaults to None
                левая граница, за которую нельзя уйти влево (_plan_start_limit_min)
            finish_limit: Defaults to None
                правая граница, за которую нельзя уйти вправо (_plan_finish_limit_max)
            duration_limit: Defaults to None
                лимит длительности, нельзя превысить (_plan_duration_limit_max)
        NNNr.   rQ   rU   rQ   rm   N)r-   r   r3   load_fieldsr2   get_parent_plan_limitr   r   maxr   r   minr`   r	   )r,   Zparent_start_limitZparent_finish_limitZparent_duration_limitparent_gantt_taskstart_limitfinish_limitduration_limitr$   r$   r%   rt      s,    


z"CmfGanttTask.get_parent_plan_limitc           
      C   s@  d } }}| j jsdS |  D ]}|jdkrR|s>|jj}n|jrRt||jj}|jdkr|s|jj}|j	r|jrt
j| |jjt|j	 }|}nD|jrt||jj}|j	rt
j| |jjt|j	 }t||}|jdkr|s|jj}n|jrt||jj}|jdkr|s\|jj}|j	r|jrt
j| |jjt|j	}|}nF|jrt||jj}|j	rt
j| |jjt|j	}t||}|jr|s|j	}n|j	rt||j	}|j jr | \}}}	|s|}n|rt||}|s
|}n|rt||}|s&|	}q |	r t||	}q |||fS )u~  
        !!! Очень тяжелый метод

        Получает из дочерних задач ограничения
        по дате начала, дате окончания и максимальной длительности.

        Returns:
            start_limit: Defaults to None
                левая граница, за которую нельзя уйти вправо (_plan_start_limit_max)
            finish_limit: Defaults to None
                правая граница, за которую нельзя уйти влево (_plan_finish_limit_min)
            duration_limit: Defaults to None
                самая большая фикс. длительность или фикс. трудозатраты (пока нет поддержки ресурсов),
                нельзя меньше (_plan_duration_limit_min)
        Nrp   )rQ   rW   rr   )rQ   rk   rq   )r   has_child_tasksr6   r   r   rO   rv   r   r   r	   r   CmfCalendarget_date_by_durationrI   rE   ru   r`   get_childrens_plan_limit)
r,   rx   ry   rz   r!   r   r   Zch_start_limitZch_finish_limitZch_duration_limitr$   r$   r%   r~     s    









z%CmfGanttTask.get_childrens_plan_limitc                 C   sB   t | ddrd S d| _|  \| _| _| _|  \| _| _| _	d S )N_calc_plan_limits_cache_doneFT)
getattrr   rt   _plan_start_limit_min_plan_finish_limit_max_plan_duration_limit_maxr~   _plan_start_limit_max_plan_finish_limit_min_plan_duration_limit_minr+   r$   r$   r%   _calc_plan_limits_cache~  s    z$CmfGanttTask._calc_plan_limits_cachec                 C   sv   | j rD| jrDtjj| jdpd}tj|  | j j	| jj	| | _
n| j jrZ| jjrZd| _
| j
jrr| jd||d d S )N)r      r   T)from_calc_sched_durationfrom_child_changesfrom_sched_dates_changed)r   r   r   CmfTaskResAssigncountr   r|   get_duration_minutesrI   rO   r	   old
is_changed_do_sched_duration_is_changed)r,   r   r   Znumber_of_recourcesr$   r$   r%   _calc_sched_duration  s       z!CmfGanttTask._calc_sched_durationc                 C   sF   | j r,| jr,tj|  | j j| jj| _n| j jrB| jjrBd| _d S Nr   )	actual_start_dateactual_finish_dater   r|   r   rI   rO   actual_durationr   r+   r$   r$   r%   _calc_actual_duration  s      z"CmfGanttTask._calc_actual_durationc                 C   sR   t j|  | jjt| j }| j||d}| j	dkrN| j
rN| j
|krN| j
}|S )u  
        Расчитывает дату начала.
        При возникновении конфликтов ограничений, откатывает на дату ограничения при rollback=True

        Args:
            rollback (bool, optional): Defaults to False
                при конфликте откатить на дату ограничения

        Returns:
            sched_start_date:
                плановая дата начала
        re   rf   rk   )r   r|   r}   rI   r   rO   rE   r	   ri   r   r   )r,   rollbackre   r   r$   r$   r%   _calc_sched_start_date  s      

z#CmfGanttTask._calc_sched_start_datec                 C   s0   t j|  | jjt| j}| j||d}|S )u  
        Расчитывает дату окончания.
        При возникновении конфликтов ограничений, откатывает на дату ограничения при rollback=True

        Args:
            rollback (bool, optional): Defaults to False
                при конфликте откатить на дату ограничения

        Returns:
            sched_finish_date:
                плановая дата окончания
        r   )	r   r|   r}   rI   r   rO   rE   r	   ro   )r,   r   re   r   r$   r$   r%   _calc_sched_finish_date  s      z$CmfGanttTask._calc_sched_finish_datec                 C   s   | j js| j dkrd | _d S | jjr0| jjr0d S | jrZ| jsZ| j|d| _|rVd | _nd S | jr| js|  | _| jdkr|  | _d S | jdkr| jdkr| jrt	d d S )Nr   r   rk   rQ   u   Плановый период может не соответствовать плановой длительности, т.к. все переменные зафиксированы)
r	   r<   r   r   r   r   r   r   r`   rc   )r,   r   r$   r$   r%   _calc_sched_period  s"    


zCmfGanttTask._calc_sched_periodc                 C   s   | j js| j dkrd | _d S | jjr0| jjr0d S | jrZtj|  | jjt	| j | _d S | jrtj|  | jjt	| j  | _d S d S r   )
r   r<   r   r   r   r|   r}   rI   rO   rE   r+   r$   r$   r%   _calc_actual_period  s&        
z CmfGanttTask._calc_actual_periodc                 C   s   | j dkrf| jjr| jjr| jdkrt| j| j d }t|d}| jjsV|| jkr|| _| 	  nd| j dkr| jjr| j
jr| j
dkrt| j| j
 d }t|d}| jjs|| jkr|| _| 	  dS )uR   
        Вычисляет фактический % завершения
        0-workr   r:   5-timetrackerN)actual_complete_typeactual_workis_not_nullr   mathfloorrv   actual_completer<   $notify_parent_update_actual_completetimetracker_sched_work)r,   tmp_completer$   r$   r%   _calc_actual_complete  s    




z"CmfGanttTask._calc_actual_completec           
      C   s  | j jsdS | jdkrdS | jdkr(dS | jdkr| jdgd}|sHdS t|}d}|D ]6}|r~|j|jkr~||jjpxd7 }qX||jjpd7 }qX|| }n:| jdkr| j jd	krt	j
jd
ddgdd| j ggddgd}nt	j
jd
d| j gddgd}|sdS d}d}	|D ]}|rr|j|j jkrr|j ddg ||j jjpHd7 }|j jjdkr|	|j jjpld7 }	n2||jjpd7 }|jjdkr|	|jjpd7 }	q|	| d }n | jdkrdS | jdkrdS | j|k r|| _dS )uB   
        updated_ch_gantt_task - указан, когда
        Nr   r   
1-completer   )r   r   2-story_pointsr.   r   r/   r   agile_story_pointszstatus.status_typer0   =CLOSEDr:   3-costz4-fixed)r   r{   r   r6   lenidr   rO   r3   r   CmfTaskr4   rs   r   statusstatus_type)
r,   updated_ch_gantt_taskZch_gantt_task_listZtotal_countZsum_actual_completecZres_actual_completeZch_task_listZtotal_story_pointsZfinished_story_pointsr$   r$   r%   _calc_summarry_actual_complete*  s`    



z+CmfGanttTask._calc_summarry_actual_completec                 C   s  |    | jdkr(| js.| jr.| j| _nd | _| jdkrB| j| _n| jdkr| j}| jrl| jrlt| j| j}|r| jr| j|k r|| _nb| jdkr| j}| jr| jrt| j| j}|r| jr| j|kr|| _n| jr| j| _| jr| j| _| jr| jr| j| jkrtddd | jr>| jr>| j| jk r>tddd | j	j
r| jr`| j	j
| jk r| jrx| j	j
| jkr| js| j	j
| _n\| jdkrnN| jd	kr| j	j
| jk r| j	j
| _n&| jd
kr| j	j
| jkr| j	j
| _| jdk| _| jr| jjr|   | jdd d S )N)rQ   rU   rW   rQ   rU   rW   u~   Нельзя указать Дату начала больше зафиксированной у дочерней задачиTr^   u   Нельзя указать Дату начала меньше зафиксированной у родительской задачи)1-earlyrW   )z2-latterrU   r   )r   r   r   r   r   ru   r   rv   rc   r   Zplan_start_datelock_sched_start_dater   _do_sched_start_date_is_changedr   )r,   r   r$   r$   r%   #_do_constrain_start_date_is_changedn  sd    







z0CmfGanttTask._do_constrain_start_date_is_changedc                 C   s   g }| j jdkrd S |  r<|  }||   || dd| j gdddgg}tjj|dgd}|rdd	 |D }d
d| j	gdd|gg}tj
j||  d}|| |S )Nr.   out_linkr   relation_type.codezsystem.additional_parentin_linkr0   c                 S   s   g | ]}|j jjqS r$   )r   rO   r   ).0Zrelr$   r$   r%   
<listcomp>  s     z8CmfGanttTask._get_parent_gantt_tasks.<locals>.<listcomp>r   Ztask_idIN)r   r3   r-   rs   r2   appendr   CmfRelationOptionr4   r   r   extend)r,   parent_gantt_tasksrw   _filterZadditional_parents_relationsZadditional_parents_tasks_idsZadditional_parents_filterZadditional_parentsr$   r$   r%   _get_parent_gantt_tasks  s.    



z$CmfGanttTask._get_parent_gantt_tasksc                 C   s6   d }|  ddg | jr | jj}|p4tjjddgdS )Nzproject.calendarzproject.calendar.timezonezsystem:defaultrK   )rb   r   )rs   r   rP   r   r|   r   )r,   rP   r$   r$   r%   rI     s
    zCmfGanttTask._get_calendarc                 C   s  d }d }dddg}| j |d}|D ]}|  |j| jjdd|_d |_|jjrZ|  n|j	dd |
  |jdd |s|jj}nt||jj}|jr|s|jj}nt||jj}| j|d | j|d q"| jr| jr| jrd | _| 	  n"|r|| _|r
|| _| jdd	 d S )
Nr`   !=Tr5   r   r   r   re   r   )r6   r   ri   r   newr   r   r{   "_calc_sched_period_for_child_tasksr   _alert_changed_plan_fieldsr   rO   rv   ru   ro   r`   r	   r   )r,   r   r   r5   child_tasks
child_taskr$   r$   r%   r     sB    




z/CmfGanttTask._calc_sched_period_for_child_tasksc                 C   s   | j | jjd |   | jjrt| jjrt| jrj| j| jk rj| j| _td| jj d| jj	 d| jdd | 
  n:| jr| jr| jsd | _|   n| jdd | jd	d
 d S )Nr   u/   Предупреждение: В задаче "r[   u2   ) дата начала перемещена на rT   ub   , так как была позже даты ограничения дочерней задачи.Tr   
start_datechanged_field)ri   r   r   r   r   r{   r   rc   ra   rb   r   r	   lock_sched_finish_dater   r   r   _calc_resourcesr+   r$   r$   r%   r     s    (

z,CmfGanttTask._do_sched_start_date_is_changedc                 C   sv   | j | jjd |   | jrR| jr*| jrR|   | jj	r^| jj
r^| jr^|   n| jdd | jj	sr| jdd d S )Nr   Tr   Zfinish_dater   )ro   r   r   r   r	   r   r`   r   r   r{   r   r   r   r   r+   r$   r$   r%    _do_sched_finish_date_is_changed  s    
z-CmfGanttTask._do_sched_finish_date_is_changedc              
   C   s  | j rf|rb|jr"| jr"|j| jk s:|jrb| jrb|j| jkrbd}t||jj|jj| jj| jj dS |dkrtt }| j	j
|krdS || j	j
 d}d}|s|nd}|s|nd}ddddgdddgg}	| j|	d}
|
D ]z}|r|j	|j	krq|jr"|s|}n |js|}n|j|jkr"|}|jr|s4|}q|jsB|}q|j|jk r|}q|r`|j}|rl|j}|s|r|jr|js|j|k r|j}|s|r|jr|js|j|kr|j}d}d}| jrp|rpd|jj d	|jj d
}| jj
}| jdkr$||kr$d|d}nL| jdkrH||k rHd|d}n(| jdkrl||krld|d}nd}d}| jr|rd|jj d	|jj d
}| jj
}| jdkr||krd|d}nL| jdkr||k rd|d}n(| jdkr||krd|d}nd}|rR|p"|}td| jj d	| jj d| d| d	dd || _|| _| jjsr| jjr~| jdd |s|r| j|jk r|j| _| jdd |  }|rt F |D ]:}||   |j| ||d |  |jddd  qW 5 Q R X dS )!u  
        Обновляет плановые даты родительской задачи.
        Плановая дата начала = самая ранняя плановая дата начала всех подзадач
        Плановая дата окончания = самая поздняя плановая дата окончания всех подзадач

        Args:
            child_task (optional): Defaults to None
                измененная дочерняя задача, которая еще не сохранена.
            ignore_child_task (bool, optional): Defaults to False
                игнорировать дочернюю задачу при обновлении дат родительской задачи.
        u   Задача "{}" ({}) вышла за диапазон плановых дат родительской задачи "{}" ({})NORr   r   r   r   "r[   )rQ   rS   rT   rU   rV   rW   rX   rj   rk   rl   rm   rn   uU   Конфликт планирования: В родительской задаче "u   ) ограничение "u<   " конфликтует с дочерней задачей r]   Tr^   r   ignore_child_taskpathF
user_inputr   )r`   r   r   rc   formatr   ra   rb   setr   rO   addr6   r   r   r   r   r   r   r	   r   r   r   disable_aclrs   r2   from_sched_dates_of_child_tasksr   r   )r,   r   r   r   msgZmin_start_dateZmax_finish_datesZchild_task_with_min_startZchild_task_with_max_finishr5   Zgantt_tasksr!   rh   Zconflict_child_task_min_startr   Zconflict_child_task_max_finishr   Zconflict_child_taskr   rw   r$   r$   r%   r   (  s    

$
&
z,CmfGanttTask.from_sched_dates_of_child_tasksc              	   C   st   |   }|rpt V t }|| jj |D ]6}|jj|kr.|j| ||d |  |j	ddd q.W 5 Q R X dS )uq  
        Обновление дат начала и окончания родительской задачи и доп родительских задач.
        Передает дочернюю задачу (self), так как она еще не сохранена
        и родительская задача не может получить обновленные данные.

        Args:
            ignore_child_task (bool, optional): Defaults to False
                игнорировать дочернюю задачу при обновлении дат родительской задачи.
        r   FTr   N)
r   r   r   r   r   r   rO   r   r   r   )r,   r   r   r   r!   r$   r$   r%   update_parent_task_sched_dates  s    

z+CmfGanttTask.update_parent_task_sched_datesc                 C   s   |   }|D ]}|jdkr$td q| | r|| rD| | || k r|jdkrt|| rt| | || krttd  d S |jdkr|| r| | || k rtd  d S | | ||< |  |j|d qd S )NrQ   ^   Дата родительской задачи является зафиксированнойrW   u   Предупреждение: Плановая дата начала дочерней задачи больше плановой даты начала родительской задачиrU   u   Предупреждение: Плановая дата начала дочерней задачи меньше плановой даты начала родительской задачи)
date_field)r   r   rc   r   _change_parent_start_date)r,   r   parentsrw   r$   r$   r%   r     s&    
 z&CmfGanttTask._change_parent_start_datec                 C   s   |   }|D ]}|jdkr$td q| | r|| rD| | || kr|jdkrt|| rt| | || k rttd  d S | | }|jdkr|| r| | || krtd |jr||jkr|j}|||< |  || q| jdk| _d S )NrQ   r   rk   u   Предупреждение: Плановая дата окончания дочерней задачи меньше плановой даты окончания родительской задачиrm   u   Предупреждение: Плановая дата окончания дочерней задачи больше плановой даты окончания родительской задачи)r   r   rc   r   r   _change_parent_finish_dater   r   )r,   r   r   rw   Z
date_valuer$   r$   r%   r     s,    
 z'CmfGanttTask._change_parent_finish_datec                 C   s\  |    | jdkr(| js.| jr.| j| _nd | _| jdkrB| j| _n| jdkrx| j}| jrl| jrlt| j| j}|r|| _nb| jdkr| j}| jr| jrt| j| j}|r| jr| j|kr|| _n| jr| j| _| j	r| j	| _| jr&| j
r| j| j
krtddd | jr&| j| jk r&tddd | jjr| j
rH| jj| j
k r| jr`| jj| jkr| jst| jj| _n\| jdkrnN| jdkr| jj| jkr| jj| _n&| jdkr| jj| jk r| jj| _| jdk| _| jr| jjr| jr| jdkr
| jsd | _|   | jrL| jjrL| jdkr>| jsDd | _|   | jdd	 d S )
N)rQ   rk   rm   rQ   rk   rm   u   Нельзя указать Дату окончания больше зафиксированной у родительской задачиTr^   u   Нельзя указать Дату окончания меньше зафиксированной у дочерней задачиr   )r   r   r   r   r   ru   rv   r   r   r   r   rc   r   Zplan_end_dater   r   r	   r   r   r   r   r   )r,   r   r$   r$   r%   $_do_constrain_finish_date_is_changed  sr    







z1CmfGanttTask._do_constrain_finish_date_is_changedc                 C   s6  | j dkrB| jdkrB| jjsB| jjsB|r6tddd ntddd |   | jrh| j| jkrhtddd | j	r| j| j	k rtddd |s2|s| j
rd | _| jdd |s| jr| js| jr| j| _| jd||d	 | jr| jr| j| jk rtd
| jj d| jj ddd |s2|s2| jdd d S )NrQ   u  Конфликт планирования: невозможно изменить трудозатраты, если установлена опция "Фиксированные Ресурсы" и зафиксированы даты начала и окончания.Tr^   u   Конфликт планирования: невозможно изменить длительность, если зафиксированы даты начала и окончания.u   Конфликт планирования: невозможно установить длительность больше зафиксированной у родительской задачи.u   Конфликт планирования: невозможно установить длительность меньше зафиксированной у дочерней задачи.r   )from_sched_duration_changedr   r      В задаче "r[   u   ) невозможно указать Длительность меньше чем Трудозатраты, когда установлена опция Фиксированные Ресурсы и Трудозатраты зафиксированы.durationr   )r   r   r   r   r   rc   r   r   r	   r   r   r   r   
const_workconst_resourcer   _do_sched_work_is_changedr   ra   rb   r   )r,   from_sched_work_changedr   r   r   from_calc_resourcesr$   r$   r%   r   I  sN    z*CmfGanttTask._do_sched_duration_is_changedc                 C   s   |    d S r*   )r   r+   r$   r$   r%   _do_actual_duration_is_changeds  s    z+CmfGanttTask._do_actual_duration_is_changedc              	   C   s   | j jrd S | j dk r"tddd | jjs>| jjdkr>| j | _|   | j jpPd| j j	pZd }| 
 r| jjdkrt 2 | 
 }||   || |jdd W 5 Q R X |s| jjrtj| j| d S )Nr   uo   "Фактические трудозатраты" стал ниже нуля, выставляем в ноль.Tr^   r.   Fr   )r   r<   rc   r   r{   r3   actual_myself_workr   r   r   r-   r   r   rs   r2   from_child_add_workr   r   r   ZCmfTimeTrackerZ"_op_gantt_actual_work_changed_hook)r,   from_timetracker
work_deltarw   r$   r$   r%   _do_actual_work_is_changedv  s     


z'CmfGanttTask._do_actual_work_is_changedc              	   C   s   | j jrd S | j dk r"tddd | jdkr~| jjr~| jdkr~t| j | j d }t|d}| j	jsp|| j	kr~|| _	| 
  |  r| jjdkr| j jpd| j jpd }t 2 |  }||   || |jdd	 W 5 Q R X d S )
Nr   ue   "Фактические Затраты" стал ниже нуля, выставляем в ноль.Tr^   r   r:   r.   Fr   )actual_costr<   rc   r   r
   r   r   r   rv   r   r   r-   r   r3   r   r   r   r   rs   r2   from_child_add_costr   )r,   r   
cost_deltarw   r$   r$   r%   _do_actual_cost_is_changed  s$    




z'CmfGanttTask._do_actual_cost_is_changedc                 C   s,   | j jrd S | jdkrd S | jdkr(d S d S )Nr   r   )r   r<   r   r+   r$   r$   r%   _do_actual_complete_is_changed  s    

z+CmfGanttTask._do_actual_complete_is_changedc                 C   sH   | j jp
d| j jpd }| jjs*| j | _n|  j|7  _| j|d d S )Nr   r   )r   r   r   r   r{   r   r   )r,   r   r   r$   r$   r%   !_do_actual_myself_work_is_changed  s
    
z.CmfGanttTask._do_actual_myself_work_is_changedc                 C   sD   | j jp
d| j jpd }| jjs*| j | _n|  j|7  _|   d S r   )actual_myself_costr   r   r   r{   r   r  r,   r   r$   r$   r%   !_do_actual_myself_cost_is_changed  s
    
z.CmfGanttTask._do_actual_myself_cost_is_changedc           	   	   C   s  | j jrd S | j| j kr(| j | _|   | jjr| j | jk r| jd  d| jd  d}| j d  d| j d  d}| jrtd| d| ddd n| j| _ |std	| d
 nt|s(| j r(|s(| j	s| j
r| j | _| jdd | j
r(| j	r(| j| j k r(td| jj d| jj ddd |   |  r| jjdkr| j jpTd| j jp`d }t < |  }||   || |  |jddd W 5 Q R X |s|s|s| jdd d S )N<      ч    мu   Нельзя запланировать меньше Трудозатрат, чем запланировано в подзадачах ( < ub   ). Увеличьте Трудозатраты или уменьшите их у подзадач.Tr^   u   DEV: Указано Трудозатрат меньше, чем запланировано в подзадачах. Выставили uo   . Если нужно уменьшить Трудозатраты, уменьшите их у подзадач.)r   r   r[   u   ) нельзя указать Трудозатраты больше чем Длительность, когда установлена опция Фиксированные Ресурсы и Длительность зафиксирована.r.   r   Fr   workr   )r   r<   r   %_do_timetracker_sched_work_is_changedr   r{   agregat_workr   rc   r`   r   r	   r   ra   rb   r   r-   r3   r   r   r   r   rs   r2   from_child_agregate_workr   r   r   )	r,   r   r   r   r   r  r   r   rw   r$   r$   r%   r     sF    

z&CmfGanttTask._do_sched_work_is_changedc              	   C   s   | j jrd S | j | jk rX| jr>td| j  d| j ddd n| j| _ td| j d |  r| jjdkr| j jpvd	| j j	pd	 }t
 < |  }||   || |  |jd
dd W 5 Q R X d S )Nu   Нельзя запланировать меньше Расходов, чем запланировано в подзадачах (r  uX   ). Увеличьте Расходы или уменьшите их у подзадач.Tr^   u|   Указано Расходов меньше, чем запланировано в подзадачах. Выставили ue   . Если нужно уменьшить Расходы, уменьшите их у подзадач.r.   r   Fr   )r
   r<   agregat_cost
const_costrc   r-   r   r3   r   r   r   r   rs   r2   from_child_agregate_costr   r   r,   r   rw   r$   r$   r%   _do_sched_cost_is_changed  s"    

z&CmfGanttTask._do_sched_cost_is_changedc                 C   s"   | j r| jr|   n|   d S r*   )r   r   r   r   r+   r$   r$   r%    _do_actual_start_date_is_changed&  s    
z-CmfGanttTask._do_actual_start_date_is_changedc                 C   s(   | j s| jr| jr|   n|   d S r*   )r   r   r   r   r   r+   r$   r$   r%   !_do_actual_finish_date_is_changed,  s    
z.CmfGanttTask._do_actual_finish_date_is_changedc                 C   sZ   | j dkr|   nB| j dkr0| jjr0|   n&| j dkrD|   n| j dkrV|   d S )Nr   r   r   r   r   )r   r   r   r{   r   r  r  r+   r$   r$   r%   #_do_actual_complete_type_is_changed2  s    





z0CmfGanttTask._do_actual_complete_type_is_changedc                 C   s   | j r|   d S r*   )r`   r   r+   r$   r$   r%   _do_const_duration_is_changed@  s    z*CmfGanttTask._do_const_duration_is_changedc                 C   s*   | j rd| _| j| _n| jj| _d | _d S NrQ   )r   r   r   r   defaultr+   r$   r$   r%   $_do_lock_sched_start_date_is_changedF  s
    

z1CmfGanttTask._do_lock_sched_start_date_is_changedc                 C   s*   | j rd| _| j| _n| jj| _d | _d S r  )r   r   r   r   r  r+   r$   r$   r%   %_do_lock_sched_finish_date_is_changedN  s
    

z2CmfGanttTask._do_lock_sched_finish_date_is_changedc                 C   s   |sd S |  ddddg |j }| |   || j || j || j	 |
| j |jr| j|jjdd| _| jjr|   n
||  |jdd d S )Nr
   r   r   r   Tr   r   )rs   r"   r'   r2   r  r
   r  r   r   r   r   r   r`   ri   r   rO   r   r   r   r   r,   r   rw   r$   r$   r%   add_child_task_to_parent_taskV  s$    


z*CmfGanttTask.add_child_task_to_parent_taskc                 C   s   |sd S |  ddddg |j }| |   || j  || j  || j	  |
| j  |j| dd |jdd d S )Nr
   r   r   r   T)r   r   )rs   r"   r'   r2   r  r
   r  r   r   r   r   r   r   r   r  r$   r$   r%   "remove_child_task_from_parent_taskn  s    
z/CmfGanttTask.remove_child_task_from_parent_taskc              	   C   sP   t  > | jjp| j}|r&| | | jjp2| j}|rB| | W 5 Q R X d S r*   )r   r   r   r   r   r   r   r  )r,   Z
old_parentZ
new_parentr$   r$   r%   _do_parent_task_is_changed|  s    

z'CmfGanttTask._do_parent_task_is_changedc                 O   s   t jj| df|| d S )Nupdated)r   ZCmfEventZdo_event)r,   argskwargsr$   r$   r%   _do_event_save  s    zCmfGanttTask._do_event_savec                 O   s
   | | j S r*   )Z	log_level)r,   
field_namer#  r$  r$   r$   r%   _get_field_log_level  s    z!CmfGanttTask._get_field_log_levelc                 C   s  | j rTd| _d| _|  rP|  }|j }|rBt|tj	 | _d| _
|   d S | j
jrbd| _
| jjrv|   d S | jjr|   d S | jjr|   d S | jjr|   |s|   d S | jjr|   d S | jjr|   d S | jjr|   d S | jjr|   d S | jjr4|   |s0|   d S | jjrJ|    d S | j!jr`| "  d S | j#jrv| $  d S | j%jr| &  d S | jjr| '  |s|   d S | j(jr| )  |s|   d S | j*js| j
jr| +  |s|   d S | j,js| j-jr0| .  |s,|   d S | j/jrF| 0  d S | j1jr\| 2  d S | j3jrn| 4  | j5jr| 6  d S )Nr   r   )7is_newr	   r   r-   r   r'   ru   rJ   rM   Zutcnowr   r   r<   r   r   r!  r   r  r  r  r   r   r   r   r   r   r   r  r   r  r   r
   r  r   r  r   r  r   r  r   r   r   r   r   r   r   r   r`   r  r   r  r   r  r   r  )r,   from_parent_changesrw   Zparent_sched_start_dater$   r$   r%   _save  s    












zCmfGanttTask._savec                 C   s8  |sd S | j jrd S | jsd S td| j  | jj}| jj}| jj}|}d}|s\|s\d S |sd|}|sl|}| j	j}| j
j}	| jj}
|d krtjjdd| j gdddgd}t|}|dkrd S |	r|d	kr|| | }nR|
r|d
kr|| | }n|dks|d	kr*|| }n|r*|d	kr*|| }|}d}|dkrL|| }|| }g }g }i }|D ]}dd|jgdd|jgdddgg}tjj|dD ]"}t  |  W 5 Q R X q|}|dkr||7 }d}d }d }|jj| dD ]
}|jj}d}|| kr||}|jj| }|dkr,qt|| |k rJt|| n|}||}||| pt|jddd}tj|j| j ||d|dd}t  |  W 5 Q R X |s|}||d}||kr|||< ||8 }|dkr|} qq| | | | | |_!| |_"t|d |_#t  |  W 5 Q R X q\t$|| _t%|| _&tt'|( | | _|| _|	r|dkr"|| jjkr"| j)dd nz|
r|dkr"|| jjkr"| j*dd nN|r"|dkr|| jjkr| j)dd |d	kr"|| jjkr"| j*dd td| j  d S )Nz&_calc_resources start. gantt_task_id: r   r   r/   personrP   r0   r   r   r  units	cmf_ownerhistory_typeZplanr1   )r      ;   )hourminute)r-  r   r   Zend_dater.  Z
time_spentremaining_estimater:   )r,  r  T)r   )r,  r   z'_calc_resources finish. gantt_task_id: )+r   r{   r   gdebugr   rO   r	   r   r   r   r`   r   r   r4   r   r+  r   ZCmfTimeTrackerHistoryr   r   deleterP   Z	work_daysdateZget_work_time_shiftZinterval_total_minutesrE   Zget_shifted_work_timereplacer   r   r   Z
date_startZdate_endr,  rv   ru   r   sumvaluesr   r   )r,   r   assignmentsr   r   r  Zinitial_workr,  r   r   r`   Znumber_of_resourcesZwork_per_personZwork_remainderZstart_timesZ	end_timesZmax_work_per_dayZ
assignmentr   ZtthZ	work_leftZ
start_timeZend_timeZdayZcur_dateZ
time_shiftZ
avail_timeZwork_for_todayZtth_start_dateZtth_end_dateZcur_max_work_for_todayr$   r$   r%   r     s    









"
	














zCmfGanttTask._calc_resourcesc                 C   s4   |  |   |   | jd|d | jdd d S )Nr,  )r   r<  Tr   )rs   r2   r   r   r   )r,   r<  r$   r$   r%   task_resources_changed_hook  s    z(CmfGanttTask.task_resources_changed_hookc                 C   sT   |  |   |   |  j|7  _|d kr8| j| | _| jdd | jdd d S )NTr  r   )rs   r2   r   r   r   r  r   )r,   r   r4  r$   r$   r%   timetracker_add_work_hook  s    	z&CmfGanttTask.timetracker_add_work_hookc                 C   sT   |  |  dg  | jrP| jj| _| jsD| | jj | | j | jdd d S )Nr.   Tr   )	rs   r2   r   r   r   r   r   r  r   r+   r$   r$   r%   task_parent_is_changed_hook  s    
z(CmfGanttTask.task_parent_is_changed_hookc                 C   s   |    d S r*   )r   r+   r$   r$   r%   r    s    z2CmfGanttTask._do_timetracker_sched_work_is_changedc                 C   s(  |rL|j dkrL| jsL| |   |   | j  |   | jdd d S |r|j dkr|r|j dkr| |   |   | j  d| _	| 
  |   | jdd |   d S |r$|j dkr$|r$|j dkr$| |   |   d | _d| _	| 
  |   | jdd |   d S d S )NZIN_PROGRESSTr   r   r:   r   )r   r   rs   r2   r   Zset_nowr  r   r   r   r  r  r   )r,   Z
old_statusZ
new_statusr$   r$   r%   task_status_is_changed_hook  sF    

z(CmfGanttTask.task_status_is_changed_hookc                 C   sP   |  |   | js tddd |   |   |   |   | jdd dS )u   
        В задаче изменились Agile-плановые даты, пересчитаем свои sched-даты, вдруг новые ограничения повлияют на них
        u?   У гант-задачи нет ссылки на задачу.Tr^   r   N)	rs   r2   r   rc   r   r   r   check_gantt_projectr   r+   r$   r$   r%   task_plan_dates_is_changed_hook  s    z,CmfGanttTask.task_plan_dates_is_changed_hookc                 C   s    | j jrd| _ |  j |7  _ d S r   )r   r<   )r,   r   r$   r$   r%   r     s    z CmfGanttTask.from_child_add_workc                 C   s    | j jrd| _ |  j |7  _ d S r   )r   r<   r  r$   r$   r%   r     s    z CmfGanttTask.from_child_add_costc              	   C   s(  | j jrd| _ |  j |7  _ | jsD| j | _| jjp4d| jjp>d }n~| j | jks| j d  d| j d  d}| jd  d| jd  d}td| d| d| j dd	d
 | j jpd| j jpd }|  r$| jj	dkr$t
 < |  }||   || |  |jdd	d W 5 Q R X d S )Nr   r  r	  r
  u;   Превысили План на Трудозатраты ( <= +   ) в родительской задаче ub   . Увеличьте в ней план или введите меньше Трудозатрат.Tr^   r.   Fr   )r  r<   r   r   r   r   rc   r   r-   r3   r   r   rs   r2   r  r   r   )r,   r   r  r   rw   r$   r$   r%   r    s(    

z%CmfGanttTask.from_child_agregate_workc              	   C   s   | j jrd| _ |  j |7  _ | js,| j | _n2| j | jks^td| j  d| j d| j ddd |  r| jjdkr| j jp|d| j j	pd }t
 2 |  }||   || |jd	d
 W 5 Q R X d S )Nr   u1   Превысили План на Расходы (rC  rD  u[   . Увеличьте в ней план или введите меньше РасходовTr^   r.   Fr   )r  r<   r  r
   rc   r   r-   r3   r   r   r   r   rs   r2   r  r   r  r$   r$   r%   r    s     


z%CmfGanttTask.from_child_agregate_costc                 C   s   |  | dS )u   
        Оповестить родителя, что ему нужно пересчитать свой агрегат actual_complete
        N)r   )r,   r   r$   r$   r%   !from_child_update_actual_complete  s    z.CmfGanttTask.from_child_update_actual_completec              	   C   sP   |   sdS t 2 |   }||   ||  |jdd W 5 Q R X dS )uP  
        Оповещение родителя.
        Передаем себя, т.к. мы еще можем быть не сохранены и родитель не сможет получить наши данные
        Другой вариант: опочещать через отдельный таск в celery
        NFr   )r-   r   r   rs   r2   rE  r   )r,   rw   r$   r$   r%   r     s    

z1CmfGanttTask.notify_parent_update_actual_completec                 C   s   | j s^| jr^dd | jddD }|ddddh }|r^| jj  | jjr^| jjjd	| jd
 | jjrz| jjrzt	ddd | j
jr| jjrt	ddd | jjs| jjr| jr| jr| j| jk rt	ddd | jjr| jjs| jdkrt	ddd d S )Nc                 S   s   h | ]\}}|qS r$   r$   )r   k_r$   r$   r%   	<setcomp>+  s     z0CmfGanttTask.check_user_input.<locals>.<setcomp>Tr   r   Zparent_task_idr   Zgantt_project_idzPPP-TSK-SCHEDULE)obju   Нельзя менять поле "Фактические трудозатраты" у Summary-задач. Используйте поле "Фактические трудозатраты по собственным ресурсам задачи"r^   u   Нельзя менять поле "Фактические Затраты" у Summary-задач. Используйте поле "Фактические Затраты по собственным ресурсам задачи"u\   Нельзя выставить Дату начала позже Даты окончанияr  u   Этот вариант рассчета процента завершения можно использовать только у Summary-задач)r(  r   itemsr   r   r'   Zcheck_project_role_accessr   r{   rc   r   r   r   r   )r,   Zchanged_fieldsr$   r$   r%   check_user_input)  s0    zCmfGanttTask.check_user_inputc                 C   s   | j jrd| _ | jjrd| _| jjr*d| _| jjr8d| _| jjrFd| _| jjrTd| _| jjrbd| _| jjrpd| _| j	jr~d| _	| j
jrd| _
| jjrd| _| jjrd| _d S r   )r   r<   r  r
   r   r	   r   r  r   r   r   Zconstrain_slackZconstrain_slack_pctr+   r$   r$   r%   r   B  s0    zCmfGanttTask.fix_null_variablesr   c                 C   sB  t d| j d|  | jjdkr(d S | js>|sR|sR| jsR| jsR| jsR| j	r>| j
  | jj| _| jjdd |s| jjr|  }|D ]4}|jr|j| jkr|jd|d d |jdd q|s*|  r*|  }||   |jjdkr*|jr|j| jkr*|jd|d d |jdd | js>| | j d S )	Nz!check_gantt_project: gantt_task: z	, level: r.   Tr   r   )r)  level)r   rM  )r5  r6  r   r   r3   r   r   r   r	   r   Z_calc_gantt_projectr   r{   r6   rA  r-   rs   r2   r   r  )r,   r)  r   rM  r   r   rw   r$   r$   r%   rA  ]  sJ    

z CmfGanttTask.check_gantt_projectT)r   r)  from_sort_orderc                   s   |r|    | j|| | j|d | jjr:| jjdd |   |   t j||}|s| j	jrdd| jgdddgg}t
jj|drtt
jj| jjgd	 |S )
N)r)  Tr   r   r/   r   zsystem.finish:startr/  )r#  )rL  r%  r*  r   r   r   rA  r   r7   r   r   r   r   Zschedule_deferred_jobZsort_task_by_order_relationsr   )r,   r   r)  rN  r#  r$  rA   r   r8   r$   r%   r     s    zCmfGanttTask.save)TEXKOM_db_deletec                   s   |  dddg |rVt| j tjj| jddD ] }t| d |_|jdd q2n&| j	r|| j	j| kr|| j	j
s|tddd | j	r| j	j| kr| | j t j|d	|i|S )
Nztask.op_gantt_taskztask.cmf_deletedr   T)r"   Zinclude_deletedr   uY   Нельзя удалять Оперативную гант задачу у задачи!r^   rO  )rs   r5  r6  r   r   r   r4   r"   r   r   Zcmf_deletedrc   r   r   r7   r7  )r,   rO  r#  r$  r   r8   r$   r%   r7    s    
zCmfGanttTask.deletec                    sB   t  jf | | jrd S td tj| jdd td d S )N_CmfAutomationCrudTrigger startupdateZbefore_save_CmfAutomationCrudTrigger end)	r7   before_save_hookr(  r5  r6  r   CmfAutomationCrudTrigger	crud_hookr   r,   r$  r8   r$   r%   rS    s    
zCmfGanttTask.before_save_hookc                    sB   t  jf | | jrd S td tj| jdd td d S )NrP  rQ  Z
after_saverR  )	r7   before_save_data_hookr(  r5  r6  r   rT  rU  r   rV  r8   r$   r%   rW    s    
z"CmfGanttTask.before_save_data_hookrQ  c              	   C   sv   | j   tjr| j sd S tjtjjjdg|| jj| jddtj| j jjd}t	d|d  ||d |d gd d S )Nproject_notifyTrI  )ZinitiatorSessionTabIdZinitiatorCurrentPersonactionZinitiatorActionZinitiatorObjIdZinitiatorObjChangedFieldsu   initiatorСomponentId	projectIdzproject_notify-rZ  )ZroomZevent_persons)
r   r'   r5  Zsession_tab_idZcurrent_personr   rO   keysZcomponent_idZcmf_emit_event)r,   Zinitiator_actionZeventr$   r$   r%   rX    s     

zCmfGanttTask.project_notifyc                 C   s  | j jdkrd S ddddd}d }| D ]}t| |}|jr*|jrd }d }|dkrd}|jr|jd	 |jd	  }}|d
d|d
d}|jd	 |jd	  }	}
|	d
d|
d
d}|dkrt|s|  }|j}|r|t	 }|t
j|jjd }|jt	 }|t
j|jjd }t|jjd	 d	\}}d|dd|d
}|d d| }|rp|d d| nd}td| j j d| j j d||  d|p|j d|p|j d q*|js*|jr*td| j j d| j j d||  d q*d S )Nr.   u)   Плановая длительностьu)   Плановые трудозатратыu&   Плановая дата началаu,   Плановая дата окончания)r	   r   r   r   )r	   r   u   Нетr  Z02dr	  u   мин)r   r   rH   zUTC z+03d:z%Y.%m.%d, %H:%M u   DEV: У задачи z (u   ) изменено поле "z":                        c u    на r]   u.   ) сброшено значение поля "z".)r   r3   r[  r   r   r   r   rI   Z
astimezoner   rJ   rL   rK   rO   divmodstrftimerc   ra   rb   )r,   r   rP   r&  r#   Znew_value_strZold_value_strZold_hourZ
old_minuteZnew_hourZ
new_minuteZdate_oldZdate_newr2  r3  Ztime_suffixr$   r$   r%   r     sR    

"

z'CmfGanttTask._alert_changed_plan_fields)N)NF)NF)FF)F)F)F)N)NFN)F)FFFFF)F)F)FFFF)N)N)F)NN)N)N)FFr   )rQ  )S__name__
__module____qualname__classmethodr&   r)   r(   r-   r6   r2   propertyrB   rF   rG   r=   ri   ro   rt   r~   r   r   r   r   r   r   r   r   r   r   r   rI   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r   r  r  r  r  r  r  r  r  r   r!  r%  r'  r*  r   r=  r>  r?  r  r@  rB  r   r   r  r  rE  r   rL  r   rA  r   r7  rS  rW  rX  r   __classcell__r$   r$   r8   r%   r      s   





&
'+_

	


%
D>	1
 
F     
*
#


    
:


^
 
'

) 


r   )rM   rJ   Zdateutil.tzr   Zcmf.includedecimalr   r   r   Z
cmf.fieldsZ#modules.gantt.fields.cmf_gantt_taskr   r$   r$   r$   r%   <module>   s   