データベース千夜一夜第16回

プログラミングとSQL(4)
~販売データの入力処理(前編)
長谷川裕行
有限会社 手國堂

顧客情報と伝票番号の表示

お客様IDが入力されたら伝票番号を生成してラベル“lblSlipNumber”に表示し、併せて「顧客_mr」から顧客のレコードを検索~抽出して氏名などを表示します。


- ID未入力時のエラーについて -

この処理は、お客様IDを入力するテキストボックス“txtCstmId”がフォーカスを失ったとき(LostFocusイベント発生時)に実行します。

このサンプルでは、テキストボックス“txtCstmId”が空白のときにはエラーメッセージを出すようにしています。そのため、“txtCstmId”が空白のまま[終了]ボタンをクリックしようとしたときにもLostFocusイベントが発生してエラーメッセージが表示されます。

このサンプルは、OCRカードなどから連続で読み込むことを前提とし、処理の過程が分かるようデータ入力部分をユーザーの手入力に置き換えたものです。そのため、上記のような状態になることを防ぐ仕組みは用意していません。

[終了]ボタンをクリックしたときに「お客様IDを入力してください」というエラーメッセージが表示されたら、txtCstmIdに取り敢えず何らかの数値を入力し、その後もう一度[終了]ボタンをクリックすればアプリケーションは終了します。サンプルを試すときには、この点に注意してください。


- 伝票番号の生成と表示~txtCstmId_LostFocus -

このプロシージャでは実際のレコード検索は行わず、後述する下請けプロシージャ“SearchCustomer”に委ねています。そのため、下請けプロシージャに渡すSQL文字列と伝票番号生成用のレコード件数を受け取る変数の2つを準備するだけです。

   Private Sub txtCstmId_LostFocus _
   (ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtCstmId.LostFocus
   Dim strSql As String ----------- 下請けプロシージャに渡すSQL
Dim intSlipCount As Integer ---- レコード件数を保存

先に紹介したFunctionプロシージャGetSlipCountを呼び出し、現在の「売上ヘッダ」のレコード件数を変数“intSlipCount”に受け取ります。

      intSlipCount = GetSlipCount()

このとき「-1」が返ってきたらエラーとし、メッセージを表示してプロシージャを抜けます。エラー発生の原因としては、例えば、他のアプリケーションが「売上ヘッダ」に排他アクセスしているような場合などが考えられます。しばらくしてからやり直せばよいので、アプリケーション自体は終わらせません。

       If intSlipCount = -1 Then
  MessageBox.Show _
     ("データベースエラーのため伝票番号を生成できませんでした。" & _
vbCrLf & _
"お客様番号の入力からやり直してください。", _
"伝票番号エラー", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
  txtCstmId.Select()
  Exit Sub
End If

無事レコード件数が取得できたらそれに1を加算した値を、テキストボックス“txtDate”に表示されている日付の後に付け足して伝票番号とします。この値は、グローバル変数“strSlipNumber”に保存すると共にラベル“lblSlipNumber”にも表示します。

       strSlipNumber = _
     Format(CDate(txtDate.Text), "MMdd")   _ ---- 日付を書式化
Format(intSlipCount + 1, "000#") ----------- 連番を書式化
lblSlipNumber.Text = strSlipNumber

伝票番号の生成が終わったら、顧客情報の検索SQLを引数にして下請けプロシージャ“SearchCustomer”を呼び出します。

       ↓顧客情報の検索用SQL
strSql = _
"SELECT 氏名, 郵便番号, 住所1, 住所2, 電話 FROM 得意先_mr " & _
"WHERE お客様ID = "

↓テキストボックスの未入力チェック
If Me.txtCstmId.Text = "" Then
     MessageBox.Show("お客様IDを入力してください。", _
"入力エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else ↓下請けプロシージャを呼び出して検索
strSql = strSql   txtCstmId.Text
SearchCustomer(strSql)

ここまでの処理を終えたらこの顧客の受注は確定され、あとは個々の商品を特定して数量を入力する処理(フォームの右側)に移行します。そのため、この段階で「売上ヘッダ」に伝票番号・日付・お客様IDなどの値を記録したレコードを1件追加します。

この処理は、後述する“AddHeader”という下請けプロシージャで行います。“AddHeader”はFunctionプロシージャで、戻り値から成功と失敗を判別できるようにしていますが、処理を簡略化するため戻り値は利用していません。

         AddHeader()
End If
  End Sub


- 顧客情報の取得と表示~SearchCustomer -

顧客情報を取得して表示する下請けプロシージャの処理は、これまでに紹介してきたレコード抽出と同じ仕組みなので、本質的には非常にシンプルです。

ただ、テーブル「顧客_mr」はこれまでに扱ってきた商品の情報(テーブル「商品_mr」、サンプルでは「商品_dmy」を使用)とは異なり、すべてのフィールドに値が記録されているわけではありません。特に、「住所1」と「住所2」の2つのフィールドに分かれている住所の情報は、短い住所の場合「住所2」フィールドが未入力となっており、そこはNull値のままとなっています。このNull値の扱いに対処するため、単純なレコード抽出に比べてソースはやや長くなります。

データベースを扱うオブジェクト変数と、SQLを保存する文字列変数を準備します。

  Private Sub SearchCustomer(ByVal strSql As String)
       Dim objConnect As New SqlConnection
Dim objCommand As New SqlCommand
Dim objDataReader As SqlDataReader

接続文字列の指定からSQLの設定までは、これまでに紹介した流れとまったく同じなので省略します。

       Try
     objConnect.ConnectionString = _
objCommand.CommandText = strSql

SQLの発行結果を受け取った後、フィールド名を引数にしてDataReaderからフィールドの値を読み出し、対応する各ラベルにそれを表示します。すべて文字列なので型の変換は不要ですが、フィールドが空でNull値が返ってきた場合には、それを空文字列("")に変換しなければなりません。この処理を、表示するフィールドの数だけ実行することになります。

フィールドのNull値(データベースのNull)はプログラミング言語のNullとは異なり、ソース上では“DBNull”という記号定数で扱います。値がDBNullかどうかは、IsDBNull関数で判断します。引数にフィールドの値を与えて呼び出し、それがDBNullならTrueが返ってきます。従って、以下のようなIf文の連続となります。

           ↓SQLを発行
objDataReader = _
   objCommand.ExecuteReader(CommandBehavior.SingleRow)
↓検索に成功した場合
If objDataReader.Read() Then
               ↓値がNullなら""に変換する
If Not IsDBNull(objDataReader("氏名")) Then
     lblCstmName.Text = objDataReader("氏名")
Else
lblCstmName.Text = ""
End If
If Not IsDBNull(objDataReader("郵便番号")) Then
lblCstmZip.Text = objDataReader("郵便番号")
Else
lblCstmZip.Text = ""
End If
            :
      (以下略)

ラベルに表示した文字列(顧客の氏名や住所)は、最初に宣言した構造体structMemberInfo型の変数Customerの各メンバにも代入しておきます。

               With Customer
     .strDate = txtDate.Text
.strName = lblCstmName.Text
.strZip = lblCstmZip.Text
.strAddr1 = lblCstmAddr1.Text
.strAddr2 = lblCstmAddr2.Text
.strPhone = lblCstmPhone.Text
End With

これ以降の例外処理と接続を閉じる処理は、既に紹介してきたものと同じなので省略します。



トップページ
全体の仕様を把握する
フォームのデザイン
データの準備
下請け処理~コントロールと変数の初期化
伝票番号の生成
顧客情報と伝票番号の表示
ID未入力時のエラーについて
伝票番号の生成と表示~txtCstmId_LostFocus
顧客情報の取得と表示~SearchCustomer
レコードの追加~AddHeader
あとがき
Copyright © MESCIUS inc. All rights reserved.