MFCで物品購入承認システム作成 9

前回までの話。

MFCで物品購入承認システム作成 1

MFCで物品購入承認システム作成 2

MFCで物品購入承認システム作成 3

MFCで物品購入承認システム作成 4

MFCで物品購入承認システム作成 5

MFCで物品購入承認システム作成 6

MFCで物品購入承認システム作成 7

MFCで物品購入承認システム作成 8

管理画面追加

今回はグループ管理・項目管理画面とその処理を作成します。

やりかたは、以前行ったユーザ管理と同じなのでサクッとやりたい。

まず画面を用意。

今回、グループ管理と項目管理は共通の画面にします。
グループ管理の場合は必要なコントロールだけ表示させるようにします。

リソースビューからDialogを右クリックし、
リソースの追加を選択。
新規作成されたダイアログ名はIDD_KOUMOKUINFO_CHGにします。

今回追加するコントロールは、
・ラジオボタン3つ(作成・変更・削除)
・スタティックテキスト4つ
・エディットボックス3つ
・コンボボックス1つ
・OKボタンキャンセルボタン (デフォルトを使用)

です。
早速配置。

こんな感じに仕上がりました。

見た目はユーザ方法変更画面とほぼ同じです。
グループテーブル変更の場合は、IDと名称は活性化し、後は非活性にします。
コンボボックスはドロップダウンリスト&前回ミスった並び替えをFalseに。
後、IDと値段エディットボックスは数字をTrueに。

目の錯覚か、
スタティックテキストとエディットボックス(コンボボックス)の隙間が統一されてない気もするんですがね。
疲れてるのかな?

IDや配置はこんな感じに。

IDD_DIALOG_KOUMOKUINFO_CHG DIALOGEX 0, 0, 145, 209
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,20,185,50,14
    PUSHBUTTON      "キャンセル",IDCANCEL,80,185,50,14
    CONTROL         "作成",IDC_RADIO_K_INSERT,"Button",BS_AUTORADIOBUTTON,20,7,30,10
    CONTROL         "変更",IDC_RADIO_K_UPDATE,"Button",BS_AUTORADIOBUTTON,60,7,30,10
    CONTROL         "削除",IDC_RADIO_K_DELETE,"Button",BS_AUTORADIOBUTTON,100,7,30,10
    LTEXT           "ID",IDC_STATIC_KOUMOKUINFO_ID,20,30,80,8
    EDITTEXT        IDC_EDIT_KOUMOKUINFO_ID,20,45,100,14,ES_AUTOHSCROLL | ES_NUMBER
    LTEXT           "名称",IDC_STATIC_KOUMOKUINFO_NAME,20,65,70,8
    EDITTEXT        IDC_EDIT_KOUMOKUINFO_NAME,20,80,100,14,ES_AUTOHSCROLL
    LTEXT           "グループID",IDC_STATIC_KOUMOKUINFO_GROUPID,20,100,70,8
    COMBOBOX        IDC_COMBO_KOUMOKUINFO_GROUPID,20,115,100,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    LTEXT           "値段",IDC_STATIC_KOUMOKUINFO_PRICE,20,135,70,8
    EDITTEXT        IDC_EDIT_KOUMOKUINFO_PRICE,20,150,100,14,ES_AUTOHSCROLL | ES_NUMBER
END

最後に、コントロールどれでもいいのでダブルクリックして、
クラスとファイルを作成します。

OKを押すとクラスが作成されるので、
クラスビューからCKoumokuInfoChgを選択し、
プロパティのオーバーライドからOnInitDialogをAddしておきます。

グループ・項目情報変更処理作成

今回はいつもと少し違って、
呼び出し元のCKoumokuManagerクラスの処理変更から開始します。

理由は、
グループタブを選択された状態でボタンを押下したか
項目タブを選択された状態でボタンを押下したかを判定するenumを追加したいため

ついでに、残り追加したい処理も付け加えます。

KoumokuInfoChg.hを開きます。
enumを定義します。

typedef enum
{
	enGroup,	// グループタブ
	enKoumoku	// 項目タブ
}TabMode;

class CKoumokuInfoChg : public CDialogEx
{
    //略
};

今度はKoumokuManager.hを開きます。

今回はこのヘッダーにKoumokuInfoChg.hをインクルードします。
TabModeを使いたいからです。

#include "KoumokuInfoChg.h"

class CKoumokuManager : public CDialogEx
{
//略
private:
	TabMode m_enTab;			// タブの選択
	BOOL	m_bTableChgFlg;		// テーブル操作があったかの判定フラグ
public:
	BOOL bGetTableChgFlg();
};

タブ選択のメンバ変数を用意します。

更に、メイン画面に戻る際にグループマップと項目マップを更新するかの判定を行うため、
フラグを一つ用意します。
bGetTableChgFlgはメインのSupplyRequestManagerDlg.cppで使います。

KoumokuManager.cppを開きます。

KoumokuManager.hにインクルードを実施しているため
今回はcppファイルにインクルードは不要です。

メンバ変数を追加したのでコンストラクタ内で初期化。

CKoumokuManager::CKoumokuManager(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_KOUMOKU_MANAGE, pParent)
{
	m_enTab = enGroup;
	m_bTableChgFlg = FALSE;
}

メンバ変数m_enTabは、タブの状況に応じて変更させます。
ちょうどタブ変更時の関数を前回作ったのでそこに処理追加。

void CKoumokuManager::OnTcnSelchangeTabGroupKoumoku(NMHDR* pNMHDR, LRESULT* pResult)
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	*pResult = 0;

	int iTabIndex = m_ctrlTabGroupKoumoku.GetCurSel();	// タブ位置を取得
	CButton* pButton = (CButton*)GetDlgItem(IDC_BUTTON_KOUMOKUMG_CHG);

	if (iTabIndex == 0)
	{
		// グループタブが選択されている
		pButton->SetWindowText(_T("グループ管理"));
		m_enTab = enGroup;
	}
	else
	{
		// 項目タブが選択されている
		pButton->SetWindowText(_T("項目管理"));
		m_enTab = enKoumoku;
	}

	//リスト再表示
	ListUpdate();
}

13と19行目に追加しました。

次いで、bGetTableChgFlgでメンバ変数を返す処理を作成。

/**
 * @fn
 * @brief	テーブル変更フラグ
 * @param	なし
 * @return	TRUE	テーブルに変更を加えた
 *			FALSE	テーブルに変更を加えていない
 */
BOOL CKoumokuManager::bGetTableChgFlg()
{
	return m_bTableChgFlg;
}

項目情報変更画面を呼び出す処理&呼び出した後の処理を追加します。
ボタン押下時の処理内に記述。

void CKoumokuManager::OnBnClickedButtonKoumokumgChg()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CKoumokuInfoChg dlgKoumokuInfoChg;
	dlgKoumokuInfoChg.SetTabMode(m_enTab);	//タブモードを送る
	if (m_enTab == enKoumoku)
	{
		// 項目の場合、コンボボックスにグループをセットするためマップを送る
		dlgKoumokuInfoChg.SendGroupMap(m_MapGroup);
	}

	if (dlgKoumokuInfoChg.DoModal() == IDOK)
	{
		m_bTableChgFlg = TRUE;	// 変更フラグを立てる
	}
}

5行目でタブのモードを送っています。
6~10行目、変更ダイアログのコンボボックスにセットする用のマップを送ります。
当然、この二つはまだ関数は作っていないためエラーです。
後は、テーブル操作が完了したらフラグを立てます。

わざわざ2行で書かず、コンストラクタを作ればいいのでは、と自分でも思いますが、
こちらの方が人間(自分)の目で処理を負追いやすいので仕事でもこのように書いてます。

これでこのクラスの処理は終わりです。

今度はCKoumokuInfoChgクラスの処理です。
リソースビューから画面を開き、
OKボタンとラジオボタン3つをダブルクリックして関数を追加します。
KoumokuInfoChg.hに以下が自動で出来上がります。

public:
	virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedOk();
	afx_msg void OnBnClickedRadioKInsert();
	afx_msg void OnBnClickedRadioKUpdate();
	afx_msg void OnBnClickedRadioKDelete();

そのままヘッダーに、変数や関数を追加します。

private:
	TabMode	m_enTabMode;	// リスト画面から送られてきたタブのモード
	SQLMode m_enSQLMode;
	std::unordered_map<int, CString> m_MapGroup;
	std::unordered_map<CString, int, CStringHash, CStringEqual> m_MapGroupReverse;		//グループマップ逆引き用

	CString m_strGroupKoumokuID;
	CString m_strGroupKoumokuName;
	CString m_strGroupID;
	CString m_strKoumokuPrice;

	CEdit	m_ctrlEditID;					// IDエディットボックス
	CEdit	m_ctrlEditName;					// 名称エディットボックス
	CComboBox m_ctrlComboGroupID;			// グループIDコンボボックス
	CEdit	m_ctrlEditKoumokuPrice;			// 項目値段エディットボックス

	CStatic m_ctrlStaticID;				// IDスタティックテキスト
	CStatic m_ctrlStaticName;			// 名称スタティックテキスト
	CStatic m_ctrlStaticGroupID;		// グループIDスタティックテキスト
	CStatic m_ctrlStaticKoumokuPrice;	// 項目値段スタティックテキスト

private:
	void RadioConditionChange();				// ラジオボタン変更時
	BOOL bValidCheck();							// 入力チェック
public:
	void SetTabMode(TabMode enTabMode);
	void SendGroupMap(std::unordered_map<int, CString> MapGroup);

定義していることはユーザ情報変更の時とほぼ同じです。
項目情報変更時はグループマップが必要なので、そこは追加しています。

KoumokuInfoChg.cppで実際に処理を書きます。

今回はヘッダーに既にMySQLManager.hをインクルードしているので、
その文は必要なし。

コンストラクタで初期化を行います。
また、DoDataExchangeで紐づけを。

CKoumokuInfoChg::CKoumokuInfoChg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_KOUMOKUINFO_CHG, pParent)
{
	m_enTabMode = enGroup;
	m_enSQLMode = enInsert;
	m_strGroupKoumokuID = _T("");
	m_strGroupKoumokuName = _T("");
	m_strGroupID = _T("");
	m_strKoumokuPrice = _T("");
}

void CKoumokuInfoChg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);

	DDX_Text(pDX, IDC_EDIT_KOUMOKUINFO_ID, m_strGroupKoumokuID);
	DDX_Text(pDX, IDC_EDIT_KOUMOKUINFO_NAME, m_strGroupKoumokuName);
	DDX_Text(pDX, IDC_EDIT_KOUMOKUINFO_PRICE, m_strKoumokuPrice);

	DDX_Control(pDX, IDC_EDIT_KOUMOKUINFO_ID, m_ctrlEditID);
	DDX_Control(pDX, IDC_EDIT_KOUMOKUINFO_NAME, m_ctrlEditName);
	DDX_Control(pDX, IDC_COMBO_KOUMOKUINFO_GROUPID, m_ctrlComboGroupID);
	DDX_Control(pDX, IDC_EDIT_KOUMOKUINFO_PRICE, m_ctrlEditKoumokuPrice);

	DDX_Control(pDX, IDC_STATIC_KOUMOKUINFO_ID, m_ctrlStaticID);
	DDX_Control(pDX, IDC_STATIC_KOUMOKUINFO_NAME, m_ctrlStaticName);
	DDX_Control(pDX, IDC_STATIC_KOUMOKUINFO_GROUPID, m_ctrlStaticGroupID);
	DDX_Control(pDX, IDC_STATIC_KOUMOKUINFO_PRICE, m_ctrlStaticKoumokuPrice);
}

セット関数を2つ書きます。

/**
 * @fn
 * @brief	タブモード受け渡し
 * @param	TabMode		enGroup		「グループ」タブ
 *						enKoumoku	「項目」タブ
 * @return	なし
 */
void CKoumokuInfoChg::SetTabMode(TabMode enTabMode)
{
	m_enTabMode = enTabMode;
}

/**
 * @fn
 * @brief	グループマップ受け渡し
 * @param	std::unordered_map<int, CString>	グループマップ
 * @return	なし
 */
void CKoumokuInfoChg::SendGroupMap(std::unordered_map<int, CString> MapGroup)
{
	m_MapGroup = MapGroup;
}

これでメンバ変数2つにデータが入りました。

続いてOnInitDialog。

BOOL CKoumokuInfoChg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO: ここに初期化を追加してください
	if (m_enTabMode == enGroup)
	{
		// グループテーブル変更用
		m_ctrlStaticGroupID.ShowWindow(SW_HIDE);
		m_ctrlComboGroupID.ShowWindow(SW_HIDE);
		m_ctrlStaticKoumokuPrice.ShowWindow(SW_HIDE);
		m_ctrlEditKoumokuPrice.ShowWindow(SW_HIDE);
		SetWindowText(_T("グループ情報変更"));
	}
	else
	{
		// 項目テーブル変更用
		m_MapGroupReverse.clear();	//空なのはわかっているが、念のため。
		BOOL bInitCheckTemp = TRUE;
		for (const auto& pair : m_MapGroup)
		{
			if (bInitCheckTemp == TRUE)
			{
				bInitCheckTemp = FALSE;
			}
			CString strTemp = _T("");
			strTemp.Format(_T("%d.  %s"), pair.first, pair.second);
			m_MapGroup[pair.first] = strTemp;
			m_ctrlComboGroupID.AddString(pair.second);			//コンボボックスに追加
			m_MapGroupReverse[pair.second] = pair.first;	//逆引き追加
		}
		SetWindowText(_T("項目情報変更"));
	}

	CButton* pButton = (CButton*)GetDlgItem(IDC_RADIO_K_INSERT);
	pButton->SetCheck(1);

	RadioConditionChange();

	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

9~13行目は、タブがグループだった場合の処理です。
グループテーブルではIDと名称だけしか列がないので、
必要ないGroupIDコンボボックスや値段は非表示にします。

項目タブの場合は、コンボボックスにセットする用の処理を書いています。
Request.cppで書いたのとほぼ同じです。
ただ、中身は「1. Stationery」のように表示させます。

次に、ラジオボタン変更時の処理3つ。
こちらもユーザ情報変更と同じ。

/**
 * @fn
 * @brief	「作成」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CKoumokuInfoChg::OnBnClickedRadioKInsert()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	m_enSQLMode = enInsert;
	RadioConditionChange();
}

/**
 * @fn
 * @brief	「変更」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CKoumokuInfoChg::OnBnClickedRadioKUpdate()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	m_enSQLMode = enUpdate;
	RadioConditionChange();
}

/**
 * @fn
 * @brief	「削除」ラジオボタン押下時に呼ばれる
 * @param	なし
 * @return	なし
 */
void CKoumokuInfoChg::OnBnClickedRadioKDelete()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	m_enSQLMode = enDelete;
	RadioConditionChange();
}

そして、そのラジオボタンが押されたときに表示をどうするかの処理。
こちらもほぼ同じ処理。

/**
 * @fn
 * @brief	ラジオボタン変更処理
 * @param	なし
 * @return	なし
 */
void CKoumokuInfoChg::RadioConditionChange()
{
	m_strGroupKoumokuID = _T("");
	m_strGroupKoumokuName = _T("");

	if (m_enTabMode == enGroup)
	{
		// グループテーブル変更の場合
		if (m_enSQLMode == enInsert)
		{
			m_ctrlStaticID.SetWindowText(_T("新規グループID (自動採番)"));
			m_ctrlStaticName.SetWindowText(_T("新規グループ名称"));
			
			m_ctrlEditID.EnableWindow(FALSE);
			m_ctrlEditName.EnableWindow(TRUE);

		}
		else if (m_enSQLMode == enUpdate)
		{
			m_ctrlStaticID.SetWindowText(_T("変更グループID "));
			m_ctrlStaticName.SetWindowText(_T("変更グループ名称"));

			m_ctrlEditID.EnableWindow(TRUE);
			m_ctrlEditName.EnableWindow(TRUE);
		}
		else if(m_enSQLMode==enDelete)
		{
			m_ctrlStaticID.SetWindowText(_T("削除グループID "));
			m_ctrlStaticName.SetWindowText(_T(""));

			m_ctrlEditID.EnableWindow(TRUE);
			m_ctrlEditName.EnableWindow(FALSE);
		}
	}
	else if (m_enTabMode == enKoumoku)
	{
		// 項目テーブル変更の場合
		m_ctrlComboGroupID.SetCurSel(0);
		m_strKoumokuPrice = _T("");

		if (m_enSQLMode == enInsert)
		{
			m_ctrlStaticID.SetWindowText(_T("新規項目ID (自動採番)"));
			m_ctrlStaticName.SetWindowText(_T("新規項目名称"));
			m_ctrlStaticGroupID.SetWindowText(_T("新規項目グループID"));
			m_ctrlStaticKoumokuPrice.SetWindowTextW(_T("新規項目値段"));

			m_ctrlEditID.EnableWindow(FALSE);
			m_ctrlEditName.EnableWindow(TRUE);
			m_ctrlComboGroupID.EnableWindow(TRUE);
			m_ctrlEditKoumokuPrice.EnableWindow(TRUE);

		}
		else if (m_enSQLMode == enUpdate)
		{
			m_ctrlStaticID.SetWindowText(_T("変更項目ID "));
			m_ctrlStaticName.SetWindowText(_T("変更項目名称"));
			m_ctrlStaticGroupID.SetWindowText(_T("変更項目グループID"));
			m_ctrlStaticKoumokuPrice.SetWindowTextW(_T("変更項目値段"));

			m_ctrlEditID.EnableWindow(TRUE);
			m_ctrlEditName.EnableWindow(TRUE);
			m_ctrlComboGroupID.EnableWindow(TRUE);
			m_ctrlEditKoumokuPrice.EnableWindow(TRUE);
		}
		else if (m_enSQLMode == enDelete)
		{
			m_ctrlStaticID.SetWindowText(_T("削除項目ID "));
			m_ctrlStaticName.SetWindowText(_T(""));
			m_ctrlStaticGroupID.SetWindowText(_T(""));
			m_ctrlStaticKoumokuPrice.SetWindowTextW(_T(""));

			m_ctrlEditID.EnableWindow(TRUE);
			m_ctrlEditName.EnableWindow(FALSE);
			m_ctrlComboGroupID.EnableWindow(FALSE);
			m_ctrlEditKoumokuPrice.EnableWindow(FALSE);
		}
	}

	UpdateData(FALSE);
}

入力チェックも同様です。
ただし、ユーザ情報変更の場合はIDが5桁かのチェックがありましたが、
今回は必要ないです。

そしてもう一つ重要なことが。

グループのデータを削除する場合、
それに紐づく項目情報も削除する必要があります。

とりあえず、メッセージで対応。
少々長いですが、大したことはしていません。

/**
 * @fn
 * @brief	入力チェック
 * @param	なし
 * @return	TRUE	問題なし
 *			FALSE	問題あり
 */
BOOL CKoumokuInfoChg::bValidCheck()
{
	if (m_enTabMode == enGroup)
	{
		// グループテーブル変更
		if (m_enSQLMode == enInsert)
		{
			// 作成で名称が空白の時
			if (m_strGroupKoumokuName == _T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
		else if (m_enSQLMode == enUpdate)
		{
			// 変更でIDか名称が空白の時
			if (m_strGroupKoumokuID == _T("") || m_strGroupKoumokuName == _T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
		else if (m_enSQLMode == enDelete)
		{
			// 削除でIDが空白の時
			if (m_strGroupKoumokuID == _T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
	}
	else if (m_enTabMode == enKoumoku)
	{
		// 項目テーブル変更時
		if (m_enSQLMode == enInsert)
		{
			// 作成で名称か値段が空白の時
			if (m_strGroupKoumokuName == _T("") || m_strKoumokuPrice ==_T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
		else if (m_enSQLMode == enUpdate)
		{
			// 変更でIDか名称か値段が空白の時
			if (m_strGroupKoumokuID == _T("") || m_strGroupKoumokuName == _T("") || m_strKoumokuPrice == _T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
		else if (m_enSQLMode == enDelete)
		{
			// 削除でIDが空白の時
			if (m_strGroupKoumokuID == _T(""))
			{
				AfxMessageBox(_T("未入力項目があります。"), MB_OK | MB_ICONEXCLAMATION);
				return FALSE;
			}
		}
	}

	CString strMes = _T("");
	if (m_enTabMode == enGroup)
	{
		if (m_enSQLMode == enInsert)
		{
			strMes = _T("以下のグループを登録します。よろしいですか?");
			strMes += _T("\r\nグループID:自動採番 (登録後、表を確認してください。)");
			strMes += _T("\r\nグループ名称:");
			strMes += m_strGroupKoumokuName;
		}
		else if (m_enSQLMode == enUpdate)
		{
			strMes = _T("以下のグループを変更します。よろしいですか?");
			strMes += _T("\r\nグループID:");
			strMes += m_strGroupKoumokuID;
			strMes += _T("\r\nグループ名称:");
			strMes += m_strGroupKoumokuName;
		}
		else if (m_enSQLMode == enDelete)
		{
			strMes = _T("以下のグループを削除します。よろしいですか?");
			strMes += _T("\r\nグループID:");
			strMes += m_strGroupKoumokuID;
		}
	}
	else if (m_enTabMode == enKoumoku)
	{
		int iGroupIDIndex = m_ctrlComboGroupID.GetCurSel();
		CString strTemp;
		m_ctrlComboGroupID.GetLBText(iGroupIDIndex, strTemp);
		if (m_enSQLMode == enInsert)
		{
			strMes = _T("以下の項目を登録します。よろしいですか?");
			strMes += _T("\r\nグループID:自動採番 (登録後、表を確認してください。)");
			strMes += _T("\r\n項目名称:");
			strMes += m_strGroupKoumokuName;
			strMes += _T("\r\n上位グループIDと名称:");
			strMes += strTemp;
			strMes += _T("\r\n項目値段:");
			strMes += m_strKoumokuPrice;
		}
		else if (m_enSQLMode == enUpdate)
		{
			strMes = _T("以下の項目を変更します。よろしいですか?");
			strMes += _T("\r\n項目ID:");
			strMes += m_strGroupKoumokuID;
			strMes += _T("\r\n項目名称:");
			strMes += m_strGroupKoumokuName;
			strMes += _T("\r\n上位グループIDと名称:");
			strMes += strTemp;
			strMes += _T("\r\n項目値段:");
			strMes += m_strKoumokuPrice;
		}
		else if (m_enSQLMode == enDelete)
		{
			strMes = _T("以下の項目を削除します。よろしいですか?");
			strMes += _T("\r\n項目ID:");
			strMes += m_strGroupKoumokuID;
		}
	}

	//確認メッセージ
	if (AfxMessageBox(strMes, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
	{
		return FALSE;
	}

	if (m_enTabMode == enGroup && m_enSQLMode == enDelete)
	{
		strMes = _T("グループを削除すると、紐づく項目もすべて削除されます。\r\nよろしいですか?");
		if (AfxMessageBox(strMes, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
		{
			return FALSE;
		}
	}

	return TRUE;
}

最後に、OKボタンが押されたときの処理。

void CKoumokuInfoChg::OnBnClickedOk()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	UpdateData(TRUE);
	if (bValidCheck() == TRUE)
	{
		CMySQLManager GroupKoumokuInfo;
		GroupKoumokuInfo.GetConnection();
		if (m_enTabMode == enGroup)
		{
			if (GroupKoumokuInfo.bGroupTableChange(m_enSQLMode, m_strGroupKoumokuID, m_strGroupKoumokuName) == TRUE)
			{
				AfxMessageBox(_T("処理が完了しました。"));
				CDialog::OnOK();
			}
		}
		else if (m_enTabMode == enKoumoku)
		{
			int iGroupIDIndex = m_ctrlComboGroupID.GetCurSel();
			CString strTemp;
			m_ctrlComboGroupID.GetLBText(iGroupIDIndex, strTemp);
			CString strGroupID = _T("");
			strGroupID.Format(_T("%d"), m_MapGroupReverse[strTemp]);
			if (GroupKoumokuInfo.bKoumokuTableChange(m_enSQLMode, m_strGroupKoumokuID, m_strGroupKoumokuName, strGroupID, m_strKoumokuPrice) == TRUE)
			{
				AfxMessageBox(_T("処理が完了しました。"));
				CDialog::OnOK();
			}
		}
	}
}

今回、タブのモードによって呼応するSQLの関数を別々にしています。(11と24行目)
共通にして、中で分岐させればいいかもしれませんが、
流石にテーブルが違うと関数を分けた方がいいかなと。
例のごとく、こちらもまだ関数未定義なのでエラーは現段階では出ます。

SQL作成

では、そのSQL処理。
MySQLManager.hで関数宣言。

// グループ情報変更
BOOL bGroupTableChange(SQLMode enMode, CString strGroupID, CString strGroupName);
// 項目情報変更
BOOL bKoumokuTableChange(SQLMode enMode, CString strKoumokuID, CString strKoumokuName, CString strGroupID, CString strKoumokuPrice);

MySQLManager.cppで処理を書きます。
まずはグループテーブルの操作の方。

/**
 * @fn
 * @brief	グループテーブル変更処理
 * @param	SQLMode enMode      enInsert    作成
 *                              enUpdate    変更
 *                              enDelete    削除
 *          CString strGroupID              グループID
 *          CStinrg strGroupName            グループ名
 * @return	TRUE    操作成功
 *          FALSE   操作失敗
 */
BOOL CMySQLManager::bGroupTableChange(SQLMode enMode, CString strGroupID, CString strGroupName)
{
    try
    {
        m_con->setSchema(SCHEMA);   //スキーマ選択
        sql::SQLString strslqlGroupID = strConverter(strGroupID);    //ID型変換
        sql::SQLString strSqlQuery = "";

        if (enMode == enUpdate || enMode == enDelete)
        {
            strSqlQuery = "SELECT * FROM " + TABLE_GROUP + " WHERE Id = " + strslqlGroupID;

            // クエリを実行
            std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
            std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());

            if (res->next())
            {
                //IDがグループテーブルに存在したのでOK
            }
            else
            {
                //IDがグループテーブルに存在しない
                if (enMode == enUpdate)
                {
                    // 変更時はエラー
                    AfxMessageBox(_T("IDが存在しないため更新処理が行えません。"), MB_OK | MB_ICONEXCLAMATION);
                    return FALSE;
                }
                else if (enMode == enDelete)
                {
                    // 削除時はエラー
                    AfxMessageBox(_T("IDが存在しないため削除処理が行えません。"), MB_OK | MB_ICONEXCLAMATION);
                    return FALSE;
                }
            }
        }

        sql::SQLString strsqlGroupName = strConverter(strGroupName);

        if (enMode == enInsert)
        {
            // 作成
            strSqlQuery = "INSERT INTO " + TABLE_GROUP + " (Name) VALUES ('" + strsqlGroupName + "')";
        }
        else if (enMode == enUpdate)
        {
            // 変更
            strSqlQuery = "UPDATE " + TABLE_GROUP + " SET Name = '" + strsqlGroupName + "' WHERE Id = " + strslqlGroupID;
        }
        else if (enMode == enDelete)
        {
            strSqlQuery = "DELETE FROM " + TABLE_GROUP + " WHERE Id = " + strslqlGroupID;
        }

        //実行
        std::unique_ptr<sql::PreparedStatement> pstmt2(m_con->prepareStatement(strSqlQuery));
        pstmt2->executeUpdate();        
    }
    catch (sql::SQLException& e)
    {
        // 実行できなかった
        ErrSQLException(e);
        return FALSE;
    }

    return TRUE;
}

ユーザ情報変更の場合、
「新規の場合IDが既に登録されていたらFALSE」
の処理を入れていましたが、
今回はIDはオートインクリメントで必要ないです。
そもそもエディットボックスが操作できない制御をかけています。
後は、ユーザ情報変更時と同じです。

続いて項目テーブルの方。
こちらもグループテーブルと同じ。
操作する列数が増えただけです。

 /**
  * @fn
  * @brief	項目テーブル変更処理
  * @param	SQLMode enMode      enInsert    作成
  *                              enUpdate    変更
  *                              enDelete    削除
  *          CString strKoumokuID            項目ID
  *          CStinrg strKoumokuName          項目名
  *          CString strGroupID              上位グループID
  *          CString strKoumokuPrice         項目値段
  * @return	 TRUE    操作成功
  *          FALSE   操作失敗
  */
BOOL CMySQLManager::bKoumokuTableChange(SQLMode enMode, CString strKoumokuID, CString strKoumokuName, CString strGroupID, CString strKoumokuPrice)
{
    try
    {
        m_con->setSchema(SCHEMA);   //スキーマ選択
        sql::SQLString strslqlKoumokuID = strConverter(strKoumokuID);    //ID型変換
        sql::SQLString strSqlQuery = "";

        if (enMode == enUpdate || enMode == enDelete)
        {
            strSqlQuery = "SELECT * FROM " + TABLE_KOUMOKU + " WHERE Id = " + strslqlKoumokuID;;

            // クエリを実行
            std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement(strSqlQuery));
            std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());

            if (res->next())
            {
                //IDがグループテーブルに存在したのでOK
            }
            else
            {
                //IDがグループテーブルに存在しない
                if (enMode == enUpdate)
                {
                    // 変更時はエラー
                    AfxMessageBox(_T("IDが存在しないため更新処理が行えません。"), MB_OK | MB_ICONEXCLAMATION);
                    return FALSE;
                }
                else if (enMode == enDelete)
                {
                    // 削除時はエラー
                    AfxMessageBox(_T("IDが存在しないため削除処理が行えません。"), MB_OK | MB_ICONEXCLAMATION);
                    return FALSE;
                }
            }
        }

        sql::SQLString strsqlKoumokuName = strConverter(strKoumokuName);
        sql::SQLString strsqlGroupID = strConverter(strGroupID);
        sql::SQLString strsqlKoumokuPrice = strConverter(strKoumokuPrice);

        if (enMode == enInsert)
        {
            // 作成
            strSqlQuery = "INSERT INTO " + TABLE_KOUMOKU +  " (Name, GroupID, Price) VALUES ('" + strsqlKoumokuName + "', " + strsqlGroupID + ", '" + strsqlKoumokuPrice + "')";
        }
        else if (enMode == enUpdate)
        {
            // 変更
            strSqlQuery = "UPDATE " + TABLE_KOUMOKU + " SET Name = '" + strsqlKoumokuName + "', GroupID = " + strsqlGroupID + ", Price = '" + strsqlKoumokuPrice + "' WHERE Id = " + strslqlKoumokuID;
        }
        else if (enMode == enDelete)
        {
            strSqlQuery = "DELETE FROM " + TABLE_KOUMOKU + " WHERE Id = " + strslqlKoumokuID;
        }

        //実行
        std::unique_ptr<sql::PreparedStatement> pstmt2(m_con->prepareStatement(strSqlQuery));
        pstmt2->executeUpdate();
    }
    catch (sql::SQLException& e)
    {
        // 実行できなかった
        ErrSQLException(e);
        return FALSE;
    }

    return TRUE;
}

今回仕様で1つのグループにつき項目は5つまでという制約を付けました。
すぐ作れはしますが、今回は管理者が6つ以上作成しないと信じて処理は省きます(´▽`*)
(訳:長すぎて少し疲れた…)

ここまでで、テーブルの操作は終了です。
まだ追加処理はありますが、長いのでいったんビルド。

動作確認1

項目情報管理画面から、「グループ」タブを選択した状態で
グループ管理ボタンを押します。

エディットボックスの制御は良さそうですね。
OKボタンのキャプションを変更させるのを忘れているので後で対応。
作成と変更を試します。
削除は、まだテーブルに外部キー設定していないので一度保留です。

新規グループ名称をTest1にして作成。

現状はまだ管理画面のリストコントロール内を更新させる処理を作っていないので、
ツールで見てみます。

ちゃんと新規作成されていました。

更新を行うと

反映されています。

項目情報変更でも同じく作成・変更・削除の処理は確認済みです。

続・変更画面処理作成

後何やるかですが、
テーブル操作を行った際に項目管理画面のリストコントロールを更新、
更に閉じた後のメイン画面のマップ2種も更新させます。

まずは管理画面の処理を記載しているKoumokuManager.cppで処理を追加。
項目情報変更画面からOKボタンを押されるとダイアログが閉じますが、
その際に変更画面からマップを取得します。

void CKoumokuManager::OnBnClickedButtonKoumokumgChg()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CKoumokuInfoChg dlgKoumokuInfoChg;
	dlgKoumokuInfoChg.SetTabMode(m_enTab);	//タブモードを送る
	if (m_enTab == enKoumoku)
	{
		// 項目の場合、コンボボックスにグループをセットするためマップを送る
		dlgKoumokuInfoChg.SendGroupMap(m_MapGroup);
	}

	if (dlgKoumokuInfoChg.DoModal() == IDOK)
	{
		m_bTableChgFlg = TRUE;	// 変更フラグを立てる

		CMySQLManager GroupKoumokuUpdate;
		GroupKoumokuUpdate.GetConnection();

		GroupKoumokuUpdate.SetGroupMap(&m_MapGroup);		// マップを更新
		GroupKoumokuUpdate.SetKoumokuMap(&m_MapKoumoku);	// マップを更新

		ListUpdate();	//リスト再表示
	}
}

16~22行目を追加です。

因みに変更を加えると14行目でフラグが立ちますので、
今度はメイン画面にて、グループテーブルおよび項目テーブルに変更があったかの判定を行います。

SupplyRequestManagerDlg.cppを開きます。
管理画面を呼び出している関数に処理を追加します。

void CSupplyRequestManagerDlg::OnBnClickedButtonMainItemlist()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CKoumokuManager dlgKoumokuManage;
	dlgKoumokuManage.GetGroupMap(m_MapGroup);		// グループマップを渡す
	dlgKoumokuManage.GetKoumokuMap(m_MapKoumoku);	// 項目マップを渡す
	dlgKoumokuManage.DoModal();
	if (dlgKoumokuManage.bGetTableChgFlg() == TRUE)
	{
		// テーブルに変更を加えた
		// マップを更新
		m_MapGroup = dlgKoumokuManage.SetGroupMap();
		m_MapKoumoku = dlgKoumokuManage.SetKoumokuMap();
	}
}

8行目以降を追加しました。

最後に、変更画面のキャプションを変更する処理ですが、
ユーザ情報変更ではすでにしているのでそれをそのままコピペすれば大丈夫です。
SetDlgItemText(IDOK, _T(“〇〇”));
ですね。処理記載は省略します。

これで一度ビルドしてみます。

動作確認2

項目情報変更画面にて、グルプを一つ作成します。

これで作成を押し、処理が完了すると画面が閉じて…

ちゃんとリストコントロールに表示されました。

更に戻るボタンを押しメイン画面にバック。
申請ボタンをおして申請ダイアログを表示すると…

こちらもちゃんと変更が反映されていました。

外部キー設定

最後に、順番逆な気はしますが、外部キーの設定をします。

MySQL Workbenchを開いて設定。

koumokuテーブルからForeign Keyを選択。
Foreign Nameは適当に、Referenced Tableはgroupテーブルを選択。
koumokuテーブルのGroupIDとgroupテーブルのIdを紐づけます。

で、On DeleteをCASCADEに。
…多分これでいいはずだけど。

いいと思うんだけど(;’∀’)

Applyを押し、A5M2でER図をリバース生成すると

大丈夫だよね?
モノは試し!!

さっきグループIDが5のTest2を作ったので、項目をいくつか作ってみます。

IDが15~17の項目が作られました。
この状態でグループタブから管理ボタンを押し、
削除を実行します。

あら、残っちゃいました…。

でもテーブルは

どちらもグループIDがこの行は削除されてました。

修正

なんてことはない、マップを初期化すればいいだけでした。

リストコントロールを表示しているKoumokuManager.cppで修正。

void CKoumokuManager::OnBnClickedButtonKoumokumgChg()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CKoumokuInfoChg dlgKoumokuInfoChg;
	dlgKoumokuInfoChg.SetTabMode(m_enTab);	//タブモードを送る
	if (m_enTab == enKoumoku)
	{
		// 項目の場合、コンボボックスにグループをセットするためマップを送る
		dlgKoumokuInfoChg.SendGroupMap(m_MapGroup);
	}

	if (dlgKoumokuInfoChg.DoModal() == IDOK)
	{
		m_bTableChgFlg = TRUE;	// 変更フラグを立てる

		CMySQLManager GroupKoumokuUpdate;
		GroupKoumokuUpdate.GetConnection();

		m_MapGroup.clear();		// 一度初期化
		m_MapKoumoku.clear();	// 一度初期化
		GroupKoumokuUpdate.SetGroupMap(&m_MapGroup);		// マップを更新
		GroupKoumokuUpdate.SetKoumokuMap(&m_MapKoumoku);	// マップを更新

		ListUpdate();	//リスト再表示
	}
}

clearで初期化します。
これで、リスト内も削除で残らなくなりました。

終わりに

今回はグループテーブルと項目テーブルの操作でした。

長いな(;´・ω・)
1回でやる分量じゃなかった気がする。

やっぱりマップ使ったら表示が崩れないな。
全部終わったらユーザ管理画面でもやってみるか。

次は申請画面の続きを…。

今回はここまで。

MFCで物品購入承認システム作成 10
前回までの話。MFCで物品購入承認システム作成 1MFCで物品購入承認システム作成 2MFCで物品購入承認システム作成 3MFCで物品購入承認システム作成 4MFCで物品購入承認システム作成 5MFCで物品購入承認システム作成 6MFCで物...

コメント

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