前回までの記事。

テーブル変更
今回、履歴テーブルにデータを追加する処理を追加するのですが、
ここで1点。
履歴テーブルに列を増やします。
履歴で、どれだけの数が増減して操作後は在庫数いくらになるか、
という情報を表示したいためです。
設計甘々なので、ご容赦…。
MySQL Workbenchでも何でもいいので、
テーブルに変更を加える処理を書きます。
左:変更前
右:変更後
| カラム名 | 型 | 説明 |
| log_id | INT | ログID(主キー) |
| oepration | VARCHAR | 操作内容 |
| product_id | INT | 商品ID |
| time_stamp | DATETIME | 操作日時 |
| カラム名 | 型 | 説明 |
| log_id | INT | ログID(主キー) |
| oepration | VARCHAR | 操作内容 |
| product_id | INT | 商品ID |
| quantity | INT | 在庫増減量 |
| current_stock | INT | 操作後の在庫数 |
| time_stamp | DATETIME | 操作日時 |
ALTER TABLE `inventory_management`.`logs`
ADD COLUMN `quantity` INT NOT NULL AFTER `product_id`,
ADD COLUMN `current_stock` INT NOT NULL AFTER `quantity`,
COMMENT = 'log_id:ログ番号\\noperation:操作内容(作成、入庫、出庫、削除)\\nproduct_id:対象商品ID\\n\\nquantity:在庫増減量\\ncurrent_stock:操作後の在庫数\\ntime_stamp:タイムスタンプ' ;
しかるべきところに追加されました。
諸々変更
テーブルを変更したので、すでに作った処理に関して変更を加えます。
基本、CMySQLManagerクラスのみです。
まずはMySQLManager.h
列名の定義を増やします。
// 履歴テーブル
const sql::SQLString COL_LOGS_ID = "log_id";
const sql::SQLString COL_LOGS_OPERATION = "operation";
const sql::SQLString COL_LOGS_PRODUCT_ID = "product_id";
const sql::SQLString COL_LOGS_QUANTITY = "quantity"; // 新規追加
const sql::SQLString COL_LOGS_CURRENT_STOCK = "current_stock"; // 新規追加
const sql::SQLString COL_LOGS_TIME_STAMP = "time_stamp";続いて関数定義。
まだ作成していない関数ですが、
前回定義しちゃったものを再定義。
// 商品テーブル 編集
BOOL bProductTableEdit(CString strProductID, CString strProductCurrentCount, BOOL bNyuko, CString strQuantity = _T("0"));
// 履歴テーブル更新
BOOL bLogTableInsert(CString strProductID, CString strOperation, CString strQuantity = _T("0"), CString strCurrentStock = _T("0"));引数を増やしました。
なお、どちらも0でデフォルト引数を使用しています。
デフォルト引数、便利ですよねぇ。
Javaなどではオーバーロードでもう一個定義しなきゃいけないのに…。
商品テーブル削除については何も修正必要ありません。
幸い、修正はここまでです。
では、履歴テーブル操作の話です。
履歴テーブル登録処理
前回、商品テーブルへ新規に商品を登録する処理を作りました。
今度は、履歴テーブルに、
なんの商品に対し何の操作行ったか、の履歴を作成します。
まずは、ヘッダーに定義を。
MySQLManager.hを開き、
class CMySQLManagerの外に定義を4つ書きます。
#define LOG_OPERATION_CREATE _T("Create") // 作成
#define LOG_OPERATION_NYUKO _T("Stock In") // 入庫
#define LOG_OPERATION_SHUKKO _T("Stock Out") // 出庫
#define LOG_OPERATION_DELETE _T("Delete") // 削除
class CMySQLManager
{
//略単に文字の定義です。
そして、先ほど修正したbLogTableInsertを、
MySQLManager.cppで処理を書いていきます。
/**
* @fn
* @brief 履歴テーブル 作成関数
* @param CString strProductID 商品ID
* @param CString strOperation 操作内容
* @param CString strQuantity 増減数(デフォルト引数=0)
* @param CString strCurrentStock 操作後の在庫数(デフォルト引数=0)
* @return BOOL TRUE 登録成功
* FALSE 登録失敗
*/
BOOL CMySQLManager::bLogTableInsert(CString strProductID, CString strOperation, CString strQuantity, CString strCurrentStock)
{
try
{
// スキーマセット
m_con->setSchema(SCHEMA);
sql::SQLString sqlStrOperation = strConverter(strOperation);
sql::SQLString sqlStrProductId = strConverter(strProductID);
sql::SQLString sqlStrQuantity = strConverter(strQuantity);
sql::SQLString sqlStrCurrentStock = strConverter(strCurrentStock);
CTime ctime = CTime::GetCurrentTime();
CString strTime = ctime.Format(_T("%Y-%m-%d %H:%M:%S"));
// これで2024-02-29 14:27:56の形になる
sql::SQLString sqlStrTime = strConverter(strTime);
sql::SQLString strSqlQuery = "INSERT INTO " + TABLE_LOGS +
" ( " + COL_LOGS_OPERATION + ", " + COL_LOGS_PRODUCT_ID + ", " + COL_LOGS_QUANTITY + ", " + COL_LOGS_CURRENT_STOCK + ", " + COL_LOGS_TIME_STAMP + " )" +
" VALUES " +
"( '" + sqlStrOperation + "', '" + sqlStrProductId + "', " + sqlStrQuantity + ", " + sqlStrCurrentStock + ", '" + sqlStrTime + "' )";
//クエリ実行
std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
pstmt->executeUpdate();
}
catch (sql::SQLException& e)
{
ErrSQLException(e);
return FALSE;
}
return TRUE;
}特記は特にありません。
現在時刻取得は22~23行目でできます。
strOperationには、最初にdefineした4つのどれかが入ります。
さぁ、この関数をどこで呼ぶかですが。
前回作成した、商品テーブル 作成関数の中で呼びます。
少し修正。
/**
* @fn
* @brief 商品テーブル 作成関数
* @param CString strProductID 商品ID
* @param CString strProductName 商品名
* @param CString strCategory カテゴリー
* @param CString strUnitPrice 単価
* @param CString strProductCount 商品在庫数
* @param CString strProductAlert 在庫アラート
* @return BOOL TRUE 登録成功
* FALSE 登録失敗
*/
BOOL CMySQLManager::bProductTableCreate(CString strProductID, CString strProductName, CString strCategory,
CString strUnitPrice, CString strProductCount, CString strProductAlert)
{
try
{
// 略
std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
pstmt->executeUpdate();
// ここまで処理が来たら、登録成功している
// 履歴テーブル登録
if (bLogTableInsert(strProductID, LOG_OPERATION_CREATE, strProductCount, strProductCount) == FALSE)
{
AfxMessageBox(_T("履歴テーブルの登録に失敗しました。\r\n管理者へ連絡を!!"), MB_ICONERROR);
return FALSE;
}
}
catch (sql::SQLException& e)
{
ErrSQLException(e);
return FALSE;
}
return TRUE;
}商品登録後、履歴テーブルへ登録を行います。
本当は、履歴テーブル登録失敗すると、
商品テーブルも何かしら作業させないといけないとは思うのですが、
そんな細かい設計は今回しません。
「作成」の場合、在庫増加数と現在在庫数は当然同じ値です。
いったんこれで行きましょうか。
前回作ったID=11111、商品名Appleは削除しておきます。
動作確認
こんなデータを登録します。

OKボタンを押すと、商品テーブルに登録できるのは前回確認済み。

さて、履歴テーブルの方はというと…。

なんか、一瞬Errorってのが見えたんですが、
すぐ正常になりました。
タイムスタンプもいいですね。
(いつ作業してるかバレちゃう)
形式が「年-月-日」のつもりだったのですが、「年/月/日」になってますね。
ま、わからないことはないので、これで進めます。
入庫・出庫編集
さ、データが入ったので、
お次は編集作業を行います。
まずは、編集画面から。
初期状態はこんな感じなはずです。

最初にやりたいのは、
商品IDが入力されると、商品テーブルを見に行き、
商品名・カテゴリー・単価・推移テキスト・在庫アラート
の5つを表示させたいです。
このために、商品IDキルフォーカス関数を作りました。
では作ります。
商品ID キルフォーカス処理
まず最初に、CString型メンバ変数を一つ作ります。
編集画面用にちょこっと、保存用に使いたいのです。
EditMain.hに定義。
private:
CString m_strCurrentStock; // 現在の在庫数(編集画面用)EditMain.cppのOnEnKillfocusEditEditmainProductId()を修正します。
/**
* @fn
* @brief 商品IDエディットボックス キルフォーカス用
* @param なし
* @return なし
*/
void CEditMain::OnEnKillfocusEditEditmainProductId()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
// メンバ変数更新
UpdateData(TRUE);
if (m_strProductID.GetLength() != 5)
{
AfxMessageBox(_T("Idが5桁ではありません。"), MB_ICONERROR);
return; // 抜ける
}
// 編集と削除だけ、検索をかける
switch (m_enMode)
{
case enEdit:
case enDelete:
break;
default:
return;
}
CMySQLManager Manager;
Manager.GetConnection();
CString strCategory = _T(""); // カテゴリー用
CString strCurrentCount = _T(""); //現在の在庫数
CString strProductNameTemp = _T(""); // 商品名
CString strUnitPriceTemp = _T(""); // 単価
CString strProductAlertTemp = _T(""); // 在庫アラート
if (Manager.bGetProductInformation(m_strProductID, strProductNameTemp , strCategory, strUnitPriceTemp , strCurrentCount, strProductAlertTemp) == TRUE)
{
// 商品が検索出来た
m_strProductName = strProductNameTemp;
m_strUnitPrice = strUnitPriceTemp;
m_strProductCount = _T(""); // ここのエディットボックスは初期化しておく
m_strProductAlert = strProductAlertTemp;
m_strCurrentStock = strCurrentCount;
// カテゴリーセット
m_ctrlComboCategory.SelectString(-1, strCategory); //文字列に合う項目を探し、セット
// 推移テキストセット
CString strText = _T("");
strText.Format(_T("現在%s⇒変更後XXXXX"), strCurrentCount);
m_ctrlStaticTransition.SetWindowText(strText);
}
else
{
// 何かしらエラー
// 特に何もしない
}
//コントロール更新
UpdateData(FALSE);
}メンバ変数更新後、IDチェックを行います。
そのご、MySQLManager側の関数を呼び、
引数へ情報を渡すようにします。
この関数はまだ作成していないので、現時点ではエラーなはずです。
もしTRUEが返ってくれば、メンバ変数を上書きし、コントロール更新。
FALSEなら、何もしません。
正直、初期化か何かするべきかなぁとは思ってるのですが、
いい案が浮かびませんでした。
編集・削除画面 データ取得
順番が本来逆ですが、
MySQLManager.hで、先ほどの関数を定義します。
public:
// 編集・削除画面 データ取得
BOOL bGetProductInformation(CString strProductID, CString& strProductName, CString& strCategory, CString& strUnitPrice, CString& strProductCount, CString& strProductAlert);引数、商品ID以外は参照渡しです。
処理内で上書きします。
続いて、MySQLManager.cppで処理記述。
/**
* @fn
* @brief 編集・削除画面 データ取得
* @param CString strProductID 商品ID
* @param CString& strProductName 商品名
* @param CString& strCategory カテゴリー
* @param CString& strUnitPrice 単価
* @param CString& strProductCount 在庫数
* @param CString& strProductAlert 在庫アラート
* @return BOOL TRUE 検索成功
* FALSE 検索失敗
*/
BOOL CMySQLManager::bGetProductInformation( CString strProductID,
CString& strProductName,
CString& strCategory,
CString& strUnitPrice,
CString& strProductCount,
CString& strProductAlert)
{
try
{
// スキーマセット
m_con->setSchema(SCHEMA);
sql::SQLString sqlStrProductId = strConverter(strProductID); // 商品ID
sql::SQLString strSqlQuery = "SELECT * FROM " + TABLE_PRODUCT + " WHERE " + COL_PRODUCT_ID + " = '" + sqlStrProductId + "' AND " + COL_PRODUCT_DISCONTINUE + " IS NULL";
//クエリ実行
std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
if (!res->next())
{
AfxMessageBox(_T("該当の商品は存在しません。"), MB_ICONERROR);
return FALSE;
}
else
{
// 商品名
std::string strCol = res->getString(COL_PRODUCT_NAME);
strProductName = strConvertFromUTF8ToUTF16(strCol);
// カテゴリー
strCol = res->getString(COL_PRODUCT_CATEGORY);
strCategory = strConvertFromUTF8ToUTF16(strCol);
// 単価
strCol = res->getString(COL_PRODUCT_UP);
strUnitPrice = strConvertFromUTF8ToUTF16(strCol);
// 在庫数
strCol = res->getString(COL_PRODUCT_COUNT);
strProductCount = strConvertFromUTF8ToUTF16(strCol);
// 在庫アラート
strCol = res->getString(COL_PRODUCT_ALERT);
strProductAlert = strConvertFromUTF8ToUTF16(strCol);
}
}
catch (sql::SQLException& e)
{
ErrSQLException(e);
return FALSE;
}
return TRUE;
strSqlQueryの作成文。
discontinueがNULL値を検索します。
ここが、1ならばそれは削除済みを意味するので、
現存する商品検索をかけます。
もし検索出来たら、
順にデータを取ってきて、引数変数に代入しています。
これで、呼び出し元にデータがわたります。
入庫・出庫 キルフォーカス処理
例えば、ラジオボタンで入庫を選択、
現在の在庫数が50(すなわち、テキストが「現在50⇒変更後XXXXX」)、
入庫のエディットボックスに30が入力さたとします。
ここでキルフォーカスを行うと、推移テキストが
「現在50⇒変更後80」になる仕様です。
逆に出庫にラジオボタンがセットされていた場合、
「現在50⇒変更後20」
とさせます。
当然ですが、変更後にマイナスになればエラーとさせます。
また、作成と同様、
在庫アラートを下回る場合は注意喚起を出します。
まずは、EditMain.hで、先を実現させる関数を定義。
private:
void SetTransitionStatus(); // 推移テキスト設定続いてEditMain.cppで実現。
OnEnKillfocusEditEditmainProductCountを前回自動作成させたので、
その中で設定関数を呼びます。
注意なのは、
この処理は「編集」の時だけで、
「作成」・「削除」には必要ないので制御はかけます。
/**
* @fn
* @brief 在庫数(入庫・出庫)エディットボックス キルフォーカス用
* @param なし
* @return なし
*/
void CEditMain::OnEnKillfocusEditEditmainProductCount()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetTransitionStatus(); // 設定関数呼応
}はい、これだけ。
何でここに処理書かないか、ですが。
後でラジオボタン変更後に呼応される関数内を(自動)作成し、
その中でも同じ処理をしたいため、
共通化しました。
モードの制御は、この関数内でまとめて行います。
ラジオボタンでカチカチ切り替わらなければいけないこと、
前回すっかり忘れてた…。
さて、SetTransitionStatusの中身ですが。
/**
* @fn
* @brief 推移テキスト変更関数
* @param なし
* @return なし
*/
void CEditMain::SetTransitionStatus()
{
switch (m_enMode)
{
case enEdit: // 編集の時だけ進める
break;
default: // 作成・削除の場合は抜ける
return;
}
// メンバ変数更新
UpdateData(TRUE);
if (m_strProductCount == _T(""))
{
// 何も入力されていない場合、これ以上の処理は必要ないので...
return;
}
int iChange = _ttoi(m_strProductCount); // エディットボックスに入った数を取得
int iAfterCount = 0; // 変更後在庫数
CString strText; // テキスト初期化用
strText.Format(_T("現在%s⇒変更後XXXXX"), m_strCurrentStock);
if (m_ctrlRadioNyuko.GetCheck() == BST_CHECKED)
{
// 入庫にチェックが入っている
// 加算
iAfterCount = _ttoi(m_strCurrentStock) + iChange;
}
else if (m_ctrlRadioShukko.GetCheck() == BST_CHECKED)
{
// 出庫にチェックが入っている
// 減算
iAfterCount = _ttoi(m_strCurrentStock) - iChange;
}
else
{
// プログラムエラー
AfxMessageBox(_T("編集画面でプログラムエラー\r\n管理者へ連絡を!!。"), MB_ICONERROR);
}
// チェック
if (iAfterCount < 0)
{
// 0を下回った
AfxMessageBox(_T("在庫数が0を下回るため、登録できません"), MB_ICONERROR);
m_strProductCount = _T(""); // 初期値に戻す
m_ctrlStaticTransition.SetWindowText(strText);
UpdateData(FALSE);
return; // 抜ける
}
else if (iAfterCount <= _ttoi(m_strProductAlert))
{
// 在庫アラートを下回っている
CString strMessage = _T("在庫数が在庫アラートよりも下回ります。\r\nよろしいですか?");
int iResponse = AfxMessageBox(strMessage, MB_YESNO | MB_ICONQUESTION);
if (iResponse == IDNO)
{
m_strProductCount = _T(""); // 初期値に戻す
m_ctrlStaticTransition.SetWindowText(strText);
UpdateData(FALSE);
return; // 抜ける
}
}
else
{
// 問題ないので処理を進める
}
// テキスト更新
strText.Format(_T("現在%s⇒変更後%d"), m_strCurrentStock, iAfterCount);
m_ctrlStaticTransition.SetWindowText(strText);
UpdateData(FALSE);
}- ~23行目:条件に合ってないので、処理を実行しません。
- 25~30行目:変数を用意。
- 36行目:入庫にチェックが入っていたら、現在在庫数+エディットボックスの数
- 43行目:出庫にチェックが入っていたら、現在在庫数-エディットボックスの数
- 52~60行目:変更後が0になればそりゃエラー扱い
- 61~73行目:在庫アラートを下回ってるので、一応確認
- 80~81行目:テキストを反映させます。
最後に、リソースビューからダイアログを開き、
入庫ラジオボタンと出庫ラジオボタンをダブルクリック。
※入庫ラジオボタンと同じ位置に、
「在庫数」ラベルを置いてるので、間違ってそちらを選択しないよう…
自動で関数が2つ追加されます。
OnBnClickedRadioEditmainNyukoとOnBnClickedRadioEditmainShukko。
この中でも、設定関数を呼応します。
/**
* @fn
* @brief 入庫ラジオボタンが押された
* @param なし
* @return なし
*/
void CEditMain::OnBnClickedRadioEditmainNyuko()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetTransitionStatus();
}
/**
* @fn
* @brief 出庫ラジオボタンが押された
* @param なし
* @return なし
*/
void CEditMain::OnBnClickedRadioEditmainShukko()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetTransitionStatus();
}動作確認
キルフォーカスだけでなんでこんなに時間かかってんだ…。
ビルドします。
編集選択画面から、編集を選択。
ひとまず、今記事最初に登録した、
ID=22222の番号でも検索してみましょう。
存在しないIDと、存在するIDを検索IDに入力します。
その後、TABキーでフォーカスを外します。
ID=22221(存在しない)

ID=22222(存在する)

存在しない方はちゃんとエラーが返ってき、
存在する方はデータが取得できました。
今度は在庫数を変更します。
120とでも入力しましょう。
入力後、同じくTABでフォーカスを外すと。

加算してくれました。
出庫は減算なので注意ですね。
それぞれ、201、195、189で出庫にしてみました。
出庫数201
マイナスになる

出庫数195
在庫アラート以下

出庫数189
問題なし

ちゃんとそれに合ったメッセージが出てくれました。
在庫アラート以下の場合、
「はい」を選択で問題なしと同じ処理になることも確認済です。
後はSQL文…。
余談
TABキーでフォーカスが外れる際、
次にどこのコントロールへフォーカスが当たるか。
基本、コントロールを配置した順なのですが、
変更できます。
リソースビューから該当の画面を開きます。
ここでCtrl + Dを押すと

自分の場合はこの順です。
(15(入庫ラジオボタン)どこ行ったんだって話ですが、
11(在庫ラベル)と同じ配置なので被ってます)
番号クリックで順番を変えられるので、
お好みに…。
なお当然ですが、
EnableWindow(FALSE)で非活性にすると、
そこへはフォーカスは当たらず次の番号へ飛びます。
商品テーブル更新
編集画面側
まずは入力チェック。
ここはメッセージ出すだけで大丈夫そうです。
EditMain.cppで、前回作成したbInutCheckを修正します。
BOOL CEditMain::bInutCheck()
{
// メンバ変数更新
UpdateData(TRUE);
// 略
if (m_enMode == enCreate)
{
// 略 前回作成
}
else if (m_enMode == enEdit)
{
// 編集画面の場合
int iUpdatedCount = 0;
if (m_ctrlRadioNyuko.GetCheck() == BST_CHECKED)
{
// 入庫にチェックがある場合
// 加算
iUpdatedCount = _ttoi(m_strCurrentStock) + _ttoi(m_strProductCount);
}
else
{
// 出庫にチェックがある場合
// 減算
iUpdatedCount = _ttoi(m_strCurrentStock) - _ttoi(m_strProductCount);
}
CString strMessage = _T("");
strMessage.Format(_T("以下で登録します。よろしいですか?\r\n商品ID:%s\r\n現在個数:%s\r\n更新後在庫数:%d"),
m_strProductID, m_strCurrentStock, iUpdatedCount);
int iResponse = AfxMessageBox(strMessage, MB_YESNO | MB_ICONQUESTION);
if (iResponse == IDNO)
{
//「いいえ」が押された
return FALSE;
}
}
return TRUE;
}続いて、OKボタンが押されたら。
void CEditMain::OnBnClickedOk()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
if (bInutCheck() == TRUE)
{
CString strCategory = _T("");
GetDlgItemText(IDC_COMBO_EDITMAIN_CATEGORY, strCategory); // カテゴリーにセットされている文字列取得
CMySQLManager Manager;
Manager.GetConnection();
if (m_enMode == enCreate)
{
// 略 前回作成
}
else if (m_enMode == enEdit)
{
// 編集モード
BOOL bRadioNyuko = TRUE; // 入庫にラジオボタンがセットされている
if (m_ctrlRadioShukko.GetCheck() == BST_CHECKED)
{
// 出庫にラジオボタンがセットされている
bRadioNyuko = FALSE;
}
if (Manager.bProductTableEdit(m_strProductID, m_strCurrentStock, bRadioNyuko, m_strProductCount) == TRUE)
{
// 更新完了
CDialogEx::OnOK();
}
else
{
// 失敗した
return;
}
}
}
}18~23行目。
入庫ならばTRUE、出庫ならFALSEにして、
SQL用の関数へ変数を渡してます。
さ、最後にSQL側…。
MySQL処理
先のbProductTableEditを作ります。
MySQLManager.cppにて。
/**
* @fn
* @brief 商品テーブル 編集関数
* @param CString strProductID 商品ID
* @param CString strProductCount 現在の在庫数
* @param BOOL bNyuko TRUE 入庫処理
* FALSE 出庫処理
* @param CString strQuantity 増減数
* @return BOOL TRUE 編集成功
* FALSE 編集失敗
*/
BOOL CMySQLManager::bProductTableEdit(CString strProductID, CString strProductCount, BOOL bNyuko, CString strQuantity)
{
int iTotalCount = _ttoi(strProductCount); // 現在の在庫数
CString strOperation = LOG_OPERATION_NYUKO; // 入庫操作
if (bNyuko == TRUE)
{
iTotalCount += _ttoi(strQuantity); // 入庫なら増加させる
}
else
{
iTotalCount -= _ttoi(strQuantity); // 出庫なら減算させる
strOperation = LOG_OPERATION_SHUKKO; //操作は出庫
}
CString strTotalCount = _T("");
strTotalCount.Format(_T("%d"), iTotalCount); // CString化
try
{
// スキーマセット
m_con->setSchema(SCHEMA);
sql::SQLString sqlStrId = strConverter(strProductID);
sql::SQLString sqlStrProductCount = strConverter(strTotalCount); // テーブルに登録する、最終的な在庫数
//UPDATE文を作る
sql::SQLString strSqlQuery = "UPDATE " + TABLE_PRODUCT + " SET " + COL_PRODUCT_COUNT + " = " + sqlStrProductCount + " WHERE " + COL_PRODUCT_ID + " = '" + sqlStrId + "'";
//クエリ実行
std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
pstmt->executeUpdate();
// ここまで処理が来たら、登録成功している
// 履歴テーブル登録
if (bLogTableInsert(strProductID, strOperation, strQuantity, strTotalCount) == FALSE)
{
AfxMessageBox(_T("履歴テーブルの登録に失敗しました。\r\n管理者へ連絡を!!"), MB_ICONERROR);
return FALSE;
}
}
catch (sql::SQLException& e)
{
ErrSQLException(e);
return FALSE;
}
return TRUE;
}- 14~26行目:前処理。操作内容や、最終的な在庫数を決めます。
- 31~41行目:SQL実行
- 45行目:履歴テーブル登録
履歴テーブルに関しては、前回もう作ったので、
今回修正する必要はありません。
引数の順番だけ注意。
(もうちょっと、わかりやすい設計にすべきだなぁ)
動作確認
3回目の動作確認。
さっきのデータに、120入庫してみます。

OKを押すと、メッセージが出てきました。

「はい」を押します。
不親切なので、
今度、正常に終わったら完了メッセージを出しましょう。
今のままだと何も言われず画面が閉じるので。
テーブルを確認します。
まずは商品テーブル。

商品数が、320に更新されています。
つづいて履歴テーブル。

2行目、120のStock In(入庫)で、320になりました。
結構できた( ;∀;)
ここから、
100入庫⇒80出庫⇒300出庫
してみます。
320 + 100 – 80 – 300 = 40になるはず。
さて。
商品テーブル

40です。
履歴テーブルは…

よーしよし!
終わりに
今回は編集画面のデータから、
商品テーブルと履歴テーブルへ更新をかけました。
削除処理作りたかったけど、
ちょっとスタミナ切れ…。
これは簡単なんですけどね。
次回は削除処理と、
そろそろメイン画面へ商品情報出しましょうかね。
今回はここまで。

コメント