Microsoft Project Serverに基づいたProject Officeツールの作成

こんにちは



今日は、プロジェクトの人件費の計画と会計にProject Serverを使用した経験、タスクにすばやく設定する方法、そしてその結果、明確なイメージを得ました:マネージャーは会社の仕組み、プロジェクトの成功度、それぞれの効果を確認します要求された期間などの個々の従業員



EastBanc TechnologiesのProject Server使用率の履歴と統計


2005年からProject Serverを使用して、異なるタイムゾーン(ロシアとアメリカ)にある2つのオフィスで構成される企業グループ内の時間追跡と作業計画を行っています。 また、一時的に引き付けられる請負業者のシステムも考慮します。



サンプル統計:


システム内の合計プロジェクト-603、



従業員-216、



毎週チェックするためのタイムシート(タイムシート、タイムシールド)-140、



週ごとのタスク260。



ワークフローは次のようになります。Projectで各プロジェクトを開始し、そこにプロジェクトチームのすべてのメンバーを含め、プロジェクト計画(タスク、計画日、人件費)を作成します。 従業員は、各作業日のプロジェクトタスクに費やされた作業時間に関する情報を定期的に入力します。いわゆるタイムシート、タイムシートに記入します。



従業員にとっては、日ごとにタスクが割り当てられたテーブルに数字を並べるようなものです。 必要に応じて、プロジェクト内のタスクを自分で開始できます。 そして、これは従業員にとっては忘れがちな「官僚的な」ルーチンであるため、自動メール通知が設定されます。



従業員から送信されたデータに基づいて、OLAPキューブが構築されます。これにより、マネージャーは数回クリックするだけでさまざまな形式のレポートを設計できます。実際、マネージャーの頭に浮かぶあらゆるニーズに合わせてレポートを収集し、あらゆるコンテキストで便利な形式で情報を分析できます。



このソリューションをProject Serverに実装するためのツール


上記のニーズに合わせてProject Serverをカスタマイズするために何をしましたか?



関心のあるセクションでレポートを作成するために、Project Server 2010が提供するいくつかの技術機能があります。



1. Project Serverデータベースの1つを使用する( ここでの構成方法について)。



最終的に、 かなり単純な要求が行われます。



SELECT DISTINCT EpmResource.ResourceTimesheetManagerUID, MSP_EpmResource.ResourceName FROM MSP_EpmResource INNER JOIN MSP_EpmResource AS EpmResource ON MSP_EpmResource.ResourceUID = EpmResource.ResourceTimesheetManagerUID AND EpmResource.ResourceUID <> EpmResource.ResourceTimesheetManagerUID ORDER BY
      
      







ピボットテーブルを使用した結果は、Excel Servicesで公開されます。







2. Project Serverに組み込まれたOLAPキューブを使用します( ここでの構成方法)。



Excelでは、次のようになります。







分析キューブで使用可能なフィールドのリスト:







私たちの場合、両方の方法に重大な欠点があるという事実に直面しています。



データ構造に関して:



  1. 「解雇」または「勤務」を理由に従業員をフィルタリングできることが必要です。 会社の10年にわたる歴史の中で、Project Serverのユーザーベースはかなり大きなアーカイブを蓄積してきました。
  2. 時間の測定では、会計期間ではなく実際の月のレポートを作成できる必要があります。 これらは、会計のタイムシートの基礎を形成し、その後1Cをロードします。
  3. 従業員ディメンションでは、次のことを理解する必要があります。 会社の構造単位に属する:ロシア、アメリカ、または外部の契約組織。 b。 1Cの従業員の従業員番号。 c。 時間通りに不完全なレポートに関する通知を送信するための電子メールアドレス。
  4. プロジェクトの目的。


Excel Servicesのパブリケーションには1つですが、重要な欠点があります。大量のデータ(上記のシステムのプロジェクト、従業員、タスクの数を参照)では、フィルターを適用するとデータベースでクエリが発生し、結果を待ってビルドする時間ですレポート。



OLAPキューブにはニュアンスもあります。たとえば、1つのタスクや別のタイムシートなど、データが散在する一連の異なるキューブに分割されます。 一般に、キューブは、アドホック作業よりもポートフォリオ分析に重点を置いています。



ニーズを満たすために行ったこと:


OLAPキューブを構築する基礎として、MicroSoftからProject Serverに標準のSQLクエリを使用し、ニーズに合わせてわずかに変更しました。 頻度では、期間内に変更が行われました。 私たちにとって重要なのは、実際の週と就業週の2つのディメンションを持つことです。従業員のメールと「解雇」のサインを追加しました。



コード
 SELECT R.StartDate, R.EndDate, R.PeriodName, R.TimeByDay, R.TimeByDay_DayOfWeek, R.ActualWorkBillable, R.ProjectName, R.TS_LINE_CACHED_ASSIGN_NAME + ' (' + R.ProjectName + ')' AS TaskName, R.Status, R.ProjectAccount, R.Location, R.ResourceCompany, R.EmployeeID, R.TS_LINE_CLASS_NAME, R.Type, R.ProjectOwner, R.Firstname, R.LastName, R.ResourceName, R.ModifiedDate, R.ResourceNameUID, DATEPART(yyyy, R.EndDate) AS PeriodYear, DATEPART(mm, R.EndDate) AS PeriodMonth, DATEPART(ww, R.EndDate) AS PeriodWeek, DATEPART(yyyy, R.TimeByDay) AS RealPeriodYear, DATEPART(mm, R.TimeByDay) AS RealPeriodMonth, DATEPART(ww, R.TimeByDay) AS RealPeriodWeek, R.ProjectStatus, CASE WHEN ISNULL(R.RES_TERMINATION_DATE, GETDATE()) >= GETDATE() THEN 0 ELSE 1 END AS IsFire, R.WRES_EMAIL AS Email FROM (SELECT R_1.RES_HIRE_DATE, R_1.RES_TERMINATION_DATE, R_1.WRES_EMAIL, R_1.TODAY, R_1.IsActive, R_1.StartDate, R_1.EndDate, R_1.PeriodName, R_1.TimeByDay, R_1.TimeByDay_DayOfWeek, R_1.ActualWorkBillable, R_1.ProjectName, R_1.[Task Name], R_1.Status, R_1.ProjectAccount, R_1.Location, R_1.ResourceCompany, R_1.EmployeeID, R_1.TS_LINE_CACHED_ASSIGN_NAME, R_1.TS_LINE_CLASS_NAME, R_1.Type, R_1.TimesheetClass, R_1.ProjectOwner, R_1.Firstname, R_1.LastName, R_1.ResourceName, R_1.ModifiedDate, R_1.ResourceNameUID, R_1.ResourceCC, R_1.CostCenter, R_1.ProjectType, CASE WHEN R_1.ProjectStatus IS NULL THEN 'Undefined' ELSE R_1.ProjectStatus END AS ProjectStatus, R_1.TS_LINE_UID, DATEADD(day, - MIN(DATEDIFF(day, T.EFFECTIVE_DATE, R_1.TimeByDay)), R_1.TimeByDay) AS EFFECTIVE_DATE FROM (SELECT res.RES_HIRE_DATE, res.RES_TERMINATION_DATE, res.WRES_EMAIL, GETDATE() AS TODAY, CASE WHEN (res.RES_TERMINATION_DATE > GETDATE() OR res.RES_TERMINATION_DATE IS NULL) THEN 'Active' ELSE 'Inactive' END AS IsActive, tpr.WPRD_START_DATE AS StartDate, tpr.WPRD_FINISH_DATE AS EndDate, tpr.WPRD_NAME AS PeriodName, ISNULL(tla.TS_ACT_START_DATE, tpr.WPRD_START_DATE) AS TimeByDay, DATEPART(weekday, tla.TS_ACT_START_DATE) AS TimeByDay_DayOfWeek, tla.TS_ACT_VALUE / 60000 AS ActualWorkBillable, CASE tcl.TS_LINE_CLASS_NAME WHEN 'Standard' THEN tp.PROJ_NAME ELSE tcl.TS_LINE_CLASS_NAME END AS ProjectName, tsk.TASK_NAME AS [Task Name], CASE t .TS_STATUS_ENUM WHEN 0 THEN 'InProgress' WHEN 1 THEN 'Submitted' WHEN 2 THEN 'Acceptable' WHEN 3 THEN 'Approved' WHEN 4 THEN 'Rejected' WHEN 5 THEN 'Pending' ELSE 'Missing' END AS Status, CASE tcl.TS_LINE_CLASS_NAME WHEN 'Standard' THEN PP.ProjectAccount WHEN 'Administrative & General' THEN '0700-000' WHEN 'Bench time' THEN '0702-000' WHEN 'Holidays' THEN '0500-000' WHEN 'Internal Projects' THEN '0701-000' WHEN 'Pre-sales & Overhead' THEN '0600-000' WHEN 'Recruitment (interview)' THEN '0703-000' WHEN 'Sales activity' THEN '0704-000' WHEN 'Vacation' THEN '0209-000' ELSE PP.ProjectAccount END AS ProjectAccount, C.Location, RC.ResourceCompany, E.EmployeeID, tl.TS_LINE_CACHED_ASSIGN_NAME, tcl.TS_LINE_CLASS_NAME, tcl.TS_LINE_CLASS_TYPE AS Type, tcltop.TS_LINE_CLASS_NAME AS TimesheetClass, pr_owner.RES_NAME AS ProjectOwner, SUBSTRING(tr.RES_NAME, 0, CHARINDEX(' ', tr.RES_NAME)) AS Firstname, SUBSTRING(tr.RES_NAME, CHARINDEX(' ', tr.RES_NAME) + 1, LEN(tr.RES_NAME)) AS LastName, SUBSTRING(tr.RES_NAME, CHARINDEX(' ', tr.RES_NAME) + 1, LEN(tr.RES_NAME)) + ' ' + SUBSTRING(tr.RES_NAME, 0, CHARINDEX(' ', tr.RES_NAME)) AS ResourceName, t.MOD_DATE AS ModifiedDate, tr.RES_UID AS ResourceNameUID, tr.ResourceCC, PCC.CostCenter, PT.ProjectType, PPS.ProjectStatus as ProjectStatus, tla.TS_LINE_UID FROM pub.MSP_WEB_TIME_PERIODS AS tpr CROSS JOIN (SELECT RES_UID, RES_NAME, CASE WHEN tr.ResourceCC = 4 OR tr.ResourceCC = 6 THEN 'Only DC' ELSE CASE WHEN tr.ResourceCC = 1 THEN 'Only NSK' ELSE 'DC & NSK' END END AS ResourceCC FROM (SELECT RES_UID, RES_NAME, SUM(CASE WHEN tr.CostCenter IS NULL THEN 4 ELSE CASE WHEN tr.CostCenter = 'DC' THEN 2 ELSE 1 END END) AS ResourceCC FROM (SELECT DISTINCT tr.RES_UID, tr.RES_NAME, PCC.CostCenter FROM pub.MSP_RESOURCES AS tr INNER JOIN pub.MSP_PROJECT_RESOURCES AS pr ON pr.RES_UID = tr.RES_UID INNER JOIN pub.MSP_PROJECTS AS p ON p.PROJ_UID = pr.PROJ_UID LEFT OUTER JOIN (SELECT pspPrjCFV.PROJ_UID, psLV.LT_VALUE_TEXT AS CostCenter FROM pub.MSP_PROJ_CUSTOM_FIELD_VALUES AS pspPrjCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS pspCF ON pspPrjCFV.MD_PROP_UID = pspCF.MD_PROP_UID LEFT OUTER JOIN pub.MSP_LOOKUP_TABLE_VALUES AS psLV ON psLV.LT_STRUCT_UID = pspPrjCFV.CODE_VALUE WHERE (pspCF.MD_PROP_NAME = 'Cost_Center')) AS PCC ON PCC.PROJ_UID = p.PROJ_UID WHERE (tr.RES_TYPE = 2 OR tr.RES_TYPE = 102)) AS tr GROUP BY RES_UID, RES_NAME) AS tr) AS tr INNER JOIN pub.MSP_RESOURCES AS res ON res.RES_UID = tr.RES_UID LEFT OUTER JOIN pub.MSP_TIMESHEETS AS t ON t.WPRD_UID = tpr.WPRD_UID AND t.RES_UID = tr.RES_UID LEFT OUTER JOIN pub.MSP_TIMESHEET_LINES AS tl ON tl.TS_UID = t.TS_UID AND tl.TS_LINE_ACT_SUM_VALUE > 0 LEFT OUTER JOIN pub.MSP_TIMESHEET_ACTUALS AS tla ON tla.TS_LINE_UID = tl.TS_LINE_UID LEFT OUTER JOIN pub.MSP_TIMESHEET_CLASSES AS tcl ON tcl.TS_LINE_CLASS_UID = tl.TS_LINE_CLASS_UID LEFT OUTER JOIN (SELECT TS_LINE_CLASS_UID, TS_LINE_CLASS_IS_EDITABLE, TS_LINE_CLASS_NAME, TS_LINE_CLASS_TYPE, TS_LINE_CLASS_NEED_APPROVAL, TS_LINE_CLASS_ORGANIZATION, TS_LINE_CLASS_DESC, TS_LINE_CLASS_IS_DISABLED, TS_LINE_CLASS_ALWAYS_DISPLAY, CREATED_DATE, MOD_DATE, CREATED_REV_COUNTER, MOD_REV_COUNTER FROM pub.MSP_TIMESHEET_CLASSES WHERE (TS_LINE_CLASS_TYPE = 0)) AS tcltop ON tcltop.TS_LINE_CLASS_UID = tl.TS_LINE_CLASS_UID LEFT OUTER JOIN (SELECT PROJ_UID, PROJ_NAME, WRES_UID FROM pub.MSP_PROJECTS UNION SELECT 'E38038FA-F8CA-47D1-BFD4-6B45B8462972' AS Expr1, 'Administrative' AS Expr2, NULL AS Expr3) AS tp ON tp.PROJ_UID = tl.PROJ_UID LEFT OUTER JOIN pub.MSP_TASKS AS tsk ON tsk.TASK_UID = tl.TASK_UID LEFT OUTER JOIN pub.MSP_RESOURCES AS pr_owner ON pr_owner.RES_UID = tp.WRES_UID LEFT OUTER JOIN (SELECT ppResCFV.RES_UID, ppResCFV.TEXT_VALUE AS EmployeeID FROM pub.MSP_RES_CUSTOM_FIELD_VALUES AS ppResCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS ppCF ON ppResCFV.MD_PROP_UID = ppCF.MD_PROP_UID WHERE (ppCF.MD_PROP_NAME = 'employeeID')) AS E ON tr.RES_UID = E.RES_UID LEFT OUTER JOIN (SELECT ppResCFV.RES_UID, ppResCFV.TEXT_VALUE AS Location FROM pub.MSP_RES_CUSTOM_FIELD_VALUES AS ppResCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS ppCF ON ppResCFV.MD_PROP_UID = ppCF.MD_PROP_UID WHERE (ppCF.MD_PROP_NAME = 'co')) AS C ON tr.RES_UID = C.RES_UID LEFT OUTER JOIN (SELECT pspPrjCFV.PROJ_UID, pspPrjCFV.TEXT_VALUE AS ProjectAccount FROM pub.MSP_PROJ_CUSTOM_FIELD_VALUES AS pspPrjCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS pspCF ON pspPrjCFV.MD_PROP_UID = pspCF.MD_PROP_UID WHERE (pspCF.MD_PROP_NAME = 'Project Account')) AS PP ON PP.PROJ_UID = tp.PROJ_UID LEFT OUTER JOIN (SELECT pspPrjCFV.PROJ_UID, psLV.LT_VALUE_TEXT AS CostCenter FROM pub.MSP_PROJ_CUSTOM_FIELD_VALUES AS pspPrjCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS pspCF ON pspPrjCFV.MD_PROP_UID = pspCF.MD_PROP_UID LEFT OUTER JOIN pub.MSP_LOOKUP_TABLE_VALUES AS psLV ON psLV.LT_STRUCT_UID = pspPrjCFV.CODE_VALUE WHERE (pspCF.MD_PROP_NAME = 'Cost_Center')) AS PCC ON PCC.PROJ_UID = tp.PROJ_UID LEFT OUTER JOIN (SELECT pspPrjCFV.PROJ_UID, psLV.LT_VALUE_TEXT AS ProjectStatus FROM pub.MSP_PROJ_CUSTOM_FIELD_VALUES AS pspPrjCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS pspCF ON pspPrjCFV.MD_PROP_UID = pspCF.MD_PROP_UID LEFT OUTER JOIN pub.MSP_LOOKUP_TABLE_VALUES AS psLV ON psLV.LT_STRUCT_UID = pspPrjCFV.CODE_VALUE WHERE (pspCF.MD_PROP_NAME = 'Project Status')) AS PPS ON PPS.PROJ_UID = tp.PROJ_UID LEFT OUTER JOIN (SELECT pspPrjCFV.PROJ_UID, psLV.LT_VALUE_TEXT AS ProjectType FROM pub.MSP_PROJ_CUSTOM_FIELD_VALUES AS pspPrjCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS pspCF ON pspPrjCFV.MD_PROP_UID = pspCF.MD_PROP_UID LEFT OUTER JOIN pub.MSP_LOOKUP_TABLE_VALUES AS psLV ON psLV.LT_STRUCT_UID = pspPrjCFV.CODE_VALUE WHERE (pspCF.MD_PROP_NAME = 'ProjectType')) AS PT ON PT.PROJ_UID = tp.PROJ_UID LEFT OUTER JOIN (SELECT ppResCFV.RES_UID, ppResCFV.TEXT_VALUE AS ResourceCompany FROM pub.MSP_RES_CUSTOM_FIELD_VALUES AS ppResCFV INNER JOIN pub.MSP_CUSTOM_FIELDS AS ppCF ON ppResCFV.MD_PROP_UID = ppCF.MD_PROP_UID WHERE (ppCF.MD_PROP_NAME = 'Resource Company')) AS RC ON tr.RES_UID = RC.RES_UID WHERE (tpr.WPRD_START_DATE < GETDATE()) AND (tpr.WPRD_START_DATE >= '12.01.2008')) AS R_1 LEFT OUTER JOIN CUSTOM_RES_PROJ_ASSIGNMENTS AS T ON T.RES_NAME = R_1.ResourceName AND T.PROJ_NAME = R_1.ProjectName AND (T.EFFECTIVE_DATE IS NULL OR DATEDIFF(day, T.EFFECTIVE_DATE, R_1.TimeByDay) >= 0) GROUP BY R_1.RES_HIRE_DATE, R_1.RES_TERMINATION_DATE, R_1.WRES_EMAIL, R_1.TODAY, R_1.IsActive, R_1.StartDate, R_1.EndDate, R_1.PeriodName, R_1.TimeByDay, R_1.TimeByDay_DayOfWeek, R_1.ActualWorkBillable, R_1.ProjectName, R_1.[Task Name], R_1.Status, R_1.ProjectAccount, R_1.Location, R_1.ResourceCompany, R_1.EmployeeID, R_1.TS_LINE_CLASS_NAME, R_1.Type, R_1.TimesheetClass, R_1.ProjectOwner, R_1.Firstname, R_1.LastName, R_1.ResourceName, R_1.ModifiedDate, R_1.ResourceNameUID, R_1.ResourceCC, R_1.CostCenter, R_1.ProjectStatus, R_1.ProjectType, R_1.TS_LINE_UID, R_1.TS_LINE_CACHED_ASSIGN_NAME) AS R LEFT OUTER JOIN CUSTOM_RES_PROJ_ASSIGNMENTS AS T ON T.RES_NAME = R.ResourceName AND T.PROJ_NAME = R.ProjectName AND (R.EFFECTIVE_DATE IS NOT NULL AND T.EFFECTIVE_DATE = R.EFFECTIVE_DATE OR R.EFFECTIVE_DATE IS NULL AND T.EFFECTIVE_DATE IS NULL)
      
      





サーバーがリモートであるため、データを一時テーブルに「転送」するために小さなSSISパッケージを作成する必要がありました。 その後、結果のテーブルをキューブのデータソースにし、必要なディメンションとメジャーを追加しました。







関連性を確保するために、データを受信し、キューブを処理するためのsql-jobを作成しました。







従業員が費やした時間に関するレポートは毎週記入しなければならないことを忘れることが一般的であることが慣例により示されています。 これを行うために、キューブへのMDXリクエストを実行し、「忘れっぽい」従業員を特定し、タイムシールドの記入を求めるメールを送信する小さなSSISパッケージを作成しました。 さらに、金曜日の場合は現在の週がチェックされ、月曜日、火曜日、または水曜日の場合は過去の週がチェックされます。







このパッケージで発生した問題について個別に説明しましょう。 実際には、このパッケージを実行するSQLジョブがGMT + 6タイムゾーンで「存続」しているという事実から成り立っていました。 アメリカの同僚は金曜日の午後5時にリマインダーを送信する必要があり、ノボシビルスクではすでに午前5時(または米国の時間変更に応じて午前4時)であり、キューブの作業週は土曜日に始まるため、すべての同僚は米国から報告書が完成しなかったという手紙が届いた。 この問題の解決策は表面にあり、現在の曜日をチェックするための追加条件を追加することにあります。



結果


ここに私たちが得たもの、いくつかのレポートの例があります:


1.全従業員の期間のレポート。 毎週のマネージャーは、すべての従業員のタイムシートを確認します-彼らは、プロジェクトの人件費の記入の事実と正確さを管理します。











2.プロジェクトレポート。 プロジェクトの終了後、マネージャーは実装がどのように行われたかを分析できます。実際に行われたタスク、それらに費やされたリソースの数、プロジェクトの初期計画にどれだけ対応しているか 同じことがプロジェクト中に制御できます。







3.従業員レポート。 いつでも、関心のある期間の個々のプロジェクト担当者の活動を分析できます。







その結果:



  1. EastBancの全従業員は、週に40時間あるため、最低40時間を完了します。 時間を埋めてください!
  2. 従業員は、タイムシールドを正しく記入します。休日、病気休暇、休日、社内プロジェクトを含む、彼らが働いているプロジェクト-すべてが必要な列に投稿されます。
  3. プロジェクトのタスクは、次のように計画に対応します。 それらは非常に詳細です。 何かがうまくいかないときに時間通りに追いつくためのタスクは16時間以上ありません。
  4. 各プロジェクトが完了すると、マネージャーは詳細な分析を受け取ります。
  5. 各マネージャーはプロジェクトを監視します。 画像全体は、プロジェクトオフィスマネージャーによって監視されます。


これで、会社全体が毎日使用する非常に便利なエンタープライズツールができました。 非常に限られた時間で既存の標準のProject Serverツールを改善することにより、すべてを行うことができました。 タスクは、人件費の会計処理の問題を迅速に解決することであり、車輪を改良したり再発明したりすることはありませんでした。



All Articles