2014年11月6日 星期四

健保e化抽審-3(國際條碼、Code 128)

我們原本使用的是 Code 39 的條碼,因為我們為了簡化進貨或盤點時的資料輸入(如:有效日期或批號等等...),再加上 Code 39 的資料越多則長度就越長,甚至會超出 Barcode Reader 的可掃瞄範圍,再加上很多的醫藥材都是使用 Code 128,所以我們導入了國際條碼。這個過程真的花了一番心力,現在我們將這項成果導入在文件的生成與辨識。

注意!以下這些 Code,已經忘記出處在那裡,若有人知道煩請告知,真是對不起原作者!

一、了解及準備 Code 128 字型檔
這兩篇 Blog 不錯,對於不了解什麼是 Barcode 的人,可以有初步的認識。
二、轉換 Barcode 資料及產生 Barcode 影像
Private Sub btnCode128_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCode128.Click

    Dim _data As String = "038000356216"

    ' Font Style
    Dim privateFonts As New System.Drawing.Text.PrivateFontCollection()
    privateFonts.AddFontFile("C:\WINDOWS\Fonts\3of9.ttf")
    privateFonts.AddFontFile("C:\WINDOWS\Fonts\code128.ttf")

    Dim font39 As New System.Drawing.Font(privateFonts.Families(0), 36)
    Dim font128 As New System.Drawing.Font(privateFonts.Families(1), 36)

    ' Code39:資料前後加上"*"做為啟始碼/停止碼即可
    Me.lblCode39.Font = font39
    Me.lblCode39.Text = BarcodeConverter39.StringToBarcode(_data)
    Me.lblCode39.AutoSize = True

    ' Code128:是由 啟始碼+資料碼+檢查碼+停止碼 所組合而成的條碼
    Me.lblCode128.Font = font128
    Me.lblCode128.Text = BarcodeConverter128.StringToBarcode(_data)

    ' Image Style
    Dim barcode As New BarcodeLib.Barcode()

    barcode.IncludeLabel = True
    barcode.Width = 300
    barcode.Height = 150
    barcode.Encode(BarcodeLib.TYPE.CODE128, _data)

    Dim ms As New MemoryStream()
    barcode.SaveImage(ms, BarcodeLib.SaveTypes.PNG)

    Me.PictureBox1.Image = System.Drawing.Image.FromStream(ms)

End Sub

三、取出 Barcode 資料
參考 GS1 General Specifications(Version 12),我們需要取得商品代號、有效日期及批號。
  • (01)商品代號~固定長度(數字形態,長度14)
  • (17)有效日期~固定長度(數字形態,長度6)
  • (10)批號~變動長度(數字形態,長度最多20)
private void btnGS1Barcode_Click(object sender, EventArgs e)
{
 string barcode;
 // 測試樣本
 //barcode = "0108888893102146";
 //barcode = "010888889310214617120315";
 barcode = "01088888931021461712031510W1040190";
 string Ai01; // (01)08888893102146
 string Ai17; // (17)120315     
 string Ai10; // (10)W1040190

 GS1BarcodeConvert(barcode, out Ai01, out Ai17, out Ai10);

 Application.DoEvents();
}

private void GS1BarcodeConvert(string pBarcode, out string pAi01, out string pAi17, out string pAi10)
{
 pAi01 = string.Empty;
 pAi17 = string.Empty;
 pAi10 = string.Empty;

 string AI;
 string DataContent = pBarcode;
 while (!string.IsNullOrEmpty(DataContent))
 {
  AI = DataContent.Substring(0, 2);

  if (AI == "01")
  {
   // Global Trade Item Number (GTIN)
   // N2 N14
   pAi01 = DataContent.Substring(2, 14);
   if (checkSum(pAi01.Remove(pAi01.Length - 1, 1), Int32.Parse(pAi01.Substring(pAi01.Length - 1, 1))))
   {
   }
   else
   {
    pAi17 = string.Empty;
   }
   DataContent = DataContent.Substring(16);
  }
  else if (AI == "17")
  {
   // Expiration Date (YYMMDD) 
   // N2 N6
   pAi17 = DataContent.Substring(2, 6);
   if (checkDate(pAi17) > DateTime.MinValue)
   {
   }
   else
   {
    pAi17 = string.Empty;
   }
   DataContent = DataContent.Substring(8);
  }
  else if (AI == "10")
  {
   // Batch or Lot Number 
   // N2 X..20
   pAi10 = DataContent.Length < 20 ? DataContent.Substring(2, DataContent.Length - 2) : DataContent.Substring(2, 20);
   DataContent = DataContent.Substring(pAi10.Length   2);
  }
 }

}

private Boolean checkSum(String pgtin, Int32 pchecksum)
{
 Boolean ret = false;
 Int32 glength = 0;
 Int32 total = 0;
 Int32 cSum = 0;
 Int32[] mutiply = { 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 };
 glength = 17 - pgtin.Length;
 for (int i = 0; i < pgtin.Length; i  )
 {
  total = total   (Int32.Parse(pgtin[i].ToString()) * mutiply[i   glength]);
 }
 cSum = 10 - (total % 10);
 if (cSum == pchecksum)
 {
  ret = true;
 }
 return ret;

}

private DateTime checkDate(string pdate)
{
 DateTime ret = DateTime.MinValue;
 DateTime convertedDate = DateTime.MinValue;
 String dFormat = "yyMMdd";
 if (DateTime.TryParseExact(pdate, dFormat, System.Globalization.CultureInfo.CurrentCulture, System.Globalization.DateTimeStyles.None, out convertedDate))
 {
  ret = convertedDate;
 }
 return ret;
}

private void GS1DataConvert(string pBarcode, out string pAi01, out string pAi17, out string pAi10)
{
 pAi01 = string.Empty;
 pAi17 = string.Empty;
 pAi10 = string.Empty;

 String aiFull = "";
 String aiWCheckSum = "";
 String aiValue = "";
 Int32 aiCheckSum = 0;
 Int32 aiMinLength = 0;
 Int32 aiMaxLength = 0;
 int index = 0;
 if (pBarcode.Contains("01"))
 {
  index = pBarcode.IndexOf("01")   2;
  AII sai = getAiInfo("01");
  aiMinLength = sai.minLength;
  aiMaxLength = sai.maxLength;

  aiFull = pBarcode.Substring(index - 2, aiMaxLength   2);
  aiWCheckSum = pBarcode.Substring(index, aiMaxLength);
  aiValue = aiWCheckSum.Remove(aiWCheckSum.Length - 1, 1);
  aiCheckSum = Int32.Parse(aiWCheckSum.Substring(aiWCheckSum.Length - 1, 1));
  if (checkSum(aiValue, aiCheckSum))
  {
   pBarcode = pBarcode.Replace(aiFull, String.Empty);
   pAi01 = aiValue;
  }
 }
 if (pBarcode.Contains("17"))
 {
  index = pBarcode.IndexOf("17")   2;
  AII sai = getAiInfo("17");
  aiFull = pBarcode.Substring(index - 2, sai.minLength   2);
  aiValue = pBarcode.Substring(index, sai.minLength);
  if (checkDate(aiValue) > DateTime.MinValue)
  {
   pBarcode = pBarcode.Replace(aiFull, String.Empty);
   pAi17 = aiValue;
  }
 }
 if (pBarcode.Contains("10"))
 {
  index = pBarcode.IndexOf("10")   2;
  AII sai = getAiInfo("10");
  aiMinLength = sai.minLength;
  aiMaxLength = pBarcode.Length < sai.maxLength ? pBarcode.Length - 2 : sai.maxLength;
  aiFull = pBarcode.Substring(index - 2, aiMaxLength   2);
  aiValue = pBarcode.Substring(index, aiMaxLength);
  pAi10 = aiValue;
 }

}

public AII getAiInfo(String pAi)
{
 AII naii = new AII();
 if (pAi == "01")
 {
  naii.AICode = "01";
  naii.minLength = 14;
  naii.maxLength = 14;
  return naii;
 }
 if (pAi == "17")
 {
  naii.AICode = "17";
  naii.minLength = 6;
  naii.minLength = 6;
  return naii;
 }
 if (pAi == "10")
 {
  naii.AICode = "10";
  naii.minLength = 1;
  naii.maxLength = 20;
 }

 return naii;
}

public struct AII
{
 public String AICode;
 public Int32 minLength;
 public Int32 maxLength;
}
四、參考資料
[1] Free Barcode Font - Barcode String Builder
[2] Barcode Image Generation Library
[3] EAN128 or GS1-128 decode c#

沒有留言:

張貼留言