以前の記事で、 受信および送信文字の動作が説明されていたことを思い出させてください。 カットの下のクライマックス:多くのテキスト、コード、そして少しのアニメーション。
情報の提示プロセスの参加者のすべての要件を、2つの等しくないグループに分割する必要がありました。
- オフィススペシャリストの要件
- 他のすべての従業員の要件。
事務員の要件は他のすべての要件とは多少異なることが判明しました。 各グループの参加者は、プロセスをさまざまな角度から見ていると言えます。
事務員が文書がどのように組織に到着したか、あるいは逆に宛先にどのように送信するかが重要である場合、他の人はまったく興味がありません。
これに基づいて、ユーザーは着信および発信文字の同様のレジストリを表示する必要がありますが、レジストリに表示される属性のリストは異なることが明らかになりました。
最初は、各グループに個別のビューのセットを作成し(これがeasla.comの選択です )、権限を使用してそれらにアクセスを「分散」する必要があるように思われましたが、ほとんどの場合、数行のコードで取得できることが明らかになりましたまさにその形。
はい、 easla.comでは、すべてのタイプがコードで記述されているため、非常に柔軟です。 たとえば、個々のユーザーまたはグループが重要な基準に対応するオブジェクトを表示するようにコードを作成するのに費用はかかりません。 または、たとえば、ビューにカテゴリを追加します。各カテゴリには個別のフィルタがあります。
すべてのビューは、exec()関数に渡されるパラメーターを使用して構成されます。 もちろん、パラメーターはコードでプログラムで生成できます。
ところで、 easla.comには、いわゆる「クイックウィザード」があり、これを使用して「貼り付け」のタイプを作成できます。 新しい外観の作成に役立ち、初心者に便利です。
したがって、すべてのタイプについて説明し、それぞれの機能について説明します。
着信文書
プロセスで最も重要な2つのドキュメントレジストリの最初。 すべての受信ドキュメントの完全なリストを日付の降順で反映します。 したがって、リストの一番上には常に最新の着信文字があります。
$attributes = array( 'crs_management_incoming_receive_regnum'=>array('link'=>'object', 'actions'=>array(), 'header'=>'. '), ); if (cuser()->inGroup('group_dp') == true) $attributes += array('crs_management_incoming_content'=>array('link'=>'value')); $attributes += array( 'crs_management_incoming_receive_date', 'crs_management_incoming_contragent_regnum'=>array('header'=>'. '), 'crs_management_incoming_contragent'=>array('link'=>'value'), 'crs_management_incoming_contact'=>array('link'=>'value'), 'crs_management_incoming_subj', 'crs_management_incoming_outgoing'=>array('link'=>'value'), 'crs_management_incoming_contract'=>array('link'=>'value','max'=>3), 'crs_management_incoming_document'=>array('link'=>'value'), 'crs_management_incoming_forwardto'=>array('link'=>'value'), 'status' ); cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_incoming'), 'attributes'=>$attributes, 'conditions'=>array( 'permission'=>'2', ), 'sort'=>array( 'crs_management_incoming_receive_date'=>array('default'=>'desc', 'enable'=>true), 'crs_management_incoming_contragent'=>array('enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true ));
このフォームでは、事務員の要件のために、$ attributes変数に属性のリストを作成する必要がありました。 各受信レターのコンテンツのタイプを表示する必要があるのは彼女だけです。
ビューの最初の列は、crs_management_incoming_receive_regnum属性の値の隣にオブジェクトのコンテキストメニューを表示するように構成されています。 標準アクションだけでなく、管理者が発表したアクションも含まれます。
さらに、属性crs_management_incoming_contractで興味深いニュアンスが浮上しました。 複数であり、着信ドキュメントが属する契約へのリンクを格納します。 ほとんどの場合、属性には1つ、最大2つの値があり、問題はありませんでした。 しかし、顧客の1人と20以上の追加契約を締結しました。 契約。 彼は私たちに手紙を送り始めました。 同意してから、いくつか、次にほぼすべてについて。 店員は、余計なものすべてを注意深く示す以外に選択肢はありませんでした。 受信トレイの作成者が参照する契約。 その結果、属性値の数が無数に増え、easla.comはそれらすべてを熱心に表示し、セルをほぼ画面全体に拡大しました。 一般に、すべてが単純に決定されました。maxオプションを使用すると、表示される値の数を省略記号の後ろに隠すことで制限できます。
着信ドキュメントのステータスはオブジェクト自体によってペイントされます( 別のパートを参照)。このプランのビューを構成する必要はありません。 最終的には、次のようになります。
発信文書
最も重要なドキュメントレジストリの2番目。 そして、これは私の役割の中で、ロールごとにアクセスを制限する2つに分割した唯一のビューです。 言い換えれば、彼は同じ名前の2つの種を作成しましたが、1つは書記官用で、もう1つは他のすべてのもの用です。
ところで、ロールによるビューのアクセシビリティマトリックスは次のようになります。
もちろん、試してみると、コードですべてを区別できますが、私はそのような決定をしました。 その瞬間、それは私にとって最も真実に思えた。
すべてのユーザーを対象とした1種類のコードのみを提供します。
cviewpub()->categories = array('',' '); cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; $attributes = array( 'crs_management_outgoing_regnum'=>array( 'link'=>'object', 'actions'=>array(), 'export'=>array( 'id', 'crs_management_outgoing_regnum', 'crs_management_outgoing_sentdate' ) ), 'crs_management_outgoing_sentdate', 'crs_management_outgoing_performers'=>array('link'=>'value'), 'crs_management_outgoing_contragent'=>array('link'=>'value'), 'crs_management_outgoing_contact'=>array('link'=>'value'), 'crs_management_outgoing_subj', 'crs_management_outgoing_incomingdocs'=>array( 'link'=>'value', 'export'=>array( 'id', 'crs_management_incoming_contragent_regnum', 'crs_management_incoming_contragent_date' ) ), 'crs_management_outgoing_contract'=>array('link'=>'value','max'=>3), 'crs_management_outgoing_content'=>array('link'=>'value'), ); $conditions = array(); switch(cviewpub()->category) { case 0: $attributes[] = 'status'; break; case 1: $conditions += array('status'=>array('crs_management_outgoing_create','crs_management_outgoing_created')); break; } cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_outgoing'), 'attributes'=>$attributes, 'conditions'=>$conditions, 'sort'=>array( 'crs_management_outgoing_regdate'=>array('default'=>'desc', 'enable'=>true), 'crs_management_outgoing_contragent'=>array('enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true ));
このフォームでは、 easla.comの非常に便利な機能をいくつか使用しました 。
まず、カテゴリに注意を払います。 彼らの助けを借りて、 easla.comでは、さまざまな角度からビューを表示できます 。 フォームでステータスで直接フィルタリングすることは可能ですが、一部のアクティブユーザーは、未送信の送信メールを含む別のビューを作成することを非常に強く要求しました。 これに同意しました。 数行のコード:
cviewpub()->categories = array('',' '); cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category;
特に指定がない限り、2つのカテゴリを作成して最初のカテゴリを選択します。 アクティブカテゴリ値はスイッチで使用され、$条件オブジェクトを選択するための条件を形成します。 シンプルで便利なことがわかりました。
さらに、crs_management_outgoing_regnum列とcrs_management_outgoing_incomingdocs列で使用されたエクスポートオプションについて詳しく説明します。 このオプションでは、たとえば、1つの属性値crs_management_outgoing_regnumの代わりにデータをエクスポートして(以下のエクスポートについて)、属性値をアンロードできます。
- id
- crs_management_outgoing_regnum(別名)
- crs_management_outgoing_sentdate。
私たちの場合、この機能は、エクスポートされたデータに基づいて生成する必要のあるレポートを準備するのに非常に役立つことが判明しました。
取引相手の発信と着信
Easla.comには、プロセスのコンテキストではなく、オブジェクトのコンテキストでビューを作成するユニークな機会があります 。 このようなビューはメインメニューには表示されませんが、それらを使用するオブジェクトのブックマークに表示されます。 スマートシステム自体は、アクティブオブジェクトとサンプルオブジェクト間の接続を検出します。
カウンターパーティの形式で2つのタブ(ビュー)を表示する必要がありました。カウンターパーティの送信トレイとカウンターパーティの 受信ボックスです。 彼らは、選択されたカウンターパーティとのすべての通信を視覚化することを可能にしました。 チーフプロジェクトエンジニアにとって非常に便利な機能です。 繰り返しますが、上記のタイプでこのような選択を行うことができますが、より便利です。
「Counterparty」オブジェクトでは、次の行を追加する必要がありました。
$objectTabs = array('crm_management_contact'); if (corganization()->hasViewpub('crs_management_all_incoming_by_contragent')) $objectTabs[] = 'crs_management_all_incoming_by_contragent'; if (corganization()->hasViewpub('crs_management_all_outgoing_by_contragent')) $objectTabs[] = 'crs_management_all_outgoing_by_contragent'; cobjectref()->objectTabs = $objectTabs;
objectTabsプロパティには、ブックマークがオブジェクトに表示されるビューのコードまたは名前が含まれている必要があります。 オブジェクトの種コードは次のとおりです。
取引相手への発信
$attributes = array( 'crs_management_outgoing_regnum'=>array('link'=>'object'), 'crs_management_outgoing_regdate', 'crs_management_outgoing_contact'=>array('link'=>'value'), 'crs_management_outgoing_subj', 'crs_management_outgoing_incomingdocs'=>array('link'=>'value'), 'crs_management_outgoing_contract'=>array('link'=>'value','max'=>3), 'crs_management_outgoing_document'=>array('link'=>'value'), 'status'=>array('actions'=>array()) ); cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_outgoing'), 'attributes'=>$attributes, 'sort'=>array( 'crs_management_outgoing_regdate'=>array('default'=>'desc', 'enable'=>true) ), 'pagination'=>array('pagesize'=>10), ));
取引相手からの受信トレイ
cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_incoming'), 'attributes'=>array( 'crs_management_incoming_receive_regnum'=>array('link'=>'object'), 'crs_management_incoming_receive_date', 'crs_management_incoming_contragent_regnum', 'crs_management_incoming_contact'=>array('link'=>'value'), 'crs_management_incoming_subj', 'crs_management_incoming_content', 'crs_management_incoming_contract'=>array('link'=>'value','max'=>3), 'crs_management_incoming_document'=>array('link'=>'value'), 'status'=>array('actions'=>array()) ), 'conditions'=>array( 'permission'=>'2', ), 'sort'=>array( 'crs_management_incoming_receive_date'=>array('default'=>'desc', 'enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), ));
その結果、取引相手の形式は次のようになり始めました。
私はエルマではそのような結果を達成できなかったことを覚えています。
私の受信トレイ
アクティブユーザーに宛てられたすべての受信ドキュメントのリストを表示する補助ビュー。 チーフエンジニアとGUIにとって特に便利です。
cviewpub()->categories = array('',' ',' ',''); cviewpub()->category = is_null(cviewpub()->category) ? 0 : cviewpub()->category; $attributes = array( 'crs_management_incoming_receive_regnum'=>array('link'=>'object', 'actions'=>array()), 'crs_management_incoming_receive_date', 'crs_management_incoming_contragent'=>array('link'=>'value'), 'crs_management_incoming_contact'=>array('link'=>'value'), 'crs_management_incoming_subj', 'crs_management_incoming_contract'=>array('link'=>'value','max'=>3), 'crs_management_incoming_document'=>array('link'=>'value'), ); $conditions = array('crs_management_incoming_forwardto'=>cuser()->id, 'permission'=>'2'); switch(cviewpub()->category) { case 0: $attributes[] = 'status'; break; case 1: $conditions += array('status'=>array('crs_management_incoming_create','crs_management_incoming_created','crs_management_incoming_handed')); break; case 2: $conditions += array('status'=>array('crs_management_incoming_exec')); break; case 3: $conditions += array('status'=>array('crs_management_incoming_ok')); break; } cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_incoming'), 'attributes'=>$attributes, 'conditions'=>$conditions, 'sort'=>array( 'crs_management_incoming_receive_date'=>array('default'=>'desc', 'enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true ));
ソースコードからわかるように、フォームには文字をすばやくフィルタリングするためのいくつかのカテゴリもあります。 そうでなければ、それは目立たない。
私の送信トレイ
アクティブユーザーが参加した開発中の文字の完全なリストを表示する補助ビュー。
$attributes = array( 'crs_management_outgoing_regnum'=>array('link'=>'object', 'actions'=>array()), 'crs_management_outgoing_regdate', 'crs_management_outgoing_performers'=>array('link'=>'value'), 'crs_management_outgoing_contragent'=>array('link'=>'value'), 'crs_management_outgoing_contact'=>array('link'=>'value'), 'crs_management_outgoing_subj', 'crs_management_outgoing_incomingdocs'=>array('link'=>'value'), 'crs_management_outgoing_contract'=>array('link'=>'value','max'=>3), 'crs_management_outgoing_document'=>array('link'=>'value'), 'status' ); $conditions = array('crs_management_outgoing_performers'=>cuser()->id); cviewpub()->exec(array( 'object'=>objectdef('crs_management', 'crs_management_outgoing'), 'attributes'=>$attributes, 'conditions'=>$conditions, 'sort'=>array( 'crs_management_outgoing_regdate'=>array('default'=>'desc', 'enable'=>true), 'crs_management_outgoing_contragent'=>array('enable'=>true) ), 'sorting'=>true, 'pagination'=>array('pagesize'=>10), 'showcreate'=>true ));
多くのユーザーは、そこから文字を探し始めます。
輸出する
Easla.comは、 Microsoft Excel (xlsx)およびMicrosoft Excel XML (xml) 形式でエクスポートを実装します 。これにより、ドキュメントレジストリをアップロードして処理し、目的のフォームに取り込むことができます。
エクスポートすると、ビュー設定で指定された属性値がアンロードされます。 言い換えると、ビューは表示されるときにアンロードされます。
しかし、ビューをアンロードする必要がありました。ビューには、顧客への送信のためのすべての着信および発信メッセージ、両方の当事者による各手紙への返信の事実を確認する一種のレジスターが含まれていました。 ビュー、Movetonに適切な属性を追加します。 ユーザーが必要とするのはオブジェクトの説明だけです。これは、さらに2つの追加の属性が必要な理由です。 必要な属性を備えた個別のビューを作成できますが、メインメニューには常に表示され、たまにしかアクセスされません。 再びugい。
列パラメーターでエクスポートパラメーターを指定でき( 上記の例を参照 )、表示された属性の代わりにエクスポート中にアンロードする必要がある属性のリストを指定できることが判明しました。 めちゃめちゃ快適!
一般に、エクスポートパラメーターを使用して、エクスポートに必要な属性を持つすべてのタイプを追加し、必要なものを取得しました。
レジストリ上の文字のアンロード
実装の観点から最も興味深いのは、 easla.comからの文字のレジストリのアンロードと、それに続く文字自体のアンロードです。 システムのツールを使用してこれを行うことはできません。レジストリのみがダウンロードされ、ファイルはダウンロードされないため、マクロを作成する必要がありました。
私は同僚にマクロを書くように指示しました;彼はこれでマスターです。 アルゴリズムは非常に単純です。 アンロードされたレジストリを調べ 、レジストリで指定された識別子を使用して、 easla.comの各オブジェクトに( SOAP 、 COM、または.NET経由で)アクセスし、メッセージファイルとアプリケーションファイルを別のフォルダーにアップロードします。 レジストリで、文字の指定を、ダウンロードしたファイルのあるフォルダーへのハイパーリンクに置き換えます。
これは、アンロード後のMicrosoft Excelのレジストリの外観です。
彼を「設定」:
アドオンファイルのアップロード
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Excel = Microsoft.Office.Interop.Excel; using Office = Microsoft.Office.Core; using TNGP_Office_Helper; using System.Windows.Forms; using System.IO; using EaslaHelper; namespace TNGP_ExcelAddIn_Specification { public class Letters { const string SHEET_NAME = "Export"; const int COLUMNS_COUN = 12; Excel.Workbook wb = null; public void RunForestRun() { ReadData(); } public void ReadData() { wb = Exl.ActiveWb; if (wb == null) return; var exportSheet = wb.GetSheet(SHEET_NAME); if (exportSheet == null) return; string path = string.Empty; var fbd = new FolderBrowserDialog(); fbd.Description = " ."; if (fbd.ShowDialog() == DialogResult.OK) path = fbd.SelectedPath; else return; int index = 1; if (wb.Path != path) wb.SaveAs(Path.Combine(path, wb.Name)); var rowsCount = ExcelHelper.CountDataRows(exportSheet, 1, 1, COLUMNS_COUN); for (int iRow = rowsCount; iRow >= 2; iRow--) { string idOut = exportSheet.Range["A" + iRow].Text; string numberOut = exportSheet.Range["B" + iRow].Text; string pathOut = Path.Combine(path, Helper.ReplaceChars(numberOut)); if (!Directory.Exists(pathOut)) Directory.CreateDirectory(pathOut); EaslaHelperClass.DLDocument("Out", "OutDocAttach", idOut, pathOut); index++; exportSheet.Hyperlinks.Add(Anchor: exportSheet.Range["B" + iRow], Address: Helper.ReplaceChars(numberOut), TextToDisplay: numberOut); string idIn = exportSheet.Range["H" + iRow].Text; if (!string.IsNullOrEmpty(idIn)) { string numberIn = exportSheet.Range["I" + iRow].Text; string dateIn = exportSheet.Range["J" + iRow].Text; string[] arrId = idIn.Split(';'); string[] arrNumber = numberIn.Split(';'); string[] arrDate = dateIn.Split(';'); if (arrId.Length > 1) { var RngToCopy = exportSheet.Rows[string.Format("{0}:{0}", iRow)]; for (int j = 0; j < arrId.Length - 1; j++) { var RngToInsert = exportSheet.Range["A" + iRow].EntireRow; RngToInsert.Insert(Excel.XlInsertShiftDirection.xlShiftDown, RngToCopy.Copy(Type.Missing)); } } for (int i = 0; i < arrId.Length; i++) { exportSheet.Range["H" + (iRow + i)].Value2 = arrId[i]; exportSheet.Range["J" + (iRow + i)].Value2 = arrDate[i]; var numberInNew = Helper.ReplaceChars(arrNumber[i]); exportSheet.Hyperlinks.Add(Anchor: exportSheet.Range["I" + (iRow + i)], Address: numberInNew, TextToDisplay: arrNumber[i]); string pathIn = Path.Combine(path, numberInNew); if (!Directory.Exists(pathIn)) Directory.CreateDirectory(pathIn); EaslaHelperClass.DLDocument("In", "InDocAttach", arrId[i], pathIn); index++; } } double percent = index * 100 / (rowsCount * 2); ExcelHelper.SetStatusBarPercent(Exl.ExlApp, Helper.PercentProgressBar(percent), " "); } exportSheet.Range["A:A"].EntireColumn.Hidden = true; exportSheet.Range["H:H"].EntireColumn.Hidden = true; wb.Save(); MessageBox.Show(" !"); } } }
ヘルパー関数
public static DWReturnType DLDocument(string objdef, string attrs, string id, string path, string filename = "") { string objectdef = string.Empty; string[] attrrefs = null; switch (objdef) { case "Out": objectdef = "crs_management_outgoing"; break; case "In": objectdef = "crs_management_incoming"; break; } switch (attrs) { case "InDocAttach": attrrefs = new string[] { "crs_management_incoming_document", "crs_management_incoming_attachments" }; break; case "InDoc": attrrefs = new string[] { "crs_management_incoming_document" }; break; case "OutDocAttach": attrrefs = new string[] { "crs_management_outgoing_document", "crs_management_outgoing_attachments" }; break; case "OutDoc": attrrefs = new string[] { "crs_management_outgoing_document" }; break; } var process = EaslaApp.getProcess("crs_management"); var object_def = EaslaApp.getObjectdef(process, objectdef, 0); var objectrefs = GetObjectRefs(id, object_def, attrrefs); if (objectrefs == null) return DWReturnType.Error; if (objectrefs.Length == 0) return DWReturnType.LetterNotFound; if (objectrefs.Length > 1) return DWReturnType.FoundManyLetters; return DownloadFile(filename, path, objectrefs[0].attributerefs); } public static DWReturnType DownloadFile(string filename, string path, Easla.AttributerefSimpleSoap[] attrs) { DWReturnType result = DWReturnType.FileNotFound; foreach (var attr in attrs) { if (attr.values != null) { var files = attr.values.OfType<Easla.FileSimpleSoap>(); var selectfiles = files.Where(f => f.isdeleted == "0" & f.vid == "0"); foreach (var fss in selectfiles) { string filePath = Path.Combine(path, fss.nowname); if (!string.IsNullOrEmpty(filename)) { string ext = Path.GetExtension(fss.nowname); filePath = Path.Combine(path, filename + ext); } EaslaApp.DownloadFile(fss.id, filePath); result = DWReturnType.Ok; } } } return result; }
結果は次のようになります。
私は、そのような決定の経済的効果を誰にも納得させてはならないと思う。 時間、したがってレポートの準備と手紙のダウンロードに費やされたお金は、2倍、そして3桁減少しました! 数週間前、ISUのアシスタントの1人がこの方法で100通または2通以上の手紙を降ろしました。 一定期間の顧客とのすべての通信。 私は夜に荷を下ろし始め、夜のためにそれを残しました。 午前中に私は完成した結果を得ました! 手動で、彼は数週間またはそれ以上すべてのファイルを収集していました。
まとめ
プロセスを説明するために3つの長い記事を書く必要があったという事実にもかかわらず、 easla.comでプロセスを作成することは簡単で簡単であると言えます。 実際、私はまだ時事に気を取られていたので、通信を管理するプロセス全体は2-3パートの日数を要しました。 真面目で注意深いアプローチは、「メールで送信。 「送信ドキュメント」オブジェクトの「メール」( 前の記事を参照 )。 それ以外の場合、オブジェクトと属性のコードは単純で、反復可能で、読みやすいです。
easla.comの他のプロセス管理者は、プロセスコードを簡単に読み取り、2つの方法で理解できると確信しています。
easla.comは優れた電子文書管理システムであり、ロシア市場にある同様の高価な商用製品を撃退する準備ができていると確信できます。
PS 記述されたプロセスは 、その中に機密データが存在するためだけに借りることができないという事実に注意を促します。 その完全なアナログ通信は 、制限なしで一般的な使用のためにサイトに公開されています! 質問があります-support@easla.comに書いてください -彼らはあなたに答えます!