2012年10月9日

[EasyFlow] DropDownList 下拉式選單的加工

加工一:在SQL Command加上其他較複雜的SQL語法

原因:
      因為在 EasyFlow 表單設計中的DropDownList選單中的 SQL Command 無法用 SQL 的
      ORDER BY  or HAVING 之類的語法, 所以必須手動加在 CS 檔中.
作法:  
      找到 ” #region 重新取得 select selectXXXXX下拉選項 ”,修改 string tSQL 後面的  SQL 語法



#region 重新取得selectcategory2下拉選項 
[AjaxPro.AjaxMethod(AjaxPro.HttpSessionStateRequirement.ReadWrite)]  
public string ajaxGetselectcategory2Item(string pselectcategory1Val)  
{  
 string strReturn = ""; string tSQL = @"select distinct * from LMIT where FormCode='表單名稱' 
        and ItemNo='1' and Category=N'" +  pselectcategory1Val + "' order by 00";
 strReturn = createEasyFlowDataTable(tSQL, "Details", "Details");  
 return strReturn;  
}  
#endregion 重新取得selectcategory2下拉選項 

加工二:DropDownList 第一個選項是空白

作法:   
  要刪除這個空白的話,在if (!IsPostBack)中,找到這個下拉式選單,註解掉this.selectXXXXX.Items.Insert(0, new ListItem("", ""));
  在JS檔中的FunOnChange_ selectXXXXX 註解掉CreateOption("","請選擇", selectXXXXX DDL);即可

2012年10月8日

[EasyFlow] 自製 TipTop 放行單



原因:
         在 TipTop AXM520 中,沒有信用查核放行的單子可拋轉至 EasyFlow 簽核,且因 EasyFlow 版本為 .net ,鼎新將 CS 檔包成 DLL 檔了,沒有 Code ,故無法在出貨單拋轉至 EasyFlow 簽核完成時回寫 TipTop .
             
 故現行公司的做法是:
         需要放行時由相關人員填寫紙本的信用查核放行單,依核決權限簽核後,交由會計至下面欄位勾選允許放行.

流程改善:
1. 當需要放行時,負責的人員至 EasyFlow 開立信用查詢放行單,依核決權限簽核,
         當簽核完成後,更新 TipTop DB OGA903 欄位(改為Y)





2.    防呆:
a)送出前檢查: 須為出貨單 / 確認碼不為X:作廢 / 狀況碼不為9:作廢 / 信用額度查核放行不為Y / Gridview裡面的單號須為同一客戶且幣別相同
b)簽核完成後檢查:流程中是否有會計部門的員工
        c)輸入出貨單單號自動帶出該訂單之金額
3.    <!--[endif]-->檢查後帶出相關資料:
a)客戶編號 / 客戶名稱 / 授信條件 / 付款條件 / 幣別 / 本幣金額
b)計算出原幣金額(單價*數量*匯率)
c)檢查通過才會出現送出的按鈕,檢查不通過會跳出視窗題是哪邊錯誤


#region 簽核流程結束時要處理的事情
protected override void AfterApprove()
{
if (this.BlnCaseClosed && this.AryFlowProperty.SerialSignResult == "2")
{
   //先判斷流程中是否有財會部的人
   string sql_EFdept = @"select * from resdd as D
        where resdd001='表單TABLE' and resdd002='"+this.StrSheetNo+"'
              and exists (
                select '"+"x"+"' from  resak X where X.resak001 =
                    D.resdd008 and (X.resak015 = 'FN3000')
             )";
DataSet set_EFdept = this.m_objDB.CreateRecordset(" use EFNETDB;" +
                   sql_EFdept, this.Session["strProcID"].ToString());
  DataTable temp_EFdept = set_EFdept.Tables[0];
  if (temp_EFdept.Rows.Count == 0)
  {
    //把ERROR存成TXT檔
  }
  else
  {
    StringBuilder builder = new StringBuilder();
    builder.Append(" use EFNETDB;");
    builder.Append(" select saleno ");
    builder.Append(" FROM 表單TABLEb ");
    //表單代號,表單編號
    builder.AppendFormat(" WHERE 表單TABLEb001='{0}' ", this.formID);
    builder.AppendFormat(" AND 表單TABLEb002='{0}' ", this.StrSheetNo);
    DataSet set_saleno=this.m_objDB.CreateRecordset(builder.ToString(),this.Session["strProcID"].ToString());
    DataTable temp_saleno = set_saleno.Tables[0];
    foreach (DataRow saleno_read in temp_saleno.Rows)
    {
       string sql_OGAFILE = @"UPDATE OPENQUERY(ORACLE,'
               select oga903 from TT.oga_file where oga01=''" + saleno_read["saleno"] + "''') SET OGA903='Y'";                      
this.m_objDB.ExecuteSQL(sql_OGAFILE.ToString(),this.Session["strProcID"].ToString());
     }
   }
 }





#region 按下檢查時要做的事情

protected void butcheck_Click(object sender, EventArgs e)
{
    //先清空textbox
    
    
    base.BtnCreateToolSendForm.Attributes.Add("style", "display:none");//隱藏送出的按鈕
    int rows_count = MasterObj.DetailObjs[0].NewRows.Count;//取得grid有幾列資料
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    string customer_1 = "";//客戶名稱
    string customer_no = "";//客戶編號
    string terms_payment = "";//付款條件
    int customer_i = 1;
    string currency_1 = "";//幣別
    double local_currency = 0.0;//本幣未稅金額NTD
    double org_currency = 0.0;//原幣未稅金額
    double credit = 0.0; //授信額度
    double rate = 0.0; //匯率
    string txt_saleno = "";//銷貨單號
    bool bPass = true;
    if (rows_count == 0)
    {
         MessageBox.Show("請先輸入訂單編號");
    }
    else
    {
        foreach (DscRow dscRow in MasterObj.DetailObjs[0].NewRows)
        {
            string sqlstring_TT = @"Select * From OPENQUERY(ORACLE, '
            select oga01 as 銷貨單號,oga03 as 客戶編號,oga032 as 客戶名稱,oga903 as 是否放行,oga55 as 狀況碼,
                    ogaconf as 核准碼,oga09 as 單據別,oag02 as 付款條件,oga24 as 匯率,
                    oga50 as 出貨金額,OCCG03 as 授信額度,oga23 as  幣別,oga50 as 原幣未稅金額,(
                              select sum(omb16) from TT.omb_file  
                                    where TT.oga_file.oga01=omb31
                            ) as 本幣未稅金額
            from TT.oga_file join TT.oag_file on oga32=oag01
                 join TT.occg_file on OCCG01=oga03
            where oga01=''" + dscRow["saleno"].Value.ToString() + "''')";

           DataSet set_TT = this.m_objDB.CreateRecordset(" use EFNETDB;" +sqlstring_TT,this.Session["strProcID"].ToString());
           DataTable dtTemp_TT = set_TT.Tables[0];
           if (dtTemp_TT.Rows.Count == 0)
           {
               sb.Append("查無'" + dscRow["saleno"].Value.ToString() + "'的訂單資料,請檢查訂單號碼是否錯誤\r\n");
                bPass = false;
          }
          else
          {
              foreach (DataRow s_read in dtTemp_TT.Rows)
              {
                if (s_read["單據別"].ToString().Trim() == "1")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "是出貨通知單喔~請再確認\r\n");
                  bPass = false;         
                }
                else if (s_read["核准碼"].ToString().Trim()=="X")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "核准碼是作廢'X',請再確認");
                  bPass = false;
                }
                else if (s_read["是否放行"].ToString().Trim()!= "N")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "已放行,請再確認.\r\n");
                  bPass = false;
                }
                else
                {
                  if (customer_i == 1)
                {
                customer_1 = s_read["客戶名稱"].ToString();
                currency_1 = s_read["幣別"].ToString();
                double x = 0.0;
                bool flag_1 = double.TryParse(s_read["匯率"].ToString(), out x);
                bool flag_2 = double.TryParse(s_read["原幣未稅金額"].ToString(), out x);
                bool flag_3 = double.TryParse(s_read["授信額度"].ToString(), out x);
                if (flag_1 && flag_2 && flag_3)
                {
                   rate = double.Parse(s_read["匯率"].ToString());
                   org_currency = double.Parse(s_read["原幣未稅金額"].ToString());
                   credit = double.Parse(s_read["授信額度"].ToString());
                }
                customer_no = s_read["客戶編號"].ToString();
                terms_payment = s_read["付款條件"].ToString();
                local_currency = rate * org_currency;
                txt_saleno = s_read["銷貨單號"].ToString();
              }
              else
              {
               if (customer_1 != s_read["客戶名稱"].ToString())               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "公司名稱與其他不同");
                 bPass = false;
               }
               if (currency_1 != s_read["幣別"].ToString())
               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "幣別與其他不同.\r\n");
                 bPass = false;
               }
               if (txt_saleno == s_read["銷貨單號"].ToString())
               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "重複.\r\n");
                 bPass = false;
               }
               org_currency = org_currency + (double.Parse(s_read["原幣未稅金額"].ToString()));
               local_currency = local_currency + (rate * (double.Parse(s_read["原幣未稅金額"].ToString())));
                            }
                        }
                    }
                }
                customer_i++;
            }
        }
string ErrorMsg = sb.ToString();
if (bPass && butcheck.ToolTip != "0")
{
  //Lucas 觸發 refresh 重新解悉流程
  string str_java = "document.getElementById('" +base.BtnCreateToolRefresh.ClientID + "').click();";
  Page.ClientScript.RegisterStartupScript(typeof(string), "jva", str_java, true);
  base.BtnCreateToolSendForm.Attributes.Add("style", "display");
  butcheck.ToolTip = "0";
  butcheck.Text = "已檢查";
  currencymoney.Text = org_currency.ToString();//原幣未稅金額
  orgmoney.Text = local_currency.ToString();
  customerno.Text = customer_no;
  overdue3.Text = terms_payment;
  credit3.Text = credit.ToString();
  }
  else
  {
     base.BtnCreateToolSendForm.Attributes.Add("style", "display:none");
     butcheck.ToolTip = "";
     MessageBox.Show("錯誤訊息如下:\r\n" + ErrorMsg);
  }
} 
#endregion
}

2012年9月20日

[.Net] 如何用 C# 將 LibreOffice 的 ods 轉成 excel 的 xls

首先,當然是要安裝 LibreOffice,
接下來要安裝 LiberOffice SDK ,坦白說,這個步驟我找好久,
下圖是下載的位置:


安裝完成之後,開啟 dotNet C# 專案,將 C:\Program files\LibreOffice 3.6\sdk\cli 中的 Dll 全部加進參考。


接下來就可以開始寫程式了:
public static string FilePath2Url(string fname)
{
    return string.Format("file:///{0}", fname.Replace("\\", "/"));
}

internal static XComponent openCalcSheet(string fname)
{
    string url = FilePath2Url(fname);
    XComponentContext oStrap = uno.util.Bootstrap.bootstrap();
    XMultiServiceFactory oServMan = (XMultiServiceFactory)oStrap.getServiceManager();
    XComponentLoader desktop = (XComponentLoader)oServMan.createInstance("com.sun.star.frame.Desktop");
    PropertyValue[] loadProps = new PropertyValue[1];
    loadProps[0] = new PropertyValue();
    loadProps[0].Name = "Hidden";
    loadProps[0].Value = new uno.Any(true);
    XComponent document = desktop.loadComponentFromURL(url, "_blank", 0, loadProps);
    return document;
}

public static XSpreadsheetDocument Open(string fname)
{
    return (XSpreadsheetDocument)openCalcSheet(fname);
}

public static void Save(XSpreadsheetDocument xDoc, string fname)
{
    string url = FilePath2Url(fname);
    PropertyValue[] propVals = new PropertyValue[2];
    /* 另存成 MS Excel 97 的格式,
       格式可以參考:http://www.oooforum.org/forum/viewtopic.phtml?t=3549  (找不到官方的清單 Orz,看官知道在哪的,請通知一聲,感恩ㄚ)
       原理可以參考:http://wiki.openoffice.org/wiki/Documentation/DevGuide/OfficeDev/Integrating_Import_and_Export_Filters
     */
    propVals[0] = new PropertyValue { Name = "FilterName", Value = new uno.Any("MS Excel 97") };
    propVals[1] = new PropertyValue { Name = "Overwrite", Value = new uno.Any(true) };
    ((XStorable)xDoc).storeToURL(url, propVals);
}

public static void Convert2Xls(string fname, string new_fname)
{
    Save(Open(fname), new_fname);
}
於是只要呼叫 Convert2Xls(ods檔案位置, xls輸出檔案位置) 就可以將 ods 轉成 xls 囉。

範例程式:將選到的 ODS 另存成 XLS 並透過 oledb 讀取資料顯示在 DataGridView 上

參考資料:
.http://www.oooforum.org/forum/viewtopic.phtml?t=3549
.Integrating Import and Export Filters
.use openoffice uno cli with c sharp to create a spreadsheet
.c-cpp.r3dcode.com


2012年9月17日

[Refactoring] Demo video


原本這程式會分別出兩張報表,新的需求是要併成一張,
看到程式的第一個念頭就是「重構」,
請記住!「重構」不是「重寫」,
我們是懷著讓原本程式更容易閱讀的心情再做「重構」的。

影片不是很清楚,程式不是很容易閱讀,純粹紀錄「重構」的感覺,
如果你也進行過「重構」工作,我想你會懂我的。




2012年8月21日

[Javascript] 實作熱鍵(HotKey)共用程式,以Konami(上上下下左右左右BA)為例

/*
不曉得有沒有人想過像 Konami 那種,按 "上上下下左右左右BA" 後,
會觸發某些事件的功能,如何撰寫,以下是我的實作,除了特定的 Konami 按鍵外,
也允許自訂自己的按鍵事件序列的測試方法,
整個程式的概念,是註冊 document 的 keyup 事件,並紀錄下最近 10 次的按鍵內容,
並提供一個 RegisterTest 方法,由註冊者自行決定目前按下的按鍵是否符合觸發的條件,
若符合要觸發什麼事件; RegisterTest 會先把這兩個 function 紀錄下來,
等到畫面上的 kepup 事件產生時,逐一呼叫 testFun , testFun 回傳 true ,
就呼叫 triggerFun 執行當初註冊的觸發事件。
這個程式需要 jQuery ,
另外這個陽春的版本不提供組合鍵的處理,當然稍微修改一下也可以支援組合鍵。
*/
var HotKeyControler = {
    MAX_QUEUE: 10, /* 保留最後 10 個鍵盤事件 */
    testFuns: new Object(),
    triggerFuns: new Object(),
    KeyQueue: new Array,
    Keyup: function(event) {
        HotKeyControler.KeyQueue.push(event.keyCode);
        if (HotKeyControler.KeyQueue.length > HotKeyControler.MAX_QUEUE) {
            HotKeyControler.KeyQueue.shift();
        }
        for (var key in HotKeyControler.testFuns) {
            if (HotKeyControler.testFuns[key](event,  HotKeyControler.KeyQueue)) {
                HotKeyControler.triggerFuns[key]();
            }
        }
    },
   /*  函數名稱:註冊熱鍵
    *  參數說明:(唯一鍵值, 測試function, 觸發function)
    *  說   明:提供註冊熱鍵的方法,提供目前畫面被按下的按鍵佇列,供AP自行判斷是否觸發執行事件
    */
    RegisterTest: function(key, testFun, triggerFun) {
        if (typeof(testFun)=='function' && typeof(triggerFun)=='function') {
            HotKeyControler.testFuns[key] = testFun;
            HotKeyControler.triggerFuns[key] = triggerFun;
        }
    }
}

$(document).ready(function(){    
    $(document).unbind("keyup",HotKeyControler.Keyup);
    $(document).bind("keyup",HotKeyControler.Keyup);
});

/* DEMO 註冊當按下 Konami(上上下下左右左右BA)時要觸發的事件 */
HotKeyControler.RegisterTest("Konami"
    ,function(event,code) { /* 上上下下左右左右BA */
        if (code.length!=10) return false;
        if (code[0]==38 && code[1]==38 && code[2]==40 && code[3]==40 &&
            code[4]==37 && code[5]==39 && code[6]==37 && code[7]==39 &&
            code[8]==66 && code[9]==65) {
            return true;
        }
        return false;
    }
    ,function() {
        alert('上上下下左右左右BA!!!');
    }
);

2012年8月20日

[RegExp] 正規表示式(Regular Expression) 應用(一)

網路上有 一大堆 關於 Regular Expression (正規表示式),寫得很棒的文章,
所以這裡並不特別介紹,因為怎麼寫也沒有比別人寫得好,
因此我主要的目的是應用的分享,所謂師傅領進門,修行靠個人,
這篇就是把小弟個人的修行成果做個紀錄,
以後就不定期來更新,

HTML DOM Style 物件擁有很多屬性,
我們可以直接用 element.style[styleProperty] 去存取他們,
偏偏有些舊 IE 時代的程式會這麼寫:
element.style.getAttribute(styleProperty);
element.style.setAttribute(styleProperty, value);
糟糕的是,其它的 browser 不甩這種寫法,而是要這麼寫
element.style.getPropertyValue(style-property)
element.style.setProperty(style-property, value)
更慘的是,新舊參雜的風格時,有大量的程式需要調整、測試,在不動舊程式的情況下,
我們希望在其他瀏覽器下模擬出 style.get/setAttribute 的行為,
於是我這麼做:
if (typeof (CSSStyleDeclaration.prototype.getAttribute) == "undefined") {
    CSSStyleDeclaration.prototype.getAttribute = function (propName) {
        return this.getPropertyValue(Me._changePropName(propName));
    }
}
if (typeof (CSSStyleDeclaration.prototype.setAttribute) == "undefined") {
    CSSStyleDeclaration.prototype.setAttribute = function (propName, value) {
        this.setProperty(Me._changePropName(propName), value);
    }
}
但這卻遇到一個問題,當用 get/set Property 時, propertyName 格式是和 CSS 一樣,
中間用 "-" 分隔,但 IE get/set Attribute 的寫法則是第二個字的字首字母大寫(ex: backgroundColor Vs. background-color),所以我們需要處理一下 propName,
處理的方式則用運用 RE 來處理:
    function changePropName(propName) {
        var pattern = /[A-Z]/;
        var c = ("-" + (propName.match(pattern))).toLowerCase();
        var s = propName.split(pattern);
        var result = s[0];
        if (s.length > 1) {
            result += c + s[1];
        }
        return result;
    }
利用 RE 找出字串中的大寫字元,以 backgroundColor為例
mach 會找出 C
split 則會找出 background,olor
然後就把 'background' + '-' + 'c' + 'olor' 就完成了我們要的轉換了。




2012年7月26日

[.Net] Gotcha: Don't use <xhtmlConformance mode="Legacy"/> if you want to cross browser

好吧,我承認,這標題是學 Gotcha: Don't use <xhtmlConformance mode="Legacy"/> with ASP.NET AJAX 的。

不過情況有些不一樣,最近接手一個舊的 ASP.NET 網站,
雖然是 .Net Framework 2.0 的網站,但骨子裡其實是從 .Net Framework 1.1 升級的,
我們的需求不多,只要可以和時下流行的 Browser 相容就好,
也不打算引入 ASP.NET AJAX,
於是著手開始調整相容性相關的程式寫法,
調整得差不多後,開始測試,
沒想到一開始就遇到 Validator 不能正常執行的情況,
追了一下發現一個很特別的現象,

造成 Validator 不正常的問題出在 Asp.Net 的 ValidatorOnLoad 中,
會直接用 DOMObject.Attribute 去取值(如下程式碼片段),
但這在其他 IE 以外的瀏覽器是不允許的(IE9標準模式也不允許)!
function ValidatorOnLoad() {
if (typeof(Page_Validators) == "undefined")
return;
var i, val;
for (i = 0; i < Page_Validators.length; i++) {
val = Page_Validators[i];
if (typeof(val.evaluationfunction) == "string") {
eval("val.evaluationfunction = " + val.evaluationfunction + ";");
} // 略 .....
正確的作法應該要用 getAttribute('name') 才對,
但這不能解釋為什麼相同的程式碼在 .Net Framework 2.0 卻能正常的運作,
我用 evaluationfunction 這個關鍵字找了半天,
完全沒有找到相關的說明,
後來拿一個運作正常的網站所產生的 HTML 來和有問題的比較,
發現:

一般正常的應該要長得像這樣:

但他卻長成這樣:

到這裡,情況已經很明朗了,應該有什麼特別的設定,造成這樣的差異,
於是我改用別的關鍵字搜尋
.Net 1.0 and 2.0 hybrid validator not work
終於勉強找到了
Why might ASP.NET be putting JavaScript in HTML Comment blocks, not CDATA?
裡面提到了關鍵字 <xhtmlConformance mode="Transitional" />
再用 xhtmlConformance 就找到了 Gotcha: Don't use <xhtmlConformance mode="Legacy"/> with ASP.NET AJAX。
原來,如果 xhtmlConformance mode="Legacy" 時,
不會處理那些不是標準 XHTML 的屬性,
反之,則會多產生一段 Javascript 把擴充屬性"塞"給 DOM Object ,
確保在不同的瀏覽器下都能夠用 DOMObject.attribute 的方式取到資料。

參考資料:
1. Why might ASP.NET be putting JavaScript in HTML Comment blocks, not CDATA?
2. Gotcha: Don't use <xhtmlConformance mode="Legacy"/> with ASP.NET AJAX
3. xhtmlConformance 項目 (ASP.NET 設定結構描述)
4. KB-由ASP.NET 1.1昇級的網站無法啟用MS AJAX