U
    dg?                    @   s\   d dl Zd dlmZ d dlmZmZ d dlZd dlT 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dZejdg Zedd ZedddZd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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/d0Zd1d2 Zd3d4 Zdd5d6Z d7d8 Z!d9d: Z"d;d< Z#d=d> Z$d?d@ Z%dAdB Z&e'e(ddCgdDdEdFdG Z)ddHdIZ*ddJdKZ+dLdM Z,dNdO Z-dPdQ Z.ddRdSZ/dTdU Z0ddVdWZ1dXdY Z2dZd[ Z3dd\d]Z4d^d_ Z5dd`daZ6dbdc Z7ddde Z8dfdg Z9dhdi Z:djdk Z;dldm Z<dndo Z=dpdq Z>ddrdsZ?ddtduZ@dvdw ZAdxdy ZBdzd{ ZCdd|d}ZDdd~dZEdd ZFdd ZGdddZHdd ZIdd ZJdd ZKdd ZLdd ZMdd ZNdd ZOdd ZPdd ZQdd ZRdd ZSdd ZTdd ZUdddZVdd ZWdd ZXdd ZYdddZZdddZ[dddZ\dddd fdd
Z] fddZ^dd fdd
Z_ fddZ` fddZadddZbdd Zcdd ZdddÄ ZedddńZfddǄ ZgedddɄZhe'e(ddDdˍdd̈́ Zie'e(ddDdˍdddЄZje'e(ddDdˍdddӄZk  ZlS )r   Toutline_insert_afterc                 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   s~   t  }|r| ||S |j  |j|_||_|j|_|j|_|  |	  |j
|_
t  |jddd W 5 Q R X |S )NF)r   r   )r   r   r'   r   loadr   	cmf_ownerZ
cmf_author
_calc_type_calc_projectr   r   r   r   )r    r   r!   r"   r%   r%   r&   create_gantt_task#   s    

zCmfGanttTask.create_gantt_taskc                 C   sp   | j rf| j jrf| j jj  | j jj}|dkr4d| _|dkrBd| _|dkrPd| _|dkrld| _d| _nd| _d S )Nconst_durationTconst_resource
const_workZconst_duration_and_work)r   r   Zdefault_gantt_task_typer(   r-   r.   r/   )selfZdefault_typer%   r%   r&   r*   @   s    
zCmfGanttTask._calc_typec                 C   s   | j r| j jr| j j| _d S N)r   r   r0   r%   r%   r&   r+   Q   s    zCmfGanttTask._calc_projectc                 C   s    | j r| j jS | jr| jjS d S r1   )r   r#   r   r2   r%   r%   r&   _get_parent_gantt_taskV   s    z#CmfGanttTask._get_parent_gantt_taskc                 C   s~   |d kr|   }| jjdkrPtjjddd gdd| jgdd| jgg|g |d}n*tjjdd| jgdd| jgg|g |d}|S )Ntask.gantt_projectr   ==r   r   filterr   )save_preload_fieldsr   logic_prefixr   r   listr   )r0   
add_filterr   Zchild_gantt_tasksr%   r%   r&   _get_children_gantt_tasks\   s*    

	

z&CmfGanttTask._get_children_gantt_tasksc                    s6   ddd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(parent_task.op_gantt_task.outline_numberztask.logic_prefixztask.parent_task.logic_prefixtask.has_child_tasksztask.plan_start_dateztask.plan_end_dateztask.responsibleztask.executorstask.parent_task task.gantt_project.op_gantt_task)superr8   )r0   r   	__class__r%   r&   r8   t   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   r0   resr%   r%   r&   _perform_cost   s    zCmfGanttTask._perform_costc                 C   s:   | j jrd S | j | j d }t|jtdtd}t|S NrC   1rD   )r
   rE   rF   rG   rH   r   intrI   r%   r%   r&   _perform_duration   s
    zCmfGanttTask._perform_durationc                 C   s:   | j jrd S | j | j d }t|jtdtd}t|S rL   )r   rE   rF   rG   rH   r   rN   rI   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secondsrC   rM   rD   )r   rE   r	   _get_calendardttimezone	timedeltarN   datetimenowZget_work_timedeltavaluerG   rH   r   )r0   calendarZtzrW   Zdelta_finish_time_secZdelta_now_time_secZperform_completerJ   r%   r%   r&   rF      s$    
zCmfGanttTask._perform_completec                 C   sR   | j jrdS | j jdkrNddd gdd| j gdd| jgg}tjj|d}|rNdS dS )	NTr4   r   r5   r   r   r7   F)r   has_child_tasksr9   r   r   r   sget)r0   _filterZhas_childrenr%   r%   r&   r[      s    

zCmfGanttTask.has_child_tasksFc                 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   r-   r   rX   r   r   namecode	cmf_alertr0   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 )NFr^   Tu   невозможно изменить дату окончания, если зафиксированы дата начала и длительностьr_   .   Фиксированное окончание ra   1-after%   Окончание не раньше 2-before#   Окончание не позже rf   uK   " конфликтует с плановой датой окончания rg   rh   ri   rj   rk   )	r   r-   r   rX   r   r   rm   rn   ro   rp   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)
        NNNr4   r^   rb   r^   ry   N)r3   r   r9   load_fieldsr8   get_parent_plan_limitr   r   maxr   r   minr-   r
   )r0   Zparent_start_limitZparent_finish_limitZparent_duration_limitparent_gantt_taskstart_limitfinish_limitduration_limitr%   r%   r&   r   #  s,    


z"CmfGanttTask.get_parent_plan_limitc           
      C   s<  d } }}| j sdS |  D ]}|jdkrP|s<|jj}n|jrPt||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|sZ|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 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)
        Nr|   )r^   rd   r~   )r^   rw   r}   )r[   r<   r   r   rX   r   r   r   r
   r   CmfCalendarget_date_by_durationrR   rN   r   r-   get_childrens_plan_limit)
r0   r   r   r   r"   r   r   Zch_start_limitZch_finish_limitZch_duration_limitr%   r%   r&   r   N  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   r   _plan_start_limit_min_plan_finish_limit_maxZ_plan_duration_limit_maxr   _plan_start_limit_max_plan_finish_limit_minZ_plan_duration_limit_minr2   r%   r%   r&   _calc_plan_limits_cache  s    z$CmfGanttTask._calc_plan_limits_cachec                 C   sn   | j r<| jr<tjj|  | j j| jj| jjp.|d}|| _n| j j	rR| jj	rRd| _| jj
rj| jd||d d S )N)rY   Zfrom_dtZto_dtforce_include_endsr   T)from_calc_sched_durationfrom_child_changesfrom_sched_dates_changed)r   r	   r   r   get_duration_minutesrR   rX   	is_manualr
   old
is_changed_do_sched_duration_is_changed)r0   r   r   r   Zcalendar_durationr%   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   rR   rX   actual_durationr   r2   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:
                плановая дата начала
        rq   rr   rw   )r   r   r   rR   r	   rX   rN   r
   ru   r   r   )r0   rollbackrq   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   rR   r   rX   rN   r
   r{   )r0   r   rq   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   rw   r^   u   Плановый период может не соответствовать плановой длительности, т.к. все переменные зафиксированы)
r
   rE   r	   r   r   r   r   r   r-   ro   )r0   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   rE   r   r   r   r   r   rR   rX   rN   r2   r%   r%   r&   _calc_actual_period4  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   rC   5-timetrackerN)actual_complete_typeactual_workis_not_nullr   mathfloorr   actual_completerE   $notify_parent_update_actual_completetimetracker_sched_work)r0   tmp_completer%   r%   r&   _calc_actual_completeH  s    




z"CmfGanttTask._calc_actual_completec           
      C   s  | j s
dS | jdkrdS | jdkr&dS | jdkr| jdgd}|sFdS t|}d}|D ]6}|r||j|jkr|||jjpvd7 }qV||jjpd7 }qV|| }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 ]}|rp|j|jjkrp|jddg ||jjjpFd7 }|jjjdkr|	|jjjpjd7 }	n2||jjp~d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_pointsr4   r   r5   r   agile_story_pointszstatus.status_typer6   =CLOSEDrC   3-costz4-fixed)r[   r   r<   lenidr   rX   r   r9   r   CmfTaskr:   r   r   statusstatus_type)
r0   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   sb  |    | 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|| _n| jdkr| j}| jr| jrt| j| j}|r| jr| j|kr|| _n`| jdkr|  }|r|j	 }|r|| _n| jr.| j| _n | jr| j| _| jr.| j| _| jrX| jrX| j| jkrXt
ddd | jr| jr| j| jk rt
d	dd | jjr,| jr| jj| jk r,| jr| 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rR| jjrR|   | jdd d S )N)r^   rb   rd   r^   rb   rd   1-earlyu~   Нельзя указать Дату начала больше зафиксированной у дочерней задачиTrk   u   Нельзя указать Дату начала меньше зафиксированной у родительской задачи)r   rd   )z2-latterrb   r   )r   r   r   r   r   r   r   r   r3   r(   ro   r   Zplan_start_datelock_sched_start_dater   _do_sched_start_date_is_changedr   )r0   r   r   parent_sched_start_dater%   r%   r&   #_do_constrain_start_date_is_changed  st    









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 )Nr4   out_linkr   relation_type.codezsystem.additional_parentin_linkr6   c                 S   s   g | ]}|j jjqS r%   )r   rX   r   .0relr%   r%   r&   
<listcomp>  s     z8CmfGanttTask._get_parent_gantt_tasks.<locals>.<listcomp>r   task_idIN)r   r9   r3   r   r8   appendr   CmfRelationOptionr:   r   r   extend)r0   parent_gantt_tasksr   r]   Z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.calendarproject.calendar.timezonezsystem:defaultrT   )rn   r   )r   r   rY   r   r   r   )r0   rY   r%   r%   r&   rR     s
    zCmfGanttTask._get_calendarc                 C   s"  d }d }dddg}| j |d}|D ]}|  |j| jjdd|_d |_|jrX|  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r;   r   r   r   rq   r   )r<   r   ru   r   newr	   r[   "_calc_sched_period_for_child_tasksr   _alert_changed_plan_fields_check_start_finish_relationsr   rX   r   r   r{   r   r
   r   )r0   r   r	   r;   child_tasks
child_taskr%   r%   r&   r     sD    




z/CmfGanttTask._calc_sched_period_for_child_tasksc                 C   s   | j | jjd |   | jrr| jjrr| jrh| j| jk rh| j| _td| jj d| jj	 d| jdd | 
  nJd }| jr| jr| jsd | _| jdd d	}n| jdd
 d}| j|d d S )Nr   u/   Предупреждение: В задаче "rh   u2   ) дата начала перемещена на ra   ub   , так как была позже даты ограничения дочерней задачи.Tr   
start_dater   durationchanged_field)ru   r   r   r   r[   r   ro   r   rm   rn   r   r
   lock_sched_finish_dater	   r   r   _calc_resourcesr0   r   r%   r%   r&   r   A  s    (
z,CmfGanttTask._do_sched_start_date_is_changedc                 C   sx   | j | jjd |   d }| jrR| jsR|   d}| jrb| jjrb| jrb| 	  n| j
dd d}| jst| j|d d S )Nr   r   Tr   r   r   )r{   r	   r   r   r
   r   r   r[   r   r   r   r   r   r%   r%   r&    _do_sched_finish_date_is_changedZ  s    
z-CmfGanttTask._do_sched_finish_date_is_changedr      )Z	only_onceZonly_once_argspriorityc                 C   sP   t jj| d}| }|| t jj||d}|jd||d |jdd d S )Nr   r   r   T)deferredr   ignore_child_taskr   )r   r   r   r8   r   from_sched_dates_of_child_tasksr   )r   child_task_idr   r   r   r   r%   r%   r&   #from_sched_dates_of_child_tasks_jobl  s    
z0CmfGanttTask.from_sched_dates_of_child_tasks_jobc              
   C   sZ  | 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
 |s| jjdkrt| j| j	j
|j	j
|dd dS d}d}|s|nd}|s|nd}	ddddgd	ddgg}
| j|
d
}|D ]}|r&|j	|j	kr&q
|jrZ|s:|}n |jsH|}n|j|jkrZ|}|jr
|	sn|}	n |	js||}	n|	j|jk r
|}	q
|r|j}|	r|	j}|s|r|jr|js|j|k r|j}|s|r|jr|js|j|kr|j}d}d}| jr|rd|jj d|jj d}| jj
}| jdkrb||krbd|d}nL| jdkr||k rd|d}n(| jdkr||krd|d}nd}d}| jrR|rRd|	jj d|	jj d}| jj
}| jdkr||krd|d}nL| jdkr*||k r*d|d}n(| jdkrN||krNd|d}nd}|r|p`|}td| jj d| jj d| d| d	dd || _|| _| jjs| jjr| jddd  |s|r| j|jk r|j| _| jdd! |   |  }|rVt F |D ]:}||   |j| ||d" |  |j d#dd$ qW 5 Q R X dS )%u  
        Обновляет плановые даты родительской задачи.
        Плановая дата начала = самая ранняя плановая дата начала всех подзадач
        Плановая дата окончания = самая поздняя плановая дата окончания всех подзадач

        Args:
            child_task (optional): Defaults to None
                измененная дочерняя задача, которая еще не сохранена.
            ignore_child_task (bool, optional): Defaults to False
                игнорировать дочернюю задачу при обновлении дат родительской задачи.
        u   Задача "{}" ({}) вышла за диапазон плановых дат родительской задачи "{}" ({})Nr4   )r   r   r   )kwargsORr   r   r	   r   "rh   )r^   r`   ra   rb   rc   rd   re   rv   rw   rx   ry   rz   uU   Конфликт планирования: В родительской задаче "u   ) ограничение "u<   " конфликтует с дочерней задачей rj   Trk   )r   r   r   r   pathF
user_inputr   )!r   r   r	   ro   formatr   rm   rn   setr   rX   addr9   schedule_deferred_jobr   r<   r   r   r   r   r   r   r
   r   r   r   r   disable_aclr   r8   r   r   r   )r0   r   r   r   r   msgZmin_start_dateZmax_finish_datesZchild_task_with_min_startZchild_task_with_max_finishr;   gantt_tasksr"   rt   Zconflict_child_task_min_startr   Zconflict_child_task_max_finishr   Zconflict_child_taskr   r   r%   r%   r&   r   y  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   rX   r   r   r   )r0   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 )Nr^   ^   Дата родительской задачи является зафиксированнойrd   u   Предупреждение: Плановая дата начала дочерней задачи больше плановой даты начала родительской задачиrb   u   Предупреждение: Плановая дата начала дочерней задачи меньше плановой даты начала родительской задачи)
date_field)r   r   ro   r   _change_parent_start_date)r0   r  parentsr   r%   r%   r&   r  5  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 )Nr^   r  rw   u   Предупреждение: Плановая дата окончания дочерней задачи меньше плановой даты окончания родительской задачиry   u   Предупреждение: Плановая дата окончания дочерней задачи больше плановой даты окончания родительской задачи)r   r   ro   r   r   _change_parent_finish_dater   r   )r0   r  r  r   Z
date_valuer%   r%   r&   r  K  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)r^   rw   ry   r^   rw   ry   u   Нельзя указать Дату окончания больше зафиксированной у родительской задачиTrk   u   Нельзя указать Дату окончания меньше зафиксированной у дочерней задачиr   )r   r   r   r	   r   r   r   r   r   r   r   ro   r   Zplan_end_dater   r   r
   r   r   r   r   r   )r0   r	   r%   r%   r&   $_do_constrain_finish_date_is_changedd  sr    







z1CmfGanttTask._do_constrain_finish_date_is_changedc                 C   sz   | j dkrB| jdkrB| jjsB| jjsB|r6tddd ntddd |sv|sb| jrVd | _| jdd |sv|sv| j	dd d S )	Nr^   u  Конфликт планирования: невозможно изменить трудозатраты, если установлена опция "Фиксированные Ресурсы" и зафиксированы даты начала и окончания.Trk   u   Конфликт планирования: невозможно изменить длительность, если зафиксированы даты начала и окончания.r   r   r   )
r   r   r   r   r   ro   r   r	   r   r   )r0   Zfrom_sched_work_changedr   r   r   from_calc_resourcesr%   r%   r&   r     s&    z*CmfGanttTask._do_sched_duration_is_changedc                 C   s   |    d S r1   )r   r2   r%   r%   r&   _do_actual_duration_is_changed  s    z+CmfGanttTask._do_actual_duration_is_changedc              	   C   s   | j jrd S | j dk r"tddd | j}|r4|s4d}|sL| jjdkrL| j | _|   | j jp^d| j j	phd }| 
 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   "Фактические трудозатраты" стал ниже нуля, выставляем в ноль.Trk   r4   Fr   )r   rE   ro   r[   r   r9   actual_myself_workr   r   r   r3   r   r   r   r8   from_child_add_workr   r   r   ZCmfTimeTrackerZ"_op_gantt_actual_work_changed_hook)r0   from_timetrackerfrom_add_childr[   
work_deltar   r%   r%   r&   _do_actual_work_is_changed  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   "Фактические Затраты" стал ниже нуля, выставляем в ноль.Trk   r   rC   r4   Fr
  )actual_costrE   ro   r   r   r   r   r   r   r   r   r3   r   r9   r   r   r   r   r   r8   from_child_add_costr   )r0   r   
cost_deltar   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   rE   r   r2   r%   r%   r&   _do_actual_complete_is_changed  s    

z+CmfGanttTask._do_actual_complete_is_changedc                 C   sF   | j jp
d| j jpd }| js(| j | _n|  j|7  _| j|d d S )Nr   r  )r  r   r   r[   r   r  )r0   r  r  r%   r%   r&   !_do_actual_myself_work_is_changed&  s
    
z.CmfGanttTask._do_actual_myself_work_is_changedc                 C   sB   | j jp
d| j jpd }| js(| j | _n|  j|7  _|   d S r   )actual_myself_costr   r   r[   r  r  r0   r  r%   r%   r&   !_do_actual_myself_cost_is_changed0  s
    
z.CmfGanttTask._do_actual_myself_cost_is_changedc           	   	   C   sT  | j jrd S | j| j kr(| 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
 |   | 	 r2| j
jdkr2| j jpd| j jpd }t < | 	 }||   || |  |jddd W 5 Q R X |sP|sP|sP| jdd d S )N<      ч    мu   Нельзя запланировать меньше Трудозатрат, чем запланировано в подзадачах ( < ub   ). Увеличьте Трудозатраты или уменьшите их у подзадач.Trk   u   DEV: Указано Трудозатрат меньше, чем запланировано в подзадачах. Выставили uo   . Если нужно уменьшить Трудозатраты, уменьшите их у подзадач.r4   r   Fr   workr   )r   rE   r   %_do_timetracker_sched_work_is_changedr[   agregat_workr/   ro   r   r3   r   r9   r   r   r   r   r   r8   from_child_agregate_workr   r   r   )	r0   Zfrom_sched_duration_changedr   r   r  r!  r   r  r   r%   r%   r&   _do_sched_work_is_changed:  s6    

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   ). Увеличьте Расходы или уменьшите их у подзадач.Trk   u|   Указано Расходов меньше, чем запланировано в подзадачах. Выставили ue   . Если нужно уменьшить Расходы, уменьшите их у подзадач.r4   r   Fr   )r   rE   agregat_cost
const_costro   r3   r   r9   r   r   r   r   r   r8   from_child_agregate_costr   r   r0   r  r   r%   r%   r&   _do_sched_cost_is_changedw  s"    

z&CmfGanttTask._do_sched_cost_is_changedc                 C   s"   | j r| jr|   n|   d S r1   )r   r   r   r   r2   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 r1   )r   r   r   r   r   r2   r%   r%   r&   !_do_actual_finish_date_is_changed  s    
z.CmfGanttTask._do_actual_finish_date_is_changedc                 C   sX   | j dkr|   n@| j dkr.| jr.|   n&| j dkrB|   n| j dkrT|   d S )Nr   r   r   r   r   )r   r  r[   r   r  r   r2   r%   r%   r&   #_do_actual_complete_type_is_changed  s    





z0CmfGanttTask._do_actual_complete_type_is_changedc                 C   s   | j r|   d S r1   )r-   r   r2   r%   r%   r&   _do_const_duration_is_changed  s    z*CmfGanttTask._do_const_duration_is_changedc                 C   s8   | j r|   n$| jr&| jd dd nd| _|   d S )NT)r   r   r   )r   r   r[   r   r   r   r2   r%   r%   r&   _do_is_manual_is_chagned  s    
z%CmfGanttTask._do_is_manual_is_chagnedc                 C   s*   | j rd| _| j| _n| jj| _d | _d S Nr^   )r   r   r   r   defaultr2   r%   r%   r&   $_do_lock_sched_start_date_is_changed  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   r0  r2   r%   r%   r&   %_do_lock_sched_finish_date_is_changed  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dd |jr| js| j|jjdd| _| jjr|   n|jr| jrn
||  |jdd d S )	Nr   r   r  r   T)r  r   r   )r   r#   r(   r8   r&  r   r"  r   r  r  r  r  r   r  r   ru   r   rX   r   r   r   r   r0   r   r   r%   r%   r&   add_child_task_to_parent_task  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   )r   r#   r(   r8   r&  r   r"  r   r  r  r  r  r   r  r   r   r3  r%   r%   r&   "remove_child_task_from_parent_task  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 r1   )r   r   r   r   r   r5  r   r4  )r0   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r0   argsr   r%   r%   r&   _do_event_save  s    zCmfGanttTask._do_event_savec                 O   s
   | | j S r1   )	log_level)r0   
field_namer9  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rr| 4  d S | j5jr| 6  | j7jr| 8  d S )Nr   r   )9is_newr
   r   r3   r   r(   r   rS   rV   Zutcnowr   r   rE   r   r   r6  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   r1  r   r   r   r2  )r0   from_parent_changesr   r   r%   r%   r&   _save	  s    













zCmfGanttTask._savec                  C   s   |sd S t d| j  |d kr@tjjdd| jgdddgd}t|}|dkrTd S |D ]X}dd|jgdd|j	gd	dd
gg}tj
j|d	gdD ] }t  |  W 5 Q R X qqX| jr| js| jr| jrd S | jj}d }| jr| jj}| jj}	| jj}
|
}d}|	s|
sd S | jj}| jj}| jj}| jr^|	r@|
s@|	}
|
rP|	sPd S |
|	 | }n|
sh|	}
|	sr|
}	|r|r|
|	 | }nr|r|dkr|
|	 | }nT|r|dkr|
|	 | }n|dks|dkr|	| }
n|r|dkr|	| }
|
}d}|dkr |
| }|
| }g }g }i }|D ]}|}|dkrP||7 }d}d }d }|jj| | jj|ov| dD ]}|jj}d}|| kr||}|jj| }|dkrq|t|| |k rt|| n|}||}||t||  p|jddd}tj
|j| j||d
|dd}t  |j dd W 5 Q R X |sX|}|!|d}||krv|||< ||8 }|dkr||} qq||"| |"| | |_#| |_$t|d |_%t  |   W 5 Q R X q0t&|| _t'|| _tt(|) | | _|
| _|rl|dkr
|	| jjkrF| j*dd |dkr
|| jjkr
| j+dd n|r|dkr
|| jjkr
| j+dd nr|r
|dkr|	| jjkr| j*dd |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   r5   personrY   r6   r   r)   history_typeZplanr   r   r  units)r   r   finish_date   ;   )hourminute)r)   r   r   Zend_daterB  Z
time_spentremaining_estimateF)r   rC   )rC  r  T)r  )rC  r   z'_calc_resources finish. gantt_task_id: ),gdebugr   r   ZCmfTaskResAssignr:   r   r   rA  r   ZCmfTimeTrackerHistoryr   r   deleter   r
   r   r[   rX   r	   r.   r/   r-   r   rY   Z	work_daysdateZget_work_time_shiftZinterval_total_minutesrN   Zget_shifted_work_timereplacer   r   r   Z
date_startZdate_endrC  r   r   sumvaluesr   r#  ) r0   r   assignmentsZnumber_of_resourcesZ
assignmentr]   Ztthr   rD  r   r  Zinitial_workrC  r.   r/   r-   Zwork_per_personZwork_remainderZstart_timesZ	end_timesZmax_work_per_dayZ	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   j  s   











"
&	














zCmfGanttTask._calc_resourcesc                 C   s,   |  |   | jp| j}|r(| | d S r1   )r   r8   r   r   r4  r0   r   r%   r%   r&   task_copy_hook  s    zCmfGanttTask.task_copy_hookc                 C   s4   |  |   |   | jd|d | jdd d S )NrC  )r   rQ  Tr   )r   r8   r   r   r   )r0   rQ  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   )r   r8   r   r  r   r  r   )r0   r  rI  r%   r%   r&   timetracker_add_work_hook  s    	z&CmfGanttTask.timetracker_add_work_hookc                 C   s<   |  |   |   |  j|7  _|   | jdd d S NTr   )r   r8   r   r  r  r   r  r%   r%   r&   timetracker_add_cost_hook/  s
    z&CmfGanttTask.timetracker_add_cost_hookc                 C   sp   |  |  ddg  d}| jjr@| jjjjdkr@| jj| _d}| jr\| jj| _|   d}|rl| j	dd d S )Nr?   task.parentF
CmfProjectTr   )
r   r8   r   r   rX   
class_namer   r   check_gantt_projectr   )r0   Z	need_saver%   r%   r&   task_parent_is_changed_hook6  s    

z(CmfGanttTask.task_parent_is_changed_hookc                 C   s   |    d S r1   )r   r2   r%   r%   r&   r   F  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   rC   r   )r   r   r   r8   r   Zset_nowr)  r   r   r   r*  r  r   )r0   Z
old_statusZ
new_statusr%   r%   r&   task_status_is_changed_hookI  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?   У гант-задачи нет ссылки на задачу.Trk   r   N)	r   r8   r   ro   r   r   r  r[  r   r2   r%   r%   r&   task_plan_dates_is_changed_hookp  s    z,CmfGanttTask.task_plan_dates_is_changed_hookc                 C   s&   |  |   |   | jdd d S rV  )r   r8   r[  r   r2   r%   r%   r&   task_logic_type_is_changed_hook~  s    z,CmfGanttTask.task_logic_type_is_changed_hookc                 C   s$   |  |   | jj| _|   d S r1   )r   r8   r   r   r   r2   r%   r%   r&    task_parent_task_is_changed_hook  s    
z-CmfGanttTask.task_parent_task_is_changed_hookc                 C   s0   |  |   | jj| _|   | jdd d S rV  )r   r8   r   r   r[  r   r2   r%   r%   r&   "task_gantt_project_is_changed_hook  s    
z/CmfGanttTask.task_gantt_project_is_changed_hookc                 C   s>   |  |   | jr:d | _d| _| js.|   | jdd d S )Nr   Tr   )r   r8   r   r   r   r   r   r2   r%   r%   r&   $task_has_child_tasks_is_changed_hook  s    z1CmfGanttTask.task_has_child_tasks_is_changed_hookc                 C   s    | j jrd| _ |  j |7  _ d S r   )r   rE   )r0   r  r%   r%   r&   r    s    z CmfGanttTask.from_child_add_workc                 C   s    | j jrd| _ |  j |7  _ d S r   )r  rE   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   . Увеличьте в ней план или введите меньше Трудозатрат.Trk   r4   Fr   )r!  rE   r/   r   r   r   ro   r   r3   r9   r   r   r   r8   r"  r   r   )r0   r  r!  r   r   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[   . Увеличьте в ней план или введите меньше РасходовTrk   r4   Fr
  )r$  rE   r%  r   ro   r   r3   r9   r   r   r   r   r   r8   r&  r   r'  r%   r%   r&   r&    s     


z%CmfGanttTask.from_child_agregate_costc                 C   s   |  | dS )u   
        Оповестить родителя, что ему нужно пересчитать свой агрегат actual_complete
        N)r   )r0   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
  )r3   r   r   r   r8   re  r   )r0   r   r%   r%   r&   r     s    

z1CmfGanttTask.notify_parent_update_actual_completec                 C   s  t | j| j| jgs tddd | 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r| jrtddd | jjr| 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s| jdkrtddd d S )Nu   Необходимо указать Тип задачи, например, фикс. длительность, фикс. трудозатраты или фикс. ресурсыTrk   c                 S   s   h | ]\}}|qS r%   r%   )r   k_r%   r%   r&   	<setcomp>  s     z0CmfGanttTask.check_user_input.<locals>.<setcomp>r   r   parent_task_idr   gantt_project_idzPPP-TSK-SCHEDULE)obju   Нельзя менять поле "Фактические трудозатраты" у Summary-задач. Используйте поле "Фактические трудозатраты по собственным ресурсам задачи"u   Нельзя менять поле "Фактические Затраты" у Summary-задач. Используйте поле "Фактические Затраты по собственным ресурсам задачи"u\   Нельзя выставить Дату начала позже Даты окончанияr+  u   Этот вариант рассчета процента завершения можно использовать только у Summary-задач)anyr-   r.   r/   ro   r>  r   itemsr   r   r(   Zcheck_project_role_accessr   r[   r  r   r   r   )r0   Zchanged_fieldsr%   r%   r&   check_user_input  s8    
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   rE   r$  r   r  r
   r   r!  r   r  r   Zconstrain_slackZconstrain_slack_pctr2   r%   r%   r&   r     s0    zCmfGanttTask.fix_null_variablesr   c           
      C   st  t d| j d|  | jjdkr(d S | jjr| jr| jjrZ| j| jjkrZd}| jj}nH| jjs| jr| jj	| 
  | jjj| jdd | jjjdd d S t| jp| jp| jp| jp| j}|s|s| js| jjdks| js|s| jjrp| jrp| j| jjkrp|rd S |rB|| _| j| j_| jjdd n,| jjsn| j  | jj| _| jjdd |s| jr|  }|D ]F}|jr|j| jkr|j| jd|d d	 |  |jdd q|sD|  rD|  }	|		| 
  |	jjdkrD|	jr|	j| jkrD|	j| jd|d d
 |	  |	jdd | jsp| jjrd| | jj | | j d S )Nz!check_gantt_project: gantt_task: z	, level: r4   T)r   r   r   )z	task.epicztask.subprojectr   )r   r?  level)r   r   rp  )rJ  rK  r   r   r9   r   r   r   r#   r   r8   r[  r   boolr   r	   r
   r   r   r   Z_calc_gantt_projectr[   r<   _calc_outline_numberr3   r5  r4  )
r0   r?  r   r   Zfrom_importrp  Zplan_fields_are_usedr   r   r   r%   r%   r&   r[  !  s    
	





z CmfGanttTask.check_gantt_projectc                 C   s
  |dkrt ddd |s| j}dd| jgdddgg}d	d
 tjj|ddgdD }d }tjjddt| gddddgd}|D ]F}|j	j
}||jjj
 }	|	r|jj}
tj|
||	}||kr|} qq|s| jr| jj}||  |js|j||d d}|S )NrC   uY   DEV: Защита от рекурсии: _get_task_blocking_back_movement: if depth > 100Trk   r   r5   r   system.finish:startc                 S   s   i | ]}|j j|jqS r%   )r   r   constrain_lagr   r%   r%   r&   
<dictcomp>x  s      zACmfGanttTask._get_task_blocking_back_movement.<locals>.<dictcomp>r   rt  r6   r   r   z	task.nameztask.idr	   r   r   )target_datedepth)ro   r   r   r   r   slistr   r:   keysr	   rX   r   r   rY   r   r   r   r#   r   r8   r    _get_task_blocking_back_movement)r0   rv  rw  Z
_filter_inZbefore_tasks_with_lagsZblocking_gantt_taskZbefore_gantt_tasksZbefore_gantt_taskrD  rt  rY   r   r%   r%   r&   rz  o  s4    
z-CmfGanttTask._get_task_blocking_back_movementc                 C   s   | j jr| j jrP| j jrP| j j| j jk rP|rP|  }|rPtd|jj ddd dd| jgdddgg}tj	j
|d	rttj	j| jjgd
 d S )NuB   Перемещение заблокировано задачей "uk   " с исходящей связью ОН. Удалите связь и попробуйте еще раз.Trk   r   r5   r   rs  rZ   r9  )r	   r   r   r   rz  ro   r   rm   r   r   r\   r   Zsort_task_by_order_relationsr   )r0   	from_saveZblocking_taskZ_filter_outr%   r%   r&   r     s"    z*CmfGanttTask._check_start_finish_relations)r   r?  from_sort_orderc                   s   |r|    | j|| | j|d | jjr:| jjdd |   |   |   t	 j||}| j
jrtj| j| j
 dd  | jjrtj| j| j dd  |s| jdd |S )N)r?  Tr      )r   rs   r;  )r|  )ro  r:  r@  r   r   r   r[  r   rr  r@   r   r   Z
CmfCommentZ	html_diffr	   r   )r0   r   r?  r}  r9  r   rJ   rA   r%   r&   r     s2    

zCmfGanttTask.savec                    s   |    t j|| d S r1   )r   r@   _save_importr8  rA   r%   r&   r    s    zCmfGanttTask._save_import)TEXKOM_db_deletec                   s   |  dddddg | jr&| | j |rlt| j tjj	| jddD ] }t| d |_
|jdd qHn&| jr| jj
| kr| jjstd	dd
 | jr| jj
| kr| | j t j|d|i|S )Nztask.op_gantt_taskztask.cmf_deletedr   r   outline_index_numT)r#   Zinclude_deletedr   uY   Нельзя удалять Оперативную гант задачу у задачи!rk   r  )r   r   outline_remover   rJ  rK  r   r   r   r:   r#   r   r   Zcmf_deletedro   r5  r@   rL  )r0   r  r9  r   r   rA   r%   r&   rL    s"    
zCmfGanttTask.deletec                    s@   t  jf | | jrd S td tj| dd td d S )N_CmfAutomationCrudTrigger startupdateZbefore_save_CmfAutomationCrudTrigger end)r@   before_save_hookr>  rJ  rK  r   CmfAutomationCrudTrigger	crud_hookr0   r   rA   r%   r&   r    s    
zCmfGanttTask.before_save_hookc                    s@   t  jf | | jrd S td tj| dd td d S )Nr  r  Z
after_saver  )r@   before_save_data_hookr>  rJ  rK  r   r  r  r  rA   r%   r&   r    s    
z"CmfGanttTask.before_save_data_hookr  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-r  )ZroomZevent_persons)
r   r(   rJ  Zsession_tab_idZcurrent_personr   rX   ry  Zcomponent_idZcmf_emit_event)r0   Zinitiator_actionZeventr%   r%   r&   r  	  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 )Nr4   u)   Плановая длительностьu)   Плановые трудозатратыu&   Плановая дата началаu,   Плановая дата окончания)r
   r   r   r	   )r
   r   u   Нетr  Z02dr  u   мин)r   r	   rQ   zUTC z+03d:z%Y.%m.%d, %H:%M u   DEV: У задачи z (u   ) изменено поле "z":                        c u    на rj   u.   ) сброшено значение поля "z".)r   r9   ry  r   r   r   r   rR   Z
astimezoner   rS   rU   rT   rX   divmodstrftimero   rm   rn   )r0   r   rY   r<  r$   Znew_value_strZold_value_strZold_hourZ
old_minuteZnew_hourZ
new_minuteZdate_oldZdate_newrG  rH  Ztime_suffixr%   r%   r&   r   	  sR    

"

z'CmfGanttTask._alert_changed_plan_fieldsc                 C   s   | j js| jjsd S | jjdkr6d| _d| _d | _d S | j jrr| j sr| j jrn| j}| jjrb| jj}| j	|d d S | j sd| _d| _d | _d S | j jr| j r| j js| 
  d S | jjr| js| j	| jjd | 
  d S )Nr4   r_   )remove_from)r   r   r   r   r9   outline_numberoutline_parent_strr  r   r  outline_insert_downr>  rR  r%   r%   r&   rr  Y	  s:    z!CmfGanttTask._calc_outline_numberc              
   C   s   |  | jj| jj d| _| jr,| jjj| _t B t	j
jdgg dd| jgdd| jgddd gdd| jggd	j}W 5 Q R X |r|d
 | _nd
| _| jr| jr| jpd d| jjd| _n| jjd| _| jjrtt	j
j| jjjg| jjg| jjjgd d S )Nr_   max__outline_index_numr   r   r   r   r   r   )r   order_byr7   r   rj   06r{  )_outline_lock_lvlr   r   r   r  r#   r  r   r   r   r   r   r  r  rX   r   r[   r   _outline_recalc_childrens)r0   Z	max_indexr%   r%   r&   r  {	  s.    




z CmfGanttTask.outline_insert_downc              
   C   s  |  dddddg | | jj| jj d }|r\tjj|ddgd}|j| jkr\tdd	d
 | j	j
}|sd}d| _	d	}d}|}d}nH|jj	j
}||k rd	}|d | _	|d }|}d}nd}|| _	|}|}d}| jr| jpd d| j	j
d| _n| j	j
d| _d}	tjjj |	| jjj
| jjj
| jj
|||d}
dd |
D }dd |
D }| jjrl|| jjj
 |rttjj||| jjj
gd d S )Nr   r   r  r  r=   zop_gantt_task.outline_index_numr   u   outline_insert_after можно вызывать только для задач на том же уровне. Сначала измените parent_taskTrk   r   Fr_   rj   r  a  UPDATE cmf_gantt_task SET
            outline_index_num = outline_index_num + :i,
            outline_number = (CASE WHEN outline_parent_str = '' or outline_parent_str is null THEN to_char(outline_index_num + :i, 'FM000000') ELSE concat(outline_parent_str, '.', to_char(outline_index_num + :i, 'FM000000')) END)
        WHERE
            (parent_task_id = :parent_task_id or (:parent_task_id is null and parent_task_id is null))
            AND gantt_project_id = :gantt_project_id
            AND outline_index_num >= :from_outline_index_num
            AND outline_index_num <= :to_outline_index_num
            AND parent_id is null
            AND id != :id
        RETURNING task_id, id
        )rj  rk  r   from_outline_index_numto_outline_index_numic                 S   s   g | ]}|d  qS r   r%   r   rr%   r%   r&   r   	  s     z5CmfGanttTask.outline_insert_after.<locals>.<listcomp>c                 S   s   g | ]}|d  qS r   r%   r  r%   r%   r&   r   	  s     r{  )r   r  r   r   r   r   r   r   ro   r  rX   r#   r  r  	CmfPersondp_ddSessionexecuter   r[   r   r   r   r  )r0   Zprev_task_idZ	prev_taskold_outline_index_numZprev_outline_index_numZ	insert_upr  r  r  sqlrJ   	child_idschild_gantt_idsr%   r%   r&   r   	  sZ    


z!CmfGanttTask.outline_insert_afterc                 C   s   | j s
d S d }|r|jj}| | jj| d| _d| _t| j }d | _ d}tj	j
j ||| jjj|| jjd}dd |D }dd |D }|rttjj||| jjjgd | jr| jjrtjj| jjjdgd	j| jj_d S )
Nr_   a}  UPDATE cmf_gantt_task SET
            outline_index_num = outline_index_num - 1,
            outline_number = (CASE WHEN outline_parent_str = '' or outline_parent_str is null THEN to_char(outline_index_num - 1, 'FM000000') ELSE concat(outline_parent_str, '.', to_char(outline_index_num - 1, 'FM000000')) END)
        WHERE
            (parent_task_id = :parent_task_id or (:parent_task_id is null and parent_task_id is null))
            AND gantt_project_id = :gantt_project_id
            AND outline_index_num >= :old_outline_index_num
            AND parent_id is null
            AND id != :id
        RETURNING task_id, id
        )rj  rk  r  r   c                 S   s   g | ]}|d  qS r  r%   r  r%   r%   r&   r   	  s     z/CmfGanttTask.outline_remove.<locals>.<listcomp>c                 S   s   g | ]}|d  qS r   r%   r  r%   r%   r&   r   	  s     r{  r  r   )r  r   rX   r  r   r  r  rN   r   r  r  r  r  r  r   r   r  r   r#   r\   )r0   r  Zremove_from_idr  r  rJ   r  r  r%   r%   r&   r  	  s0    
zCmfGanttTask.outline_removec                 C   s   t d| d|  d S )NZOUTLINE_NUMBER_rg  )r   Zcache_obj_lock_get)r    rk  rj  r%   r%   r&   r  
  s    zCmfGanttTask._outline_lock_lvluY   Вычисление структурных номеров у дочерних задач)descriptionr   c                    s   t d tjjtjj|  tjjtj	j| g g   fddt d |  t d tjjtjj tjjtj	j  t d d S )Nz)_outline_recalc_childrens init invalidatec                    s   t dt|   | D ]}tj| qt d d}tjjj	 
|t| d}g }|D ]6}|d r|||d  |d   |d  qb|r| d S )Nz3_outline_recalc_childrens recalc_subtree start len=z2_outline_recalc_childrens recalc_subtree locks seta  UPDATE cmf_gantt_task SET
                outline_parent_str = pg.outline_number,
                outline_number = (CASE WHEN pg.outline_number = '' or pg.outline_number is null THEN to_char(ugt.outline_index_num, 'FM000000') ELSE concat(pg.outline_number, '.', to_char(ugt.outline_index_num, 'FM000000')) END )
            FROM
                cmf_gantt_task ugt
                LEFT JOIN cmf_task pt ON pt.id = ugt.parent_task_id
                LEFT JOIN cmf_gantt_task pg ON pt.op_gantt_task_id = pg.id
            WHERE
                ugt.parent_task_id IN :parent_task_id_list
                AND ugt.gantt_project_id = :gantt_project_id
                AND ugt.id = cmf_gantt_task.id
                AND ugt.parent_id is null
            RETURNING ugt.task_id, ugt.id, (select cmf_task.has_child_tasks from cmf_task where cmf_task.op_gantt_task_id = ugt.id) as has_child_tasks
            )Zparent_task_id_listrk  r[   r   r   )rJ  rK  r   r   r   r  r  r  r  r  r  tupler   )Zparent_id_listZ	parent_idr  rJ   Zrecursive_parent_id_listtZfinish_gantt_invalidateZfinish_invalidaterk  recalc_subtreer%   r&   r  
  s"    
z>CmfGanttTask._outline_recalc_childrens.<locals>.recalc_subtreez._outline_recalc_childrens start recalc_subtreez+_outline_recalc_childrens finish invalidatez_outline_recalc_childrens end)
rJ  rK  cmfapp	CMF_CACHEinvalidate_idsr   r   rZ  r   )ZidsZgantt_task_idsrk  r%   r  r&   r  
  s    
"

z&CmfGanttTask._outline_recalc_childrensuB   Перевычисление структурных номеровc                 C   s$  | r| g}n*t jjddgdddgdd}dd	 |D }td
t|  |D ]} td|  d t }|d  |rN| }td|  d|  d}t j	j
j ||| d}t }|D ](}|d r||d  ||d  qt  td tjjt jjt| td qtqNd S )Nz--r   r9   r   r4   T)r   r7   Zinclude_systemc                 S   s   g | ]
}|j qS r%   r   r  r%   r%   r&   r   I
  s     z6CmfGanttTask._outline_force_recalc.<locals>.<listcomp>u<   _outline_force_recalc Всего гант-проектов: u/   _outline_force_recalc обрабатываем u   . Уровень 0u   . Дочки от u
  WITH gt AS (
                    select g.id, g.task_id, g.parent_task_id, x.num
                    from
                        cmf_gantt_task as g
                        inner join cmf_task t ON t.id=g.task_id
                        inner join unnest(array(
                            SELECT cmf_gantt_task.id from cmf_gantt_task inner join cmf_task ON cmf_task.id=cmf_gantt_task.task_id
                            WHERE
                                -- Основной WHERE. Нужен чтобы верно рассчитались номера
                                cmf_gantt_task.parent_task_id = :parent_task_id OR (:parent_task_id is null and cmf_gantt_task.parent_task_id is null)
                                AND cmf_gantt_task.gantt_project_id = :gantt_project_id
                                AND cmf_gantt_task.cmf_deleted = false
                                AND cmf_gantt_task.parent_id is null
                            ORDER BY
                                cmf_gantt_task.outline_number, cmf_task.orderno, cmf_gantt_task.cmf_created_at
                        )) WITH ORDINALITY AS x(id, num) ON x.id=g.id
                        WHERE
                            -- Дубль WHERE. Нужно чтобы PG не селектил все гант-таски в cmf_gantt_task as g
                            g.parent_task_id = :parent_task_id OR (:parent_task_id is null and g.parent_task_id is null)
                            AND g.gantt_project_id = :gantt_project_id
                            AND g.cmf_deleted = false
                            AND g.parent_id is null
                        ORDER BY
                            -- Дубль сортировки
                            g.outline_number, t.orderno, g.cmf_created_at
                )
                UPDATE cmf_gantt_task SET
                    outline_index_num = gt.num,
                    outline_parent_str = pg.outline_number,
                    outline_number = (CASE WHEN pg.outline_number = '' or pg.outline_number is null THEN to_char(gt.num, 'FM000000') ELSE concat(pg.outline_number, '.', to_char(gt.num, 'FM000000')) END )
                FROM
                    gt
                    LEFT JOIN cmf_task pt ON pt.id = gt.parent_task_id
                    LEFT JOIN cmf_gantt_task pg ON pt.op_gantt_task_id = pg.id
                WHERE
                    gt.id = cmf_gantt_task.id
                RETURNING gt.task_id, (select cmf_task.has_child_tasks from cmf_task where cmf_task.id = gt.task_id) as has_child_tasks
                )rj  rk  r[   r   z&_outline_force_recalc cache invalidatez-_outline_force_recalc finish cache invalidate)r   r   rx  rJ  rK  r   r   r   popr  r  Zdata_driverr  r  
cmf_commitr  r  r  r  rZ  r:   )rk  Zgantt_project_id_listrJ   Zprocess_parent_id_setrj  r  Zclean_cache_idsr  r%   r%   r&   _outline_force_recalcB
  s4    
&
z"CmfGanttTask._outline_force_recalcu*   Пересчет гант проектовc           	      C   s  g }| rdd| g}t jjdg|dD ]v}|j}|s6q$d|j_d|j_d|j_d|j_dd|gdd|gddd gd	d
dd gd
ddggddd gdddggddd gdddggddd gdddgggg}d
dddg}d}t j	j||d g||dgd}|sqp|D ]l}|j j|jj
pd7  _|j j|jj
p*d7  _|j j|jj
pDd7  _|j j|jj
p^d7  _q|d7 }q|jj|j_|jj|j_|jjdd t  q$d S )Nrn   r5   z main_gantt_project.op_gantt_task)r   r7   r   rX  r4   r>   r   r   r   r   r  r   i  Zcmf_created_at)slicer7   r   r  Tr   )r   rY  r:   Zmain_gantt_projectr#   r   r!  r  r$  r   rX   r   r   r   r  )	Zproject_codeZproject_filterr   r   r7   r   r  r   r"   r%   r%   r&   recalc_gantt_projects
  sh    

z"CmfGanttTask.recalc_gantt_projects)N)NF)NF)FFF)F)F)F)N)NFNF)F)FFFFF)FF)F)FFFF)N)N)F)NN)N)N)FFNFr   )Nr   )F)r  )N)N)N)N)m__name__
__module____qualname__Z	api_allowr   Zapi_methodsclassmethodr'   r,   r*   r+   r3   r<   r8   propertyrK   rO   rP   rF   r[   ru   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   rR   r   r   r   staticmethodZcmf_deferred_jobr   r   r  r  r  r  r   r	  r  r  r  r  r  r#  r(  r)  r*  r,  r-  r.  r1  r2  r4  r5  r6  r:  r=  r@  r   rS  rT  rU  rW  r\  r   r]  r^  r_  r`  ra  rb  r  r  r"  r&  re  r   ro  r   r[  rz  r   r   r  rL  r  r  r  r   rr  r  r   r  r  r  r  r  __classcell__r%   r%   rA   r&   r      s   






&
'+_

!	


%
DF	4
 #
F     
3



    
=



a
 )
'

N
$
.


<"
G&
3
N
r   )rV   rS   Zdateutil.tzr   decimalr   r   r   Z
cmf.fieldsZcmf.includeZ#modules.gantt.fields.cmf_gantt_taskr   r%   r%   r%   r&   <module>   s   