設計組織でのタスク管理

チーフエンジニアは、タスク管理プロセスの自動化を要求しました。 彼は部下に多くの指示を出しますが、その実行プロセスを手動で制御するのは非現実的です。 すべての指示をさらに不可能に覚えておいてください。 したがって、彼はすぐに適切な自動化ツールの使用を主張しました。



自明でないタスクには、自明でないアプローチが必要でした。 カットの下の写真とソースコードを含む詳細な説明。



約200人の番号を付けた設計組織。 トップマネジメントには、チーフエンジニアの直属の部下、4人のチーフプロジェクトエンジニア(ISU)、同数のアシスタント、およびすべての部門長がいます。



1日のチーフエンジニアは、5〜15のタスクを直属の部下に委任し、直属の部下がタスクの実行を複数の部門長とその部下に委任できます。 古典的な階層スキーム。 したがって、単位時間あたりのアクティブなタスクの数は600〜800に達する可能性があります。 それらをすべて頭の中に収めるのは単純に非現実的であり、不完全なパフォーマンスの規律では、コントロールの問題が重要になります。



ただし、その時点の組織では、MS Projectが数年間使用され、短期的な割り当てではなく、プロジェクト全般を管理していました。 短時間の割り当てを管理するためにそれを使用するという考えは、簡単な議論の後で浅くなります。

通信を管理するためにeasla.comシステムを使用した経験から、タスク管理に使用することを決定しました。 さらに、タスクには通信との緊密な統合が含まれていました。



挑戦する



ある意味では、タスクではなく、問題の声明です。 最初は、テーマ、説明、作成者、出演者、予定日、実際の日付など、タスクをシンプルにすることがまだ計画されていました。 したがって、要件は簡単でした。



少し後に、すでに試用中に機能を拡張する必要があり、さらに多くの要件がありました。



一般に、タスクは一見したほど単純ではありませんでした。



解決策



まず第一に、質問は何人かのパフォーマーのタスクを引き起こしました。 すべての人に対して1つのタスクを作成する場合、つまり 個人的ではない場合、実行される可能性はほぼゼロになります。 各パフォーマーは別のものを望みます。 他の人がどうするかわかりませんが、私たちにとってはそうです。 したがって、すべてのタスクは個別である必要があるため、各エグゼキューターにタスクを複製するメカニズムを実装する必要がありました。

次に、タスクの重要性を判断する必要がありました。 3種類の重要度が導入され、それぞれに最大期限が割り当てられました。



労働も興味深いトピックです。 一部の従業員は、費やした時間を示すことが難しいと感じています。 これを説明する方法すらわかりませんが、少なすぎることを恐れているか、多すぎることを恐れているため、分析に十分な精度で近似スケールを導入しました。



ツールとプロセスの原則を決定してから、実装を開始しました。



実装



easla.comでは、新しいタスクプロセスが作成されました。 オブジェクト「Task」を作成しました。 オブジェクトには、次の属性が付与されます。



属性





連番の通常のカウンター。



指定


文字列属性 値はタスクの作成後に計算され、ユーザーが変更することはできません。 「読み取り専用」モードでは、属性は「初期化時」スクリプトで変換されます。

cobjectref()->attributeref('tsk_task_code')->readonly = true;
      
      







著者


ユーザー、つまり 組織の従業員。 著者は、組織の任意の従業員にすることができます。 従業員の完全なリストは、スクリプト「初期化中」で作成されます。

 $src_users = corganization()->users(); $end_users = array(); foreach ($src_users as $u) $end_users += array($u->id => $u->description); cobjectref()->attributeref('tsk_task_author')->values = $end_users; cobjectref()->attributeref('tsk_task_author')->value = cuser()->id; cobjectref()->attributeref('tsk_task_author')->readonly = true;
      
      





この属性にはアクティブなユーザーの値と読み取り専用モードが割り当てられているため、別の従業員からタスクを作成することはできません。



出演者


タスクの完了を任された組織の従業員。 複数の属性 異なる専門家が同じタスクを実行できます。たとえば、ISUはすべてを1つのソリューションにまとめます。 従業員のリストは、スクリプト「初期化中」で作成されます。

 $src_users = corganization()->group('group_all')->users(); $end_users = array(); foreach ($src_users as $u) if ($u['islocked'] == 0) $end_users += array($u->id => $u->description); asort($end_users); cobjectref()->attributeref('tsk_task_executor')->values = $end_users; cattributeref()->size = 6;
      
      







契約


オブジェクト「契約」へのリンク。 この属性は、Taskオブジェクトのスクリプトによって初期化されます。



テーマ


通常の文字列属性。 最初は契約番号が入力されていましたが、すぐにこの慣行を放棄し、契約番号に別の属性を導入しました。



説明


タスクの詳細な説明の複数行の文字列属性。 説明はタスクの作成者のみが変更できるため、「初期化時」および「変更時」のスクリプトには次のように記述されています。

 cattributeref()->readonly = cuser()->id != cobjectref()->attributeref('tsk_task_author')->value;
      
      







実行時に通知


整数属性。実際には、タスクの完了を作成者に通知する可能性を定義するフラグです。 有効な値のリストと初期値は、スクリプト「初期化時」で定義されています。

 cattributeref()->values = array('',''); if (empty(cattributeref()->value)) cattributeref()->value = 0;
      
      







タスクカテゴリ


タスクのカテゴリを定義する分類子。 現在、そのうちの3つのみがあります。



有効な値のリストは、スクリプト「初期化中」で生成されます。

 $src_classificators = classificatorChilds('task_category'); $end_classificators = array(); foreach($src_classificators as $c) $end_classificators += array($c['id']=>$c['name']); if (count($end_classificators) > 0) { cobjectref()->attributeref('tsk_task_category')->values = $end_classificators; cobjectref()->attributeref('tsk_task_category')->value = key($end_classificators); }
      
      





カテゴリを変更すると、「 閉じる理由」属性の「必要性」が変わります。

 $src_classificators = classificatorChilds('task_category'); foreach($src_classificators as $c) if ($c['id'] == cattributeref()->value) break; if (empty($c)) return; if ($c['code'] == 'task_category_answer') { cobjectref()->attributeref('tsk_task_base_open')->isRequired = true; }
      
      







重要性


分類子。 有効な値のリストと初期値は、スクリプト「初期化時」でも定義されています。

 $src_classificators = classificatorChilds('tsk_importance'); $end_classificators = array(); foreach($src_classificators as $c) $end_classificators += array($c['id']=>$c['name']); if (count($end_classificators) > 0) { cobjectref()->attributeref('tsk_task_importance')->values=$end_classificators; cobjectref()->attributeref('tsk_task_importance')->value = array_flip($end_classificators)['']; }
      
      





「変更するとき」、タスクの計画終了日が再集計されます。

 if (empty(cattributeref()->value)) return; cobjectref()->calcPlanEndDate(cattributeref()->value);
      
      





calcPlanEndDate関数オブジェクト自体に記述されています



オープンの理由


オブジェクトへの参照。特に、 受信ドキュメントまたは送信ドキュメント。タスクの外観の基礎になりました。 受信ドキュメントと送信ドキュメントのリストは、スクリプト「初期化中」で作成されます。

 cattributeref()->readonly = cobjectref()->attributeref('tsk_task_description')->readonly; $base = cobjectref()->prepareIncomings(); $base += cobjectref()->prepareOutgoings(); $base = array_reverse($base, true); cattributeref()->values = $base;
      
      







用途


ファイル属性。 めったに使用されませんが、付随するドキュメントをタスクに添付する必要がある場合に必要です。



開始日(計画)


タスクの開始予定日時。 スクリプト「初期化中」に記述されている現在の時刻に+1時間シフトして割り当てることに同意しました。

 cattributeref()->readonly = cobjectref()->attributeref('tsk_task_description')->readonly; cattributeref()->value = calendarDateAdd(currentDateTime(), 3600);
      
      





著者のみが変更できます。 calendarDateAdd関数に特別な注意を払い、 生産カレンダーに従って計画開始日を計算します



終了日(計画)


タスクを完了するための計画日時。 重要度と計画開始日によって異なります。 初期値は「初期化時」に計算されます:

 cattributeref()->readonly = cobjectref()->attributeref('tsk_task_description')->readonly; if (empty(cattributeref()->value) && !empty(cobjectref()->tsk_task_importance)) { cobjectref()->calcPlanEndDate(cobjectref()->tsk_task_importance); }
      
      







開始日(事実)および終了日(事実)


実際の開始日と終了日。タスクのステータスが変更されたときにのみ設定されます。



閉鎖の理由


オブジェクトへの参照、つまり、タスクを閉じるための基礎となった発信ドキュメント。 さらに、 タスクカテゴリが 「着信応答の準備」の場合終了の基準が満たされるまでタスクを終了できません。 利用可能な送信ドキュメントのリストは、スクリプト「初期化中」で作成されます。

 $base = cobjectref()->prepareIncomings(); $base += cobjectref()->prepareOutgoings(); $base = array_reverse($base, true); cattributeref()->values = $base;
      
      







人件費


タスクでエグゼキューターが費やした秒数を含む整数属性。 タスクは短期的であると想定されているため、十分な許容値があります。 属性が初期化されるときに形成されます:

 cattributeref()->values = array( 0=>'', 1=>'', 5=>' ', 15=>'15 ', 30=>'', 45=>'45 ', 60=>' ', 75=>' ', 90=>' ', 105=>'  ', 120=>'2 ', 150=>'2  30 ', 360=>'3 ', 240=>'', 480=>' ', 960=>'2 ', 1440=>'3 ', 1920=>'4 ', 2400=>' ', 4800=>' ', 7200=>' ', 9600=>' ', 19200=>' ', 19200=>' ', 28800=>' ', ); cattributeref()->value = 0;
      
      







コメント


タスクにコメントするための複数行のテキスト属性。 この属性は履歴を保存するため、いつ誰が何を書いたかを追跡できます。

それがすべての属性です!



対象



Taskオブジェクトには複雑な動作と検証があり、スクリプトで説明されています。 属性を初期化し、日付と時刻を計算するためのヘルパー関数は、スクリプトで説明されています。

オブジェクトの初期化前
 function calcTskCode($num) { return '-'.sprintf('%06d', $num); } function prepareContracts() { $src_contracts = selectAll( 'agr_management', 'agr_management_contract' ); $end_contracts = array(); foreach ($src_contracts as $s) $end_contracts += array($s['id'] => $s['description']); asort($end_contracts); return $end_contracts; } function prepareIncomings() { $src_documents = selectAll( 'crs_management', 'crs_management_incoming', array('crs_management_incoming_contragent_regnum') ); $end_documents = array(); foreach ($src_documents as $d) $end_documents += array($d['id'] => $d['description'].' ['.$d['crs_management_incoming_contragent_regnum'].']'); //asort($end_documents); return $end_documents; } function prepareOutgoings() { $src_documents = selectAll( 'crs_management', 'crs_management_outgoing' ); $end_documents = array(); foreach ($src_documents as $d) $end_documents += array($d['id'] => $d['description']); //asort($end_documents); return $end_documents; } function calcPlanEndDate($importance) { if (empty($importance)) return; $c = classificator($importance); if (empty($c)) return; $delta = 0; switch ($c['code']) { case 'tsk_importance_01': $delta = 28800; break; case 'tsk_importance_02': $delta = 144000; break; case 'tsk_importance_03': $delta = 288000; break; } cobjectref()->attributeref('tsk_task_plan_enddate')->value = calendarDateAdd(currentDateTime(), $delta); } if (cobjectref()->hasAttributeref('tsk_task_contract')) cobjectref()->attributeref('tsk_task_contract')->values = prepareContracts(); cobjectref()->childTabs = array('tsk_task_sub'); cobjectref()->childAll = false;
      
      







calcPlanEndDate関数には特に注意を払います。この関数は、 calendarDateAdd関数を使用して予定の日付と時刻を計算します。 その助けを借りて、組織の生産カレンダーを考慮に入れて、労働時間で正確に時間を計算することが可能です。



追加の属性の初期化と計画された開始日と終了日の状態の計算は、スクリプトで実行されます。

オブジェクトの初期化後
 if (cobjectref()->hasAttributeref('tsk_task_base_open') && empty(cobjectref()->attributeref('tsk_task_base_open')->value) && !empty(cobjectref()->parentrefId)) { $parent = select(cobjectref()->parentrefId); if (!empty($parent)) cobjectref()->attributeref('tsk_task_base_open')->value = $parent->attributeref('tsk_task_base_open')->value; } cobjectref()->attributeref('tsk_task_code')->value = calcTskCode(cobjectref()->attributeref('tsk_task_num')->value); if (!cobjectref()->inFinalStatus()) { if (!empty(cobjectref()->tsk_task_plan_startdate) && (cobjectref()->status->code == 'tsk_task_initiated' || cobjectref()->status->code == 'tsk_task_created')) { if (cobjectref()->tsk_task_plan_startdate instanceof DateTime) $dts_plan = date_timestamp_get(cobjectref()->tsk_task_plan_startdate); else $dts_plan = date_timestamp_get(date_create(cobjectref()->tsk_task_plan_startdate)); $dts_now = date_timestamp_get(date_create()); if ($dts_plan < $dts_now) cobjectref()->attributeref('tsk_task_plan_startdate')->state = 1; elseif ($dts_plan - $dts_now < 3600) cobjectref()->attributeref('tsk_task_plan_startdate')->state = 2; elseif ($dts_plan - $dts_now < 28800) cobjectref()->attributeref('tsk_task_plan_startdate')->state = 3; else cobjectref()->attributeref('tsk_task_plan_startdate')->state = 4; } if (!empty(cobjectref()->tsk_task_plan_enddate) && (cobjectref()->status->code == 'tsk_task_initiated' || cobjectref()->status->code == 'tsk_task_created' || cobjectref()->status->code == 'tsk_task_processed')) { if (cobjectref()->tsk_task_plan_enddate instanceof DateTime) $dts_plan = date_timestamp_get(cobjectref()->tsk_task_plan_enddate); else $dts_plan = date_timestamp_get(date_create(cobjectref()->tsk_task_plan_enddate)); $dts_now = date_timestamp_get(date_create()); if ($dts_plan < $dts_now) cobjectref()->attributeref('tsk_task_plan_enddate')->state = 1; elseif ($dts_plan - $dts_now < 3600) cobjectref()->attributeref('tsk_task_plan_enddate')->state = 2; elseif ($dts_plan - $dts_now < 28800) cobjectref()->attributeref('tsk_task_plan_enddate')->state = 3; else cobjectref()->attributeref('tsk_task_plan_enddate')->state = 4; } } if (!empty(cobjectref()->tsk_task_author)) { $pgroup = array('group_pdg'); $agroups = corganization()->user(cobjectref()->tsk_task_author)->groups(); foreach ($agroups as $ag) if (in_array($ag['code'], $pgroup)) { cobjectref()->attributeref('tsk_task_author')->readonly = false; } } if (cobjectref()->hasAttributeref('tsk_task_notice_of_execute')) { if (empty(cobjectref()->attributeref('tsk_task_notice_of_execute')->value)) { cobjectref()->attributeref('tsk_task_notice_of_execute')->value = 0; } cobjectref()->attributeref('tsk_task_notice_of_execute')->readonly = $cuser_id != cobjectref()->attributeref('tsk_task_author')->value; } if (cobjectref()->hasAttributeref('tsk_task_category')) { if (empty(cobjectref()->attributeref('tsk_task_category')->value)) { $values = cobjectref()->attributeref('tsk_task_category')->values; cobjectref()->attributeref('tsk_task_category')->value = key($values); } $category_id = cobjectref()->attributeref('tsk_task_category')->value; $category_classificator = classificator($category_id); if ($category_classificator->code == 'task_category_plan') { $ro = cuser()->id != cobjectref()->attributeref('tsk_task_author')->value; cobjectref()->attributeref('tsk_task_category')->readonly = $ro; cobjectref()->attributeref('tsk_task_plan_startdate')->readonly = $ro; cobjectref()->attributeref('tsk_task_plan_enddate')->readonly = $ro; } } cobjectref()->attributeref('tsk_task_plan_enddate')->readonly = $cuser_id != cobjectref()->attributeref('tsk_task_author')->value;
      
      







属性の開始日(計画)および終了日(計画)の状態は、現在の時刻に基づいて計算されます。 日付がなくなるとすぐに、属性は赤でペイントされますが、その前に、最初にオレンジと黄色で、このように簡単な方法でタスクを時間どおりに完了することの重要性を思い出させます。



オブジェクトを保存する前に、入力したすべての値を検証し、何か問題がある場合は保存を拒否することが重要です。

エラーの特定に加えて、トピック、発見の基礎、および実行者の3つの方法で、同様の問題の存在をチェックします。 まったく同じタスクが見つかった場合、新しいタスクの予約は拒否されます。 とても便利な機能です!

さらに、上記のように、タスクは1つのエグゼキューターのみに割り当てられ、リスト内の他の全員がそのコピーを受け取る必要があるため、タスクは1つのエグゼキューターのみで保存され、残りはオブジェクトの引数に格納されます。

ところで、パフォーマーのリストは、GUIの存在について分析されます。 そして、見つかった場合、GUIタスクがメインタスクになり、残りはすべてサブタスクとして作成されます。 このタスクの順序は、GUIにとって非常に便利です。

オブジェクトを保存する前に
 $executors = cobjectref()->attributeref('tsk_task_executor')->value; if (count($executors) > 1) { $gips = corganization()->group('group_gip_only')->users(); $fgip = false; foreach ($gips as $gip) if (in_array($gip['id'], $executors)) { $fgip = true; break; } $hgips = corganization()->group('group_gip_helper_only')->users(); $fhgip = false; foreach ($hgips as $hgip) if (in_array($hgip['id'], $executors)) { $fhgip = true; break; } if ($fgip) { cobjectref()->attributeref('tsk_task_executor')->value = $gip->id; $this->arguments['executor'] = array_diff($executors, array($gip->id)); $this->arguments['executorIsChild'] = true; } elseif ($fhgip) { cobjectref()->attributeref('tsk_task_executor')->value = $hgip->id; $this->arguments['executor'] = array_diff($executors, array($hgip->id)); $this->arguments['executorIsChild'] = true; } else { cobjectref()->attributeref('tsk_task_executor')->value = $executors[0]; $this->arguments['executor'] = array_slice($executors, 1); } } if (!empty($executors) && cobjectref()->attributeref('tsk_task_executor')->existValue != cobjectref()->attributeref('tsk_task_executor')->value) { $conditions = array( 'tsk_task_subj'=>cobjectref()->tsk_task_subj, 'tsk_task_base_open'=>cobjectref()->tsk_task_base_open, 'tsk_task_executor'=>$executors[0], ); if (!cobjectref()->isNewRecord) $conditions['id'] = '<>'.cobjectref()->id; $exist = selectAll('tsk_management', 'tsk_task', array(), $conditions); if (count($exist) > 0) { $exs_task_links = array(); $executor = corganization()->user($executors[0]); foreach ($exist as $x) { $exs_task = select($x['id']); $exs_task_links[] = $exs_task->viewLink().'  '.$executor->viewLink().' : '.$exs_task->status->viewLink(); } throw new Exception('  , ..   :'.implode('',$exs_task_links)); } } if (cobjectref()->status->code == 'tsk_task_initiated') { cobjectref()->status = 'tsk_task_created'; cobjectref()->flags = 1; } elseif (!cobjectref()->isNewRecord && (cobjectref()->status->code == 'tsk_task_created' || cobjectref()->status->code == 'tsk_task_processed')) { $src_user_id = cobjectref()->attributeref('tsk_task_executor')->existValue; $trg_user_id = cobjectref()->attributeref('tsk_task_executor')->value; $src_user_id = $src_user_id[0]; $trg_user_id = $trg_user_id[0]; if ($src_user_id != $trg_user_id) { $conditions = array( 'tsk_task_subj'=>cobjectref()->tsk_task_subj, 'tsk_task_base_open'=>cobjectref()->tsk_task_base_open, 'tsk_task_executor'=>$trg_user_id, ); $exist = selectAll('tsk_management', 'tsk_task', array(), $conditions); if (count($exist) > 0) { $exs_task_links = array(); foreach ($exist as $x) { $exs_task = select($x['id']); $exs_task_links[] = $exs_task->viewLink().'  '.corganization()->user($trg_user_id)->viewLink().' : '.$exs_task->status->viewLink(); echo '  , ..      :'.implode('',$exs_task_links); } } else { sendEmail(array( 'to'=>corganization()->user($trg_user_id), 'subj'=>cobjectref()->attributeref('tsk_task_code')->value.' ', 'body'=>'    '.corganization()->user($src_user_id)->description.'!', 'objects'=>cobjectref(), 'roles'=>'tsk_executor', 'files'=>true )); echo cobjectref()->viewLink().'    '.corganization()->user($trg_user_id)->viewLink(); } } cobjectref()->flags = 0; } else cobjectref()->flags = 0; cobjectref()->description = cobjectref()->attributeref('tsk_task_code')->value;
      
      







タスクのコピーが割り当てられる実行者のリストの一時的な避難場所は次のとおりです。

 $this->arguments['executor']
      
      





オブジェクトには、このような「引数」を好きなだけ作成できます。 私の場合、1つで十分でした。



オブジェクトを保存した後、必要に応じてタスクのクローンが作成され、通知が送信されます。

オブジェクトを保存した後
 if (cobjectref()->hasAttributeref('tsk_task_base_open') && !empty(cobjectref()->attributeref('tsk_task_base_open')->value)) { $base = select(cobjectref()->attributeref('tsk_task_base_open')->value); if (!empty($base) && ($base->status->code == 'crs_management_incoming_handed' || $base->status->code == 'crs_management_incoming_created')) { $base->status = 'crs_management_incoming_exec'; $base->save(); } } if ((cobjectref()->status->code == 'tsk_task_created') && (cobjectref()->flags == 1)) { $to = cobjectref()->attributeref('tsk_task_executor')->value; $to = corganization()->user(is_array($to) ? $to[0] : $to); sendEmail(array( 'to'=>$to, 'subj'=>cobjectref()->attributeref('tsk_task_code')->value.' ', 'body'=>'   !', 'objects'=>cobjectref(), 'roles'=>'tsk_executor', 'files'=>true )); echo cobjectref()->viewLink().'    '.$to->description; if (!empty(cobjectref()->attributeref('tsk_task_base_open')->value)) { $base = select(cobjectref()->attributeref('tsk_task_base_open')->value); if (is_null($base)) throw new Exception('        '); if ($base->code == 'crs_management_incoming') { if ($base->status->code != 'crs_management_incoming_ok') { $base->status = 'crs_management_incoming_ok'; $base->save(); } } } } if (isset($this->arguments['executor'])) { $executors = $this->arguments['executor']; $ischild = isset($this->arguments['executorIsChild']) ? $this->arguments['executorIsChild'] : false; if (count($executors) > 0) { $new_task_links = array(); $exs_task_links = array(); foreach ($executors as $e) { $conditions = array( 'tsk_task_subj'=>cobjectref()->tsk_task_subj, 'tsk_task_base_open'=>cobjectref()->tsk_task_base_open, 'tsk_task_executor'=>$e, ); $exist = selectAll('tsk_management', 'tsk_task', array(), $conditions); if (count($exist) > 0) { foreach ($exist as $x) { $exs_task = select($x['id']); $exs_task_links[] = $exs_task->viewLink().'  '.corganization()->user($e)->viewLink().' : '.$exs_task->status->viewLink(); } } else { $new_task = new Objectref(); $new_task->prepare(objectDef('tsk_management','tsk_task')); $new_task->attributeref('tsk_task_author')->value = cobjectref()->tsk_task_author; $new_task->attributeref('tsk_task_contract')->value = cobjectref()->tsk_task_contract; $new_task->attributeref('tsk_task_subj')->value = cobjectref()->tsk_task_subj; $new_task->attributeref('tsk_task_description')->value = cobjectref()->tsk_task_description; $new_task->attributeref('tsk_task_category')->value = cobjectref()->tsk_task_category; $new_task->attributeref('tsk_task_importance')->value = cobjectref()->tsk_task_importance; $new_task->attributeref('tsk_task_plan_startdate')->value = cobjectref()->tsk_task_plan_startdate; $new_task->attributeref('tsk_task_plan_enddate')->value = cobjectref()->tsk_task_plan_enddate; $new_task->attributeref('tsk_task_executor')->value = $e; $new_task->attributeref('tsk_task_comment')->value = cobjectref()->tsk_task_comment; $new_task->attributeref('tsk_task_base_open')->value = cobjectref()->tsk_task_base_open; $new_task->save(); if ($ischild === true) cobjectref()->childAdd($new_task); else { $parents = cobjectref()->parents(); if (!empty($parents)) { $p = select($parents[0]['id']); $p->childAdd($new_task); } } $new_task_links[] = $new_task->viewLink().'  '.corganization()->user($e)->viewLink(); } } if (count($exs_task_links) > 0) echo '.   , ..  :'.implode('',$exs_task_links); } } if (cobjectref()->hasAttributeref('tsk_task_notice_of_execute')) { if (cobjectref()->attributeref('tsk_task_notice_of_execute')->value == 1) { if (cobjectref()->status->code == 'tsk_task_ok') sendEmail(array( 'to'=>corganization()->user(cobjectref()->attributeref('tsk_task_author')->value), 'subj'=>cobjectref()->attributeref('tsk_task_code')->value.' ', 'body'=>'   !', 'objects'=>cobjectref(), 'roles'=>'tsk_executor', )); } } if (cobjectref()->status->code == 'tsk_task_failed') sendEmail(array( 'to'=>corganization()->user(cobjectref()->attributeref('tsk_task_author')->value), 'subj'=>cobjectref()->attributeref('tsk_task_code')->value.' ', 'body'=>'   !', 'objects'=>cobjectref(), 'roles'=>'tsk_executor', ));
      
      







最終的に、オブジェクトの形状は次のようになり始めました。





ステータス



彼は、作成、受け入れ、完了、拒否というステータスで賢くはなりませんでした。 Acceptedは、従業員がレビューして実行を承認したタスクです。 残りのステータスは、名前から明らかです。





アクション



ステータスの数が少ないと、プロセスの理解が深まり、アクションの数が減ります。 アクションとストレートが少し判明しました。



受け入れる


目的は、実際には名前に由来しています。 タスクを「承認済み」ステータスに移行し、作業の実際の開始日を設定します。

 cobjectref()->status = 'tsk_task_processed'; cobjectref()->attributeref('tsk_task_startdate')->value = currentDateTime();
      
      







走る


タスクの実際の終了日を修正することにより、タスクを正常に終了します。 必ずコメントが必要です。 これは、チーフエンジニアの要件の1つでした。 「最初のカップル」では、部下がコメントなしでタスクを閉じたとき、彼は非常にinしました。 何が行われ、どのような根拠でタスクが閉じられたかは完全に理解できませんでした。

さらに、アクションはタスクがネストされているかどうかを確認し、ネストされている場合は、それに隣接するすべてのタスクが完了しているかどうかを確認します。 結果が正の場合、親タスクのステータスを確認し、必要に応じて、タスクが閉じられる可能性があることを通知する通知を実行者にメールで送信します。 ネストされたタスクはすべて完了します。

 if (cobjectref()->hasAttributeref('tsk_task_efforts')) { if (empty(cobjectref()->attributeref('tsk_task_efforts')->value)) throw new Exception("    !"); } $src_classificators = classificatorChilds('task_category'); foreach($src_classificators as $c) if ($c['id'] == cobjectref()->attributeref('tsk_task_category')->value) break; if (empty($c)) throw new Exception("   !"); if ($c['code'] == 'task_category_answer') { cobjectref()->attributeref('tsk_task_base_open')->isRequired = true; if (empty(cobjectref()->attributeref('tsk_task_comment')->value) || empty(cobjectref()->attributeref('tsk_task_base_close')->value)) { echo '    '.$c->useLink().'       !'; caction()->redirect = cobjectref()->updateUrl(); return; } } elseif (empty(cobjectref()->attributeref('tsk_task_comment')->value)) { echo '     !'; caction()->redirect = cobjectref()->updateUrl(); return; } if (empty(cobjectref()->attributeref('tsk_task_startdate')->value)) { cobjectref()->attributeref('tsk_task_startdate')->value = currentDateTime(); } cobjectref()->attributeref('tsk_task_enddate')->value = currentDateTime(); cobjectref()->status = 'tsk_task_ok'; $parents = cobjectref()->parents(); if (!empty($parents)) { $parentId = $parents[0]['id']; $childTasks = selectAll('tsk_management','tsk_task',array(),array( 'parents'=>$parentId, 'status'=>array('tsk_task_initiated','tsk_task_created','tsk_task_processed') )); if (!empty($childTasks)) { $parentTask = select($parentId); if (in_array($parentTask->status->code, array('tsk_task_initiated','tsk_task_created','tsk_task_processed'))) { sendEmail(array( 'to'=>corganization()->user($parentTask->attributeref('tsk_task_executor')->value[0]), 'subj'=>$parentTask->attributeref('tsk_task_code')->value.'   ?', 'body'=>',  '.$parentTask->viewLink().'   , ..      !', )); } } }
      
      







拒否する


名前から明らかなように、アクションはタスクを拒否します。 1つの条件:コメントは拒否の理由を示す必要があります。

 if (empty(cobjectref()->attributeref('tsk_task_comment')->value)) echo '     .'; else { cobjectref()->status = 'tsk_task_failed'; cobjectref()->attributeref('tsk_task_enddate')->value = currentDateTime(); }
      
      







戻る


このアクションは、プロセスマネージャーのみが使用できます。タスクを最終ステータスから承認済みステータスに戻すことができます。タスクが誤って閉じられた場合に必要です。まれに必要ですが、それでも必要です。

 cobjectref()->status = 'tsk_task_processed'; cobjectref()->attributeref('tsk_task_enddate')->value = null; <h5> </h5>       .      . $task = cobjectref(); $new_task = new Objectref(); $new_task->prepare(objectDef('tsk_management','tsk_task')); $new_task->parentrefId = $task->id; $new_task->attributeref('tsk_task_description')->value = $task->attributeref('tsk_task_description')->value; if ($task->hasAttributeref('tsk_task_contract')) $new_task->attributeref('tsk_task_contract')->value = $task->attributeref('tsk_task_contract')->value; if ($task->hasAttributeref('tsk_task_subj')) $new_task->attributeref('tsk_task_subj')->value = $task->attributeref('tsk_task_subj')->value; if ($task->hasAttributeref('tsk_task_category')) $new_task->attributeref('tsk_task_category')->value = $task->attributeref('tsk_task_category')->value; if ($task->hasAttributeref('tsk_task_base_open')) $new_task->attributeref('tsk_task_base_open')->value = $task->attributeref('tsk_task_base_open')->value; $new_task->attributeref('tsk_task_plan_startdate')->value = $task->attributeref('tsk_task_plan_startdate')->value; $new_task->attributeref('tsk_task_plan_enddate')->value = $task->attributeref('tsk_task_plan_enddate')->value; $new_task->status = 'tsk_task_initiated'; caction()->redirect = urlNewObjectref($new_task);
      
      







チーム



チームを作成する必要があった最初のプロセス。コマンドはアクションとは異なり、オブジェクトではなくプロセスのコンテキストで実行されます。したがって、オブジェクトを「バッチ」で処理することができます。すべてを一度に、またはユーザーが選択します。



実際には、「タスク」プロセスを発信レターの「通信」と統合する場合、指定された受信ボックスに応答して発信レターを送信するときに、対応する受信ボックスに基づいて作成されたタスクにコメントを付け、発信を設定するアルゴリズムが実装されました手紙。 Uff ...言い換えれば、着信者がコメントと完了理由を受け取ったときに作成されたタスク。次のような非常に便利な機能多くの場合、ISUは非常に忙しいため、今すぐタスクを終了する時間がないため、「手が届いた」ときに、どの基準でタスクを終了するかを覚えておく必要があります。忘れたり覚えたりしないようにすべてが時間通りに行われなければならないことは明らかですが、それが起こった場合は、メモリ内の画像の復元に費やす時間を減らす必要があります。



このようなクローズの理由の自動入力の結果として、GUIは閉じる準備ができたタスクを蓄積します。残っているのは「ボタンを押すだけ」です(これは、チーフエンジニアが各タスクの人件費を示すまで必要でした)。そのような既製のタスクが多数ある場合、それらの閉鎖は連続的な「マウスポッキング」に変わりました。したがって、「Close Ready」チームが作成されました。



準備完了


チームはすぐに終了できるタスクを検索し、最初の10個を終了します。閉鎖の少なくとも一部の制御を維持するためだけではありません。この方法でタスクを閉じると、GUIがキャッチされた場合がありましたが、遅すぎて手動でタスクを返す必要がありました。

準備完了
 $readyTasks = selectAll( 'tsk_management', 'tsk_task', array(), array( 'tsk_task_executor'=>array('id',cuser()->id), 'tsk_task_base_close'=>array('not like','is not null'), 'status'=>array('and','<>tsk_task_ok','<>tsk_task_failed') ) ); // debugMode(true); // debug($readyTasks); $success = array(); $failed = array(); $max = 10; $q = 1; foreach ($readyTasks as $task) { $obj = select($task['id']); progress($q/$max * 100, $task['description']); if (!empty($obj)) { $obj->attributeref('tsk_task_efforts')->value = 1; $obj->attributeref('tsk_task_enddate')->value = currentDateTime(); $obj->status = 'tsk_task_ok'; try { $obj->save(); $success[] = $obj->viewLink(); } catch (Exception $e) { $failed[] = $obj->viewLink(); } } else { $failed[] = $task['description']; } $q++; if ($q > $max) break; } if (count($success) > 0) { echo("   :".implode(", ",$success).": ".count($success)); } if (count($failed) > 0) { warning("  :".implode(", ",$failed).": ".count($failed)); }
      
      







このコマンドは下部のビューで利用できます(下のスクリーンショットを参照)。



選択を閉じる


まったく同じコマンドですが、ユーザーが選択したタスクのみを閉じます。

選択を閉じる
 $objectrefIds = ccommand()->objectrefIds; $success = array(); $failed = array(); $cnt = count($objectrefIds); $q = 1; if ($cnt == 0) throw new Exception('  !'); foreach ($objectrefIds as $objectrefId) { $obj = select($objectrefId); progress($q/$cnt * 100, $obj['description']); if (!empty($obj)) { $obj->attributeref('tsk_task_enddate')->value = currentDateTime(); $obj->status = 'tsk_task_ok'; try { $obj->save(); $success[] = $obj->viewLink(); } catch (Exception $e) { $failed[] = $obj->viewLink(); } } else { $failed[] = $objectrefId; } $q++; if ($q > $cnt) break; } if (count($success) > 0) { echo("   : ".implode(", ",$success).": ".count($success)); } if (count($failed) > 0) { warning("  : ".implode(", ",$failed).": ".count($failed)); }
      
      











タイプ(選択)、オブジェクトを介したナビゲーションの重要なコンポーネント。ユーザーの要件に応じて、タスクのリストの表示を柔軟にカスタマイズできます。



私のタスク


「インテリジェントな」外観、なぜなら 開いたアクティブユーザーの位置を分析します。部門長によって開かれた場合、部門名と部門長のカテゴリを追加します。したがって、部門の長は、自分のタスクだけでなく、すべての部下に割り当てられたタスクも見ることができます。

ちなみに、このタイプの存在は、ユーザー、特にチーフエンジニア、GUI、部門長が提唱する重要な要件の1つでした。これは、個々のタスクの管理によって強化された他のシステムでの実装が常に可能であるとは限りません。

私のタスク
 $groups = cuser()->groups(); $isgip = false; $ishead = false; foreach($groups as $group) if (strncmp($group['data_one'],'09.',3) == 0) { $isgip = true; break; } elseif (strcmp($group['code'],'group_head_and_deputy') == 0) { $ishead = true; break; } $attributes = array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_base_open'=>array('link'=>'value'), 'tsk_task_contract'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160) ); if ($isgip) { $categories = array($group->name, cuser()->description); $us = array('id'); foreach ($group->users() as $u) $us[] = $u['id']; cviewpub()->categories = $categories; cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; switch(cviewpub()->category) { case 0: $attributes += array('tsk_task_executor'=>array('link'=>'value','inplaceEdit'=>true,'options'=>array('style'=>'width: 30%;'))); $conditions = array('tsk_task_executor'=>$us); break; case 1: $conditions = array('tsk_task_executor'=>array('id',cuser()->id)); break; } } elseif ($ishead) { foreach($groups as $group) if (is_numeric($group['data_one'])) break; $us = array('id'); foreach ($group->users() as $u) $us[] = $u['id']; $categories = array($group->name, cuser()->description); cviewpub()->categories = $categories; cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; switch(cviewpub()->category) { case 0: $attributes += array('tsk_task_executor'=>array('link'=>'value','inplaceEdit'=>true,'options'=>array('style'=>'width: 30%;'))); $conditions = array('tsk_task_executor'=>$us); break; case 1: $conditions = array('tsk_task_executor'=>array('id',cuser()->id)); break; } } else { $categories = array(cuser()->description); $conditions = array('tsk_task_executor'=>array('id',cuser()->id)); } $attributes += array( 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'tsk_task_base_close'=>array('link'=>'value'), 'tsk_task_comment', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ); $conditions['status'] = array('tsk_task_initiated','tsk_task_created','tsk_task_processed'); cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>$attributes, 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_contract'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'conditions'=>$conditions, 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true, ));
      
      











私に指名された


ビューには、アクティブユーザーが作成したタスクのリストが表示されることが名前から明らかです。また、「知的」でもあります。アクティブなユーザーを分析し、GUIの場合、GUIグループとGUIのフルネームという2つのカテゴリを追加します。ISUが個人タスクとアシスタントに割り当てられたタスクの両方を個別に表示できるように、カテゴリが必要です。ISUとそのアシスタントは、同じタスクプールで作業しています。

私に指名された
 $groups = cuser()->groups(); $isgip = false; foreach($groups as $group) if (strncmp($group['data_one'],'09.',3) == 0) { $isgip = true; break; } if ($isgip) { $categories = array($group->name, cuser()->description); $us = array(); foreach ($group->users() as $u) $us[] = $u['id']; cviewpub()->categories = $categories; cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; switch(cviewpub()->category) { case 0: $conditions = array('tsk_task_author'=>$us); break; case 1: $conditions = array('tsk_task_author'=>cuser()->id); break; } } else { $categories = array(cuser()->description); $conditions = array('tsk_task_author'=>cuser()->id); } $conditions['status'] = array('tsk_task_initiated','tsk_task_created','tsk_task_processed'); cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_base_open'=>array('link'=>'value'), 'tsk_task_contract'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160,'inplaceEdit'=>true), //'tsk_task_description'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ), 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_executor'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'conditions'=>$conditions, 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true, ));
      
      











私の完成


シンプルなビュー。実行者がアクティブなユーザーである完了したタスクのリストを表示します。

私の完成
 cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_base_open'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'tsk_task_startdate', 'tsk_task_enddate' ), 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'desc', 'enable'=>true) ), 'conditions'=>array( 'tsk_task_executor'=>cuser()->id, 'status'=>'tsk_task_ok' ), 'sorting'=>true, 'pagination'=>array('pagesize'=>20) ));
      
      









すべてのタスク


すべてのアクティブなタスクの完全なリストを表示します。原則として、他の人のタスクを検索するために使用されます。

すべてのタスク
 cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_base_open'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ), 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_executor'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'conditions'=>array( 'status'=>array('tsk_task_initiated','tsk_task_created','tsk_task_processed') ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true, ));
      
      









すべての計画ソリューション


次のような非常に重要なタスクのリスト 計画会議の決定によって割り当てられ、制御下にあるタスクのみが含まれます。スクリプトが実行されると、ビューはすべてのタスクのリストを分析し、そこからすべてのエグゼキューターを選択し、部門ごとにグループ化し、部門名でビューカテゴリのリストを作成します。

したがって、各従業員、および原則として、これらは部門のヘッドおよびチーフスペシャリストであり、タスクのリスト全体から自分のタスクのみを簡単に除外できます。

すべての計画ソリューション
 $src_executors = selectColumnAll('tsk_management','tsk_task','tsk_task_executor', array( 'status'=>array('tsk_task_initiated','tsk_task_created','tsk_task_processed'), 'tsk_task_category'=>array('task_category_plan') ) ); $end_executors = array(); foreach ($src_executors as $u) { $end_executors[] = $u['id']; } $users = corganization()->users($end_executors); $departments = array(0=>''); foreach ($users as $u) { foreach ($u->departments as $d) { $departments[$d['id']] = $d['name']; } } cviewpub()->categories = $departments; cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; $conditions = array( 'status'=>array('tsk_task_initiated','tsk_task_created','tsk_task_processed'), 'tsk_task_category'=>array('task_category_plan') ); if (cviewpub()->category != '0') { $department = corganization()->department(cviewpub()->category); if (!empty($department)) { $users = $department->users(); $c = array('id'); foreach ($users as $u) $c[] = $u['id']; $conditions['tsk_task_executor'] = $c; } } cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_contract'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'tsk_task_startdate', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ), 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_executor'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'conditions'=>$conditions, 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true, ));
      
      











建築監督


ISUが現場監督の見積もりを準備するために使用することは、非常に重要かつ困難な見解です。ビューの複雑さは、タスクに関する情報だけでなく、タスクの開閉の理由に関する情報も実際に表示するという事実にあります。手紙について。したがって、フォームには3つのオブジェクトに関する情報が一度に表示されます!Easla.comは、「オブジェクト」タイプの属性を使用し、ネストされたオブジェクトの属性のタイプの説明でドットを指定するときにこの機会を提供します。

建築監督
 cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_contract'=>array('link'=>'value'), 'tsk_task_contract.agr_management_contract_title'=>array('header'=>' ','limit'=>'30'), 'tsk_task_contract.agr_management_contract_contragent'=>array('header'=>''), 'tsk_task_contract.agr_management_contract_project_manager'=>array('header'=>''), 'tsk_task_subj'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_executor.email', 'tsk_task_base_open'=>array('link'=>'value','export'=>array('id','crs_management_incoming_contragent_regnum')), 'tsk_task_base_open.crs_management_incoming_receive_date'=>array('header'=>'  '), 'tsk_task_base_close'=>array('link'=>'value','export'=>array('id','crs_management_outgoing_regnum')), 'tsk_task_base_close.crs_management_outgoing_sentdate', 'tsk_task_efforts', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ), 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_executor'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'conditions'=>array( 'tsk_task_base_close'=>array('crs_management_outgoing_content'=>684) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true, ));
      
      







ところで、表示設定で使用されるエクスポートオプションに注意を払います。必要なすべての列を表示するようにビューを調整すると、幅が非常に大きくなり、ワイドスクリーン画面に収まらなくなります。ただし、ビューをExcelにエクスポートする場合、多くの列が必要です。エクスポートオプションを使用して解決されます。ビューに表示される代わりにエクスポートする列を示します。かっこいい!





メインメニューからユーザーが使用できる一般的なタイプに加えて、追加します。easla.comの用語でのオブジェクトのビュー-オブジェクトのコンテキスト。彼らの助けを借りて、着信および発信文字の形式に依存するタスクのリストを表示することができました。選択した、たとえば着信文書に基づいてどのタスクが割り当てられ、現在どのような状態にあるかを分析する必要がある場合に非常に便利です。

ドキュメントタスク
 cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_subj'=>array('limit'=>160), //'tsk_task_description'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ), 'sort'=>array( 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), ));
      
      











さらに、サブタスクは、システムがデフォルトで提供するよりも詳細に表示する必要があったため、追加のビューが作成されました。

サブタスク
 $attributes = array( 'tsk_task_code'=>array('link'=>'object','options'=>array('style'=>'width: 10%;')), 'tsk_task_base_open'=>array('link'=>'value'), 'tsk_task_subj'=>array('limit'=>160), 'tsk_task_executor'=>array('link'=>'value','options'=>array('style'=>'width: 10%;')), 'tsk_task_plan_startdate', 'tsk_task_plan_enddate', 'status'=>array('link'=>'value', 'actions'=>array(),'options'=>array('style'=>'width: 100px;')) ); cviewpub()->exec(array( 'object'=>objectDef('tsk_management','tsk_task'), 'attributes'=>$attributes, 'sort'=>array( 'tsk_task_code'=>array('enable'=>true), 'tsk_task_base_open'=>array('enable'=>true), 'tsk_task_subj'=>array('enable'=>true), 'tsk_task_plan_startdate'=>array('enable'=>true), 'tsk_task_plan_enddate'=>array('default'=>'asc', 'enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>20), 'showcreate'=>true, ));
      
      











役割



マネージャー、参加者、オブザーバーの3つのロールのみがあります。もちろん、マネージャーは何でもできます。参加者はタスクを削除することはできません。オブザーバーはタスクのみを表示できます。



もちろん、このアクセス権の分割により、各従業員は他の人のタスクを変更する権利を持ちます。しかし、プロセスと会社のチームの内部関係に表明されたすべての要件を分析すると、重要な制裁なしに誰も何も変更できないように権利を変更できる可能性があるという結論に達しました。 「履歴」属性を使用して、タスクをtasks意性から保護します。つまり、誰かがタスク内の何かを変更した場合、トレースが残り、それがどのようになったか、どのようになったか、誰が変更されたかを確認できます。



まとめ



個人的には、自動化プロジェクトの成功の主な基準は、署名済みの行為または「完了」というスタンプの付いた参照条件ではなく、実行中のシステムを実行しているという事実です。

現在、タスクプロセスは、プロセスのすべての参加者によって積極的に使用されています。これの大きなメリットは、最も重要なエンジニアであり、個人的な例で他の全員を訓練します。ところで、彼はaddの主要なサプライヤの1人です。プロセス要件。

非常に嬉しい驚きは、データをアップロードしてMicrosoft Excelで推定値を生成するために使用される「建築監督」タイプの外観でした。そのような見積もりは、組織にかなりの利益をもたらすことが判明しました。

ちなみに、「目的」オブジェクトに加えて、説明されたプロセスの一部として、「知人」オブジェクトが実装されました。これにより、実行の制御を知っている文書を送信できます。しかし、それについては別途。



All Articles