MFCで在庫管理システム作成 5

前回までの記事。

MFCで在庫管理システム作成 4
前回までの記事。在庫編集画面作るモノ前回、編集選択画面を作成しました。今回は、選択状況に応じて在庫状況を編集します。必要なコントロールは、・ラベルスタティックテキスト×6・商品ID、商品名、単価、在庫数、在庫アラートのエディットボックス・カ...

テーブル変更

今回、履歴テーブルにデータを追加する処理を追加するのですが、
ここで1点。

履歴テーブルに列を増やします。

履歴で、どれだけの数が増減して操作後は在庫数いくらになるか、
という情報を表示したいためです。

設計甘々なので、ご容赦…。

MySQL Workbenchでも何でもいいので、
テーブルに変更を加える処理を書きます。
左:変更前
右:変更後

カラム名説明
log_idINTログID(主キー)
oeprationVARCHAR操作内容
product_idINT商品ID
time_stampDATETIME操作日時
カラム名説明
log_idINTログID(主キー)
oeprationVARCHAR操作内容
product_idINT商品ID
quantityINT在庫増減量
current_stockINT操作後の在庫数
time_stampDATETIME操作日時
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です。
履歴テーブルは…

よーしよし!

終わりに

今回は編集画面のデータから、
商品テーブルと履歴テーブルへ更新をかけました。

削除処理作りたかったけど、
ちょっとスタミナ切れ…。

これは簡単なんですけどね。

次回は削除処理と、
そろそろメイン画面へ商品情報出しましょうかね。

今回はここまで。

MFCで在庫管理システム作成 6
前回までの記事。前回から少しシステムを触って、商品数と履歴を増やしました。何回入庫した、出庫したかは重要ではありませんので、真似して作りたい方は適当に操作してみてください。商品削除処理編集画面についてはこれが最後です。削除処理。と言っても、...

コメント

タイトルとURLをコピーしました