diff --git a/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/create_dcf_inst_merge.py b/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/create_dcf_inst_merge.py index 294fe004..1fae0013 100644 --- a/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/create_dcf_inst_merge.py +++ b/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/create_dcf_inst_merge.py @@ -20,8 +20,8 @@ def exec(): db = Database.get_instance() try: db.connect() - db.to_jst() db.begin() + db.to_jst() logger.debug('DCF施設統合マスタ作成処理開始') # COM施設からDCF施設統合マスタに登録 (is_add_dcf_inst_merge, duplication_inst_records) = _insert_dcf_inst_merge_from_com_inst(db) diff --git a/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/integrate_dcf_inst_merge.py b/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/integrate_dcf_inst_merge.py index 85de15ed..eeb2ebd2 100644 --- a/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/integrate_dcf_inst_merge.py +++ b/ecs/jskult-batch-daily/src/batch/dcf_inst_merge/integrate_dcf_inst_merge.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta + from src.batch.batch_functions import logging_sql from src.batch.common.batch_context import BatchContext from src.db.database import Database @@ -14,8 +15,8 @@ def exec(): db = Database.get_instance() try: db.connect() - db.to_jst() db.begin() + db.to_jst() logger.debug('DCF施設統合マスタ日次更新処理開始') # DCF施設統合マスタ移行先コードのセット(無効フラグが『0(有効)』) enabled_dst_inst_merge_records = _set_enabled_dct_inst_merge(db) @@ -120,12 +121,13 @@ def _add_ult_ident_presc(db: Database, enabled_dst_inst_merge_records: list[dict logger.info('納入先処方元マスタの登録 終了') -def _select_emp_chg_inst_ta_cd(db: Database, dcf_inst_cd: str) -> list[dict]: - # 従業員担当施設マスタから、DCF施設コードに対応した領域コードの取得 +def _select_primary_key_from_emp_chg_inst(db: Database, dcf_inst_cd: str) -> list[dict]: + # 従業員担当施設マスタから、DCF施設コードに対応した領域コードと担当者種別コードの取得 try: sql = """ SELECT - ta_cd + ta_cd, + emp_chg_type_cd FROM src05.emp_chg_inst WHERE @@ -134,14 +136,14 @@ def _select_emp_chg_inst_ta_cd(db: Database, dcf_inst_cd: str) -> list[dict]: AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < end_date """ params = {'dcf_inst_cd': dcf_inst_cd} - emp_chg_inst_ta_cd_records = db.execute_select(sql, params) + emp_chg_inst_primary_key_records = db.execute_select(sql, params) logging_sql(logger, sql) - logger.info('従業員担当施設マスタから領域コードの取得に成功') + logger.info('従業員担当施設マスタから領域コード、担当者種別コードの取得に成功') except Exception as e: - logger.debug('従業員担当施設マスタから領域コードの取得に失敗') + logger.debug('従業員担当施設マスタから領域コード、担当者種別コードの取得に失敗') raise e - return emp_chg_inst_ta_cd_records + return emp_chg_inst_primary_key_records def _add_emp_chg_inst(db: Database, enabled_dst_inst_merge_records: list[dict]): @@ -149,10 +151,10 @@ def _add_emp_chg_inst(db: Database, enabled_dst_inst_merge_records: list[dict]): logger.info('従業員担当施設マスタの登録 開始') for enabled_merge_record in enabled_dst_inst_merge_records: tekiyo_month_first_day = _get_first_day_of_month(enabled_merge_record['tekiyo_month']) - emp_chg_inst_ta_cd_records = _select_emp_chg_inst_ta_cd(db, enabled_merge_record['dcf_inst_cd']) - for emp_chg_inst_ta_cd_record in emp_chg_inst_ta_cd_records: + emp_chg_inst_primary_key_records = _select_primary_key_from_emp_chg_inst(db, enabled_merge_record['dcf_inst_cd']) + for emp_chg_inst_primary_key_record in emp_chg_inst_primary_key_records: emp_chg_inst_records = _select_emp_chg_inst(db, enabled_merge_record['dcf_inst_cd'], enabled_merge_record['dup_opp_cd'], - emp_chg_inst_ta_cd_record['ta_cd']) + emp_chg_inst_primary_key_record['ta_cd'], emp_chg_inst_primary_key_record['emp_chg_type_cd']) for emp_chg_inst_row in emp_chg_inst_records: # 重複時相手先コードが存在したかのチェック if emp_chg_inst_row['opp_count'] > 0: @@ -173,7 +175,7 @@ def _add_emp_chg_inst(db: Database, enabled_dst_inst_merge_records: list[dict]): emp_chg_inst_row) continue # 適用開始日 ≧ DCF施設統合マスタの適用月度の1日の場合、N(論理削除レコード)に設定する - _update_emp_chg_inst_disabled(db, enabled_merge_record['dcf_inst_cd'], emp_chg_inst_row['ta_cd'], + _update_emp_chg_inst_disabled(db, enabled_merge_record['dcf_inst_cd'], emp_chg_inst_row['ta_cd'], emp_chg_inst_row['emp_chg_type_cd'], emp_chg_inst_row['start_date']) logger.info('従業員担当施設マスタの登録 終了') @@ -207,7 +209,7 @@ def _delete_ult_ident_presc(db: Database, start_date: str, ult_ident_presc_row: raise e -def _update_emp_chg_inst_disabled(db: Database, dcf_inst_cd: str, ta_cd: str, start_date: str): +def _update_emp_chg_inst_disabled(db: Database, dcf_inst_cd: str, ta_cd: str, emp_chg_type_cd: str, start_date: str): # emp_chg_instをUPDATE try: elapsed_time = ElapsedTime() @@ -221,9 +223,10 @@ def _update_emp_chg_inst_disabled(db: Database, dcf_inst_cd: str, ta_cd: str, st WHERE inst_cd = :dcf_inst_cd AND ta_cd = :ta_cd + AND emp_chg_type_cd = :emp_chg_type_cd AND start_date = :start_date """ - params = {'dcf_inst_cd': dcf_inst_cd, 'ta_cd': ta_cd, 'start_date': start_date} + params = {'dcf_inst_cd': dcf_inst_cd, 'ta_cd': ta_cd, 'emp_chg_type_cd': emp_chg_type_cd, 'start_date': start_date} res = db.execute(sql, params) logging_sql(logger, sql) logger.info(f'従業員担当施設マスタのYorNフラグ更新に成功, {res.rowcount} 行更新 ({elapsed_time.of})') @@ -246,6 +249,7 @@ def _update_emp_chg_inst_end_date(db: Database, dcf_inst_cd: str, last_end_date: WHERE inst_cd = :dcf_inst_cd AND ta_cd = :ta_cd + AND emp_chg_type_cd = :emp_chg_type_cd AND emp_cd = :emp_cd AND bu_cd = :bu_cd AND start_date = :start_date @@ -254,6 +258,7 @@ def _update_emp_chg_inst_end_date(db: Database, dcf_inst_cd: str, last_end_date: 'end_date': last_end_date, 'dcf_inst_cd': dcf_inst_cd, 'ta_cd': emp_chg_inst_row['ta_cd'], + 'emp_chg_type_cd': emp_chg_inst_row['emp_chg_type_cd'], 'emp_cd': emp_chg_inst_row['emp_cd'], 'bu_cd': emp_chg_inst_row['bu_cd'], 'start_date': emp_chg_inst_row['start_date'] @@ -276,6 +281,7 @@ def _insert_emp_chg_inst(db: Database, dup_opp_cd: str, set_start_date: str, src05.emp_chg_inst( inst_cd, ta_cd, + emp_chg_type_cd, emp_cd, bu_cd, start_date, @@ -290,6 +296,7 @@ def _insert_emp_chg_inst(db: Database, dup_opp_cd: str, set_start_date: str, VALUES( :dup_opp_cd, :ta_cd, + :emp_chg_type_cd, :emp_cd, :bu_cd, :start_date, @@ -305,6 +312,7 @@ def _insert_emp_chg_inst(db: Database, dup_opp_cd: str, set_start_date: str, params = { 'dup_opp_cd': dup_opp_cd, 'ta_cd': emp_chg_inst_row['ta_cd'], + 'emp_chg_type_cd': emp_chg_inst_row['emp_chg_type_cd'], 'emp_cd': emp_chg_inst_row['emp_cd'], 'bu_cd': emp_chg_inst_row['bu_cd'], 'start_date': set_start_date, @@ -518,13 +526,14 @@ def _insert_ult_ident_presc(db: Database, set_Start_Date: str, dup_opp_cd: str, raise e -def _select_emp_chg_inst(db: Database, dcf_inst_cd: str, dup_opp_cd: str, ta_cd: str) -> list[dict]: +def _select_emp_chg_inst(db: Database, dcf_inst_cd: str, dup_opp_cd: str, ta_cd: str, emp_chg_type_cd: str) -> list[dict]: # emp_chg_instからSELECT try: sql = """ SELECT eci.inst_cd, eci.ta_cd, + eci.emp_chg_type_cd, eci.emp_cd, eci.bu_cd, eci.start_date, @@ -539,16 +548,18 @@ def _select_emp_chg_inst(db: Database, dcf_inst_cd: str, dup_opp_cd: str, ta_cd: WHERE eciopp.inst_cd = :dup_opp_cd AND eciopp.ta_cd = :ta_cd + AND eciopp.emp_chg_type_cd = :emp_chg_type_cd ) AS opp_count FROM src05.emp_chg_inst AS eci WHERE eci.inst_cd = :dcf_inst_cd AND eci.ta_cd = :ta_cd + AND eci.emp_chg_type_cd = :emp_chg_type_cd AND eci.enabled_flg = 'Y' AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < eci.end_date """ - params = {'dcf_inst_cd': dcf_inst_cd, 'dup_opp_cd': dup_opp_cd, 'ta_cd': ta_cd} + params = {'dcf_inst_cd': dcf_inst_cd, 'dup_opp_cd': dup_opp_cd, 'ta_cd': ta_cd, 'emp_chg_type_cd': emp_chg_type_cd} emp_chg_inst_records = db.execute_select(sql, params) logging_sql(logger, sql) logger.info('従業員担当施設マスタの取得 成功') diff --git a/ecs/jskult-batch-daily/src/batch/laundering/emp_chg_inst_laundering.py b/ecs/jskult-batch-daily/src/batch/laundering/emp_chg_inst_laundering.py index 8ec3b84e..51680d2e 100644 --- a/ecs/jskult-batch-daily/src/batch/laundering/emp_chg_inst_laundering.py +++ b/ecs/jskult-batch-daily/src/batch/laundering/emp_chg_inst_laundering.py @@ -53,7 +53,9 @@ def _insert_into_emp_chg_inst_lau_from_emp_chg_inst(db: Database): src05.emp_chg_inst_lau SELECT inst_cd, - ta_cd,emp_cd, + ta_cd, + emp_chg_type_cd, + emp_cd, bu_cd, start_date, end_date, diff --git a/ecs/jskult-batch-daily/src/batch/laundering/sales_laundering.py b/ecs/jskult-batch-daily/src/batch/laundering/sales_laundering.py index 2cc530f9..8a25ae76 100644 --- a/ecs/jskult-batch-daily/src/batch/laundering/sales_laundering.py +++ b/ecs/jskult-batch-daily/src/batch/laundering/sales_laundering.py @@ -1,8 +1,9 @@ from src.batch.common.batch_context import BatchContext -from src.batch.laundering import ( - create_inst_merge_for_laundering, emp_chg_inst_laundering, - ult_ident_presc_laundering, sales_results_laundering) from src.batch.dcf_inst_merge import integrate_dcf_inst_merge +from src.batch.laundering import (create_inst_merge_for_laundering, + emp_chg_inst_laundering, + sales_results_laundering, + ult_ident_presc_laundering) from src.logging.get_logger import get_logger batch_context = BatchContext.get_instance() @@ -18,7 +19,7 @@ def exec(): logger.info('営業日ではないため、実績洗替処理をスキップします。') return # DCF施設統合マスタ日次更新 - integrate_dcf_inst_merge.exec() + integrate_dcf_inst_merge.exec() # 洗替用マスタ作成 create_inst_merge_for_laundering.exec() # 施設担当者洗替 diff --git a/ecs/jskult-webapp/src/controller/login.py b/ecs/jskult-webapp/src/controller/login.py index 5c8d904e..9b2c808e 100644 --- a/ecs/jskult-webapp/src/controller/login.py +++ b/ecs/jskult-webapp/src/controller/login.py @@ -70,11 +70,22 @@ def login( jwt_token = login_service.login(request.username, request.password) except NotAuthorizeException as e: logger.info(f'ログイン失敗:{e}') + # ログイン失敗回数をカウント + login_service.increase_login_failed_count(request.username) + # ログイン失敗回数を超過した場合はメッセージを変える + if login_service.is_login_failed_limit_exceeded(request.username): + login_service.on_login_fail_limit_exceeded(request.username) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_FAILED_LIMIT_EXCEEDED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR) except JWTTokenVerifyException as e: logger.info(f'ログイン失敗:{e}') raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) + # ログイン成功問わず、DBのログイン失敗回数が10回以上あれば、ログアウト画面にリダイレクトする + if login_service.is_login_failed_limit_exceeded(request.username): + logger.info(f'ログイン失敗回数が10回以上: {request.username}') + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_FAILED_LIMIT_EXCEEDED) + verified_token = jwt_token.verify_token() # 普通の認証だと、`cognito:username`に入る。 user_id = verified_token.user_id diff --git a/ecs/jskult-webapp/src/controller/master_mainte.py b/ecs/jskult-webapp/src/controller/master_mainte.py index e9d4ed14..87b16978 100644 --- a/ecs/jskult-webapp/src/controller/master_mainte.py +++ b/ecs/jskult-webapp/src/controller/master_mainte.py @@ -199,7 +199,7 @@ def new_inst_result_view( return templates_response -@ router.get('/instEmpCsvDL', response_class=HTMLResponse) +@router.get('/instEmpCsvDL', response_class=HTMLResponse) def inst_emp_csv_download_view( request: Request, batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)) @@ -270,6 +270,7 @@ def inst_emp_csv_download( ta_cd=csv_download_form.ta_cd, inst_cd=csv_download_form.inst_cd, emp_cd=csv_download_form.emp_cd, + emp_chg_type_cd=csv_download_form.emp_chg_type_cd, apply_date_from=csv_download_form.apply_date_from, start_date_from=csv_download_form.start_date_from, start_date_to=csv_download_form.start_date_to, diff --git a/ecs/jskult-webapp/src/db/database.py b/ecs/jskult-webapp/src/db/database.py index c729faf5..bd828db3 100644 --- a/ecs/jskult-webapp/src/db/database.py +++ b/ecs/jskult-webapp/src/db/database.py @@ -189,7 +189,7 @@ class DatabaseClient: self.__session = None def to_jst(self): - self.execute('SET time_zone = "+9:00"') + self.execute('SET SESSION time_zone = "Asia/Tokyo"') def __execute_with_transaction(self, query: str, parameters: dict): # トランザクションを開始してクエリを実行する diff --git a/ecs/jskult-webapp/src/model/db/user_master.py b/ecs/jskult-webapp/src/model/db/user_master.py index 913b415e..d86253ae 100644 --- a/ecs/jskult-webapp/src/model/db/user_master.py +++ b/ecs/jskult-webapp/src/model/db/user_master.py @@ -2,7 +2,7 @@ from datetime import datetime from typing import Optional from src.model.db.base_db_model import BaseDBModel - +from src.system_var import constants class UserMasterModel(BaseDBModel): user_id: Optional[str] @@ -25,6 +25,8 @@ class UserMasterModel(BaseDBModel): updater: Optional[str] update_date: Optional[datetime] mntuser_flg: Optional[str] + mntuser_login_failed_cnt: Optional[int] + mntuser_last_login_failed_datetime: Optional[datetime] def is_enable_user(self): return self.enabled_flg == 'Y' @@ -34,3 +36,6 @@ class UserMasterModel(BaseDBModel): def is_groupware_user(self): return self.mntuser_flg == '0' or self.mntuser_flg is None + + def is_login_failed_limit_exceeded(self): + return self.mntuser_login_failed_cnt >= constants.LOGIN_FAIL_LIMIT \ No newline at end of file diff --git a/ecs/jskult-webapp/src/model/internal/master_mainte_csv.py b/ecs/jskult-webapp/src/model/internal/master_mainte_csv.py index e288ce3a..a6146731 100644 --- a/ecs/jskult-webapp/src/model/internal/master_mainte_csv.py +++ b/ecs/jskult-webapp/src/model/internal/master_mainte_csv.py @@ -1,16 +1,17 @@ import csv import json - -from io import TextIOWrapper -from datetime import datetime from abc import ABCMeta, abstractmethod +from datetime import datetime +from io import TextIOWrapper + +from src.logging.get_logger import get_logger +from src.repositories.bu_master_cd_repository import BuMasterRepository +from src.repositories.emp_chg_inst_repository import EmpChgInstRepository +from src.repositories.emp_master_repository import EmpMasterRepository +from src.repositories.generic_kbn_mst_repository import GenericKbnMstRepository +from src.repositories.mst_inst_repository import MstInstRepository from src.system_var import constants from src.util.string_util import is_not_empty -from src.repositories.mst_inst_repository import MstInstRepository -from src.repositories.bu_master_cd_repository import BuMasterRepository -from src.repositories.emp_master_repository import EmpMasterRepository -from src.repositories.emp_chg_inst_repository import EmpChgInstRepository -from src.logging.get_logger import get_logger logger = get_logger('マスターメンテ') @@ -24,6 +25,7 @@ class MasterMainteCSVItem(metaclass=ABCMeta): emp_master_repository: EmpMasterRepository bu_master_repository: BuMasterRepository emp_chginst_repository: EmpChgInstRepository + generic_kbn_mst_repository: GenericKbnMstRepository def __init__( self, @@ -33,7 +35,8 @@ class MasterMainteCSVItem(metaclass=ABCMeta): mst_inst_repository: MstInstRepository, emp_master_repository: EmpMasterRepository, bu_master_repository: BuMasterRepository, - emp_chginst_repository: EmpChgInstRepository + emp_chginst_repository: EmpChgInstRepository, + generic_kbn_mst_repository: GenericKbnMstRepository ): self.csv_row = csv_row self.table_name = table_name @@ -42,6 +45,7 @@ class MasterMainteCSVItem(metaclass=ABCMeta): self.emp_master_repository = emp_master_repository self.bu_master_repository = bu_master_repository self.emp_chginst_repository = emp_chginst_repository + self.generic_kbn_mst_repository = generic_kbn_mst_repository def validate(self) -> list[str]: """ @@ -57,6 +61,10 @@ class MasterMainteCSVItem(metaclass=ABCMeta): error_list.extend(self.check_require()) # 施設コード存在チェック error_list.extend(self.check_inst_cd_exists()) + # 領域コード存在チェック + error_list.extend(self.check_ta_cd_exists()) + # 担当者種別コード存在チェック + error_list.extend(self.check_emp_chg_type_cd_exists()) # MUID存在チェック error_list.extend(self.check_emp_cd_exists()) # BuCd存在チェック @@ -79,7 +87,7 @@ class MasterMainteCSVItem(metaclass=ABCMeta): return error_list def emp_chg_inst_count(self, start_date: str): - return self.emp_chginst_repository.fetch_count(self.inst_cd, self.ta_cd, start_date, self.table_name) + return self.emp_chginst_repository.fetch_count(self.inst_cd, self.ta_cd, self.emp_chg_type_cd, start_date, self.table_name) def is_exist_emp_cd(self, start_date: str) -> bool: if start_date is None or len(start_date) == 0: @@ -91,12 +99,36 @@ class MasterMainteCSVItem(metaclass=ABCMeta): def is_exist_inst_cd(self) -> bool: return True if self.mst_inst_repository.fetch_count(self.inst_cd) > 0 else False + def is_exist_emp_chg_type_cd(self, start_date: str) -> bool: + if start_date is None or len(start_date) == 0: + return False + if self.generic_kbn_mst_repository.fetch_count('emp_chg_type_cd', self.emp_chg_type_cd, start_date) > 0: + return True + return False + + def is_exist_ta_cd(self, start_date: str) -> bool: + if start_date is None or len(start_date) == 0: + return False + if self.generic_kbn_mst_repository.fetch_count('ta_cd', self.ta_cd, start_date) > 0: + return True + return False + def is_exist_bu_cd(self) -> bool: return True if self.bu_master_repository.fetch_count(self.bu_cd) > 0 else False def make_require_error_message(self, line_num: str, col_name: str) -> str: return f'{line_num}行目の{col_name}が入力されておりません。' + def make_data_exist_error_message(self, line_num: str, primary_key_col_names: list[str]) -> str: + return self.__make_check_data_exists_error_message(line_num, primary_key_col_names, 'がすべて同一のデータが既に登録されています。') + + def make_data_not_exist_error_message(self, line_num: str, primary_key_col_names: list[str]) -> str: + return self.__make_check_data_exists_error_message(line_num, primary_key_col_names, 'がすべて同一のデータが存在しないため更新できません。') + + def __make_check_data_exists_error_message(self, line_num: str, primary_key_col_names: list[str], suffix_message: str) -> str: + primary_key_logical_names = '、'.join(primary_key_col_names) + return f'{line_num}行目の{primary_key_logical_names}{suffix_message}' + def __parse_str_to_date(self, check_date: str) -> tuple[bool, datetime]: try: check_date_time: datetime = datetime.strptime(check_date, '%Y%m%d') @@ -160,6 +192,18 @@ class MasterMainteCSVItem(metaclass=ABCMeta): pass ... + @abstractmethod + def check_emp_chg_type_cd_exists(self) -> list[str]: + """担当者種別コード存在チェック""" + pass + ... + + @abstractmethod + def check_ta_cd_exists(self) -> list[str]: + """領域コード存在チェック""" + pass + ... + @abstractmethod def check_emp_cd_exists(self) -> list[str]: """MUID存在チェック""" @@ -205,7 +249,8 @@ class MasterMainteNewInstEmpCSVItem(MasterMainteCSVItem): mst_inst_repository: MstInstRepository, emp_master_repository: EmpMasterRepository, bu_master_repository: BuMasterRepository, - emp_chginst_repository: EmpChgInstRepository + emp_chginst_repository: EmpChgInstRepository, + generic_kbn_mst_repository: GenericKbnMstRepository ): super().__init__( csv_row, @@ -214,11 +259,13 @@ class MasterMainteNewInstEmpCSVItem(MasterMainteCSVItem): mst_inst_repository, emp_master_repository, bu_master_repository, - emp_chginst_repository + emp_chginst_repository, + generic_kbn_mst_repository ) self.inst_cd = super().get_csv_value(constants.CSV_NEW_INST_CD_COL_NO) self.inst_name = super().get_csv_value(constants.CSV_NEW_INST_NAME_COL_NO) self.ta_cd = super().get_csv_value(constants.CSV_NEW_TA_CD_COL_NO) + self.emp_chg_type_cd = super().get_csv_value(constants.CSV_NEW_EMP_CHG_TYPE_CD_COL_NO) self.emp_cd = super().get_csv_value(constants.CSV_NEW_EMP_CD_COL_NO) self.emp_name_family = super().get_csv_value(constants.CSV_NEW_EMP_NAME_FAMILY_COL_NO) self.emp_name_first = super().get_csv_value(constants.CSV_NEW_EMP_NAME_FIRST_COL_NO) @@ -237,6 +284,9 @@ class MasterMainteNewInstEmpCSVItem(MasterMainteCSVItem): if len(self.ta_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_TA_CD_COL_NO])) + if len(self.emp_chg_type_cd) == 0: + error_list.append(self.make_require_error_message( + self.line_num, constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_EMP_CHG_TYPE_CD_COL_NO])) if len(self.emp_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_EMP_CD_COL_NO])) @@ -271,6 +321,26 @@ class MasterMainteNewInstEmpCSVItem(MasterMainteCSVItem): は従業員マスタに存在しない もしくは 適用期間外のIDです。') return error_list + def check_emp_chg_type_cd_exists(self) -> list[str]: + error_list = [] + if not self.start_date or not self.emp_chg_type_cd: + return error_list + + if is_not_empty(self.emp_chg_type_cd) and super().is_exist_emp_chg_type_cd(self.start_date) is False: + error_list.append(f'{self.line_num}行目の{constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_EMP_CHG_TYPE_CD_COL_NO]}\ +は汎用区分マスタに存在しない もしくは 適用期間外のコードです。') + return error_list + + def check_ta_cd_exists(self) -> list[str]: + error_list = [] + if not self.start_date or not self.ta_cd: + return error_list + + if is_not_empty(self.ta_cd) and super().is_exist_ta_cd(self.start_date) is False: + error_list.append(f'{self.line_num}行目の{constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_TA_CD_COL_NO]}\ +は汎用区分マスタに存在しない もしくは 適用期間外のコードです。') + return error_list + def check_bu_cd_exists(self) -> list[str]: error_list = [] @@ -303,7 +373,15 @@ class MasterMainteNewInstEmpCSVItem(MasterMainteCSVItem): def check_data_exists(self) -> list[str]: error_list = [] if super().emp_chg_inst_count(self.start_date) > 0: - error_list.append(f'{self.line_num}行目の施設コード、領域コード、適用開始日がすべて同一のデータが既に登録されています。') + error_list.append(super().make_data_exist_error_message( + self.line_num, + primary_key_col_names=[ + constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_INST_CD_COL_NO], + constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_TA_CD_COL_NO], + constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_EMP_CHG_TYPE_CD_COL_NO], + constants.NEW_INST_EMP_CSV_LOGICAL_NAMES[constants.CSV_NEW_START_DATE] + ] + )) return error_list @@ -329,7 +407,8 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): mst_inst_repository: MstInstRepository, emp_master_repository: EmpMasterRepository, bu_master_repository: BuMasterRepository, - emp_chginst_repository: EmpChgInstRepository + emp_chginst_repository: EmpChgInstRepository, + generic_kbn_mst_repository: GenericKbnMstRepository ): super().__init__( csv_row, @@ -338,7 +417,8 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): mst_inst_repository, emp_master_repository, bu_master_repository, - emp_chginst_repository + emp_chginst_repository, + generic_kbn_mst_repository ) self.bu_cd = super().get_csv_value(constants.CSV_CHANGE_BU_CD_COL_NO) self.bu_name = super().get_csv_value(constants.CSV_CHANGE_BU_NAME_COL_NO) @@ -348,6 +428,7 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): self.inst_name = super().get_csv_value(constants.CSV_CHANGE_INST_NAME_COL_NO) self.ta_cd = super().get_csv_value(constants.CSV_CHANGE_TA_CD_COL_NO) self.explain = super().get_csv_value(constants.CSV_CHANGE_EXPLAIN_COL_NO) + self.emp_chg_type_cd = super().get_csv_value(constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO) self.emp_cd = super().get_csv_value(constants.CSV_CHANGE_EMP_CD_COL_NO) self.emp_full_name = super().get_csv_value(constants.CSV_CHANGE_EMP_FULL_NAME_COL_NO) self.inst_emp_start_date = super().get_csv_value(constants.CSV_CHANGE_INST_EMP_START_DATE_COL_NO) @@ -370,6 +451,9 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): if len(self.ta_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO])) + if len(self.emp_chg_type_cd) == 0: + error_list.append(self.make_require_error_message( + self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO])) if len(self.emp_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CD_COL_NO])) @@ -388,6 +472,9 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): if len(self.ta_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO])) + if len(self.emp_chg_type_cd) == 0: + error_list.append(self.make_require_error_message( + self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO])) if len(self.inst_emp_start_date) == 0: error_list.append(self.make_require_error_message( self.line_num, @@ -403,6 +490,10 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): if len(self.ta_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO])) + if len(self.emp_chg_type_cd) == 0: + error_list.append(self.make_require_error_message( + self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO])) + if len(self.emp_cd) == 0: error_list.append(self.make_require_error_message( self.line_num, constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CD_COL_NO])) @@ -435,6 +526,28 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): は従業員マスタに存在しない もしくは 適用期間外のIDです。') return error_list + def check_emp_chg_type_cd_exists(self) -> list[str]: + error_list = [] + + if not self.inst_emp_start_date or not self.emp_chg_type_cd: + return error_list + + if is_not_empty(self.emp_chg_type_cd) and super().is_exist_emp_chg_type_cd(self.inst_emp_start_date) is False: + error_list.append(f'{self.line_num}行目の{constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO]}\ +は汎用区分マスタに存在しない もしくは 適用期間外のコードです。') + return error_list + + def check_ta_cd_exists(self) -> list[str]: + error_list = [] + + if not self.inst_emp_start_date or not self.ta_cd: + return error_list + + if is_not_empty(self.ta_cd) and super().is_exist_ta_cd(self.inst_emp_start_date) is False: + error_list.append(f'{self.line_num}行目の{constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO]}\ +は汎用区分マスタに存在しない もしくは 適用期間外のコードです。') + return error_list + def check_bu_cd_exists(self) -> list[str]: error_list = [] @@ -484,10 +597,26 @@ class MasterMainteChangeInstEmpCSVItem(MasterMainteCSVItem): error_list = [] emp_chg_inst_count = super().emp_chg_inst_count(self.inst_emp_start_date) if self.comment == '追加' and emp_chg_inst_count > 0: - error_list.append(f'{self.line_num}行目の施設コード、領域コード、施設担当_開始日がすべて同一のデータが既に登録されています。') + error_list.append(super().make_data_exist_error_message( + self.line_num, + primary_key_col_names=[ + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_INST_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_INST_EMP_START_DATE_COL_NO] + ] + )) elif (self.comment == '終了' or self.comment == '担当者修正') and emp_chg_inst_count == 0: - error_list.append(f'{self.line_num}行目の施設コード、領域コード、施設担当_開始日がすべて同一のデータが存在しないため更新できません。') + error_list.append(super().make_data_not_exist_error_message( + self.line_num, + primary_key_col_names=[ + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_INST_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_TA_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO], + constants.CHANGE_INST_CSV_LOGICAL_NAMES[constants.CSV_CHANGE_INST_EMP_START_DATE_COL_NO] + ] + )) return error_list @@ -525,7 +654,8 @@ class MasterMainteCSVItems: mst_inst_repository: MstInstRepository, emp_master_repository: EmpMasterRepository, bu_master_repository: BuMasterRepository, - emp_chginst_repository: EmpChgInstRepository + emp_chginst_repository: EmpChgInstRepository, + generic_kbn_mst_repository: GenericKbnMstRepository ) -> None: reader = csv.reader(file) csv_rows = [] @@ -540,7 +670,9 @@ class MasterMainteCSVItems: mst_inst_repository, emp_master_repository, bu_master_repository, - emp_chginst_repository)) + emp_chginst_repository, + generic_kbn_mst_repository + )) self.lines = csv_rows def __select_function(self, @@ -551,7 +683,8 @@ class MasterMainteCSVItems: mst_inst_repository: MstInstRepository, emp_master_repository: EmpMasterRepository, bu_master_repository: BuMasterRepository, - emp_chginst_repository: EmpChgInstRepository) -> MasterMainteCSVItem: + emp_chginst_repository: EmpChgInstRepository, + generic_kbn_mst_repository: GenericKbnMstRepository) -> MasterMainteCSVItem: if function_type == 'new': return MasterMainteNewInstEmpCSVItem( row, @@ -560,7 +693,8 @@ class MasterMainteCSVItems: mst_inst_repository, emp_master_repository, bu_master_repository, - emp_chginst_repository) + emp_chginst_repository, + generic_kbn_mst_repository) elif function_type == 'change': return MasterMainteChangeInstEmpCSVItem( row, @@ -569,4 +703,5 @@ class MasterMainteCSVItems: mst_inst_repository, emp_master_repository, bu_master_repository, - emp_chginst_repository) + emp_chginst_repository, + generic_kbn_mst_repository) diff --git a/ecs/jskult-webapp/src/model/internal/master_mainte_emp_chg_inst_function.py b/ecs/jskult-webapp/src/model/internal/master_mainte_emp_chg_inst_function.py index 18a5cb0c..1c79b75e 100644 --- a/ecs/jskult-webapp/src/model/internal/master_mainte_emp_chg_inst_function.py +++ b/ecs/jskult-webapp/src/model/internal/master_mainte_emp_chg_inst_function.py @@ -29,8 +29,8 @@ class MasterMainteEmpChgInstFunction(metaclass=ABCMeta): def save(self): error_list = [] try: - self.emp_chginst_repository.to_jst() self.emp_chginst_repository.begin() + self.emp_chginst_repository.to_jst() (result_message, error_list) = self.write_emp_chg_inst_table() if len(error_list) > 0: self.emp_chginst_repository.rollback() @@ -46,6 +46,7 @@ class MasterMainteEmpChgInstFunction(metaclass=ABCMeta): self.emp_chginst_repository.insert_emp_chg_inst( data['施設コード'], data['領域コード'], + data['担当者種別コード'], data['MUID'], data['ビジネスユニットコード'], start_date, @@ -148,6 +149,7 @@ class ChangeEmpChgInstFunction(MasterMainteEmpChgInstFunction): self.emp_chginst_repository.end_emp_chg_inst( data['施設コード'], data['領域コード'], + data['担当者種別コード'], data['施設担当_開始日'], data['終了日の変更'], self.user_name, @@ -158,6 +160,7 @@ class ChangeEmpChgInstFunction(MasterMainteEmpChgInstFunction): data['施設コード'], data['領域コード'], data['施設担当_開始日'], + data['担当者種別コード'], data['MUID'], self.user_name, self.table_name) diff --git a/ecs/jskult-webapp/src/model/request/master_mainte_csvdl.py b/ecs/jskult-webapp/src/model/request/master_mainte_csvdl.py index 4155fa73..0ec43a10 100644 --- a/ecs/jskult-webapp/src/model/request/master_mainte_csvdl.py +++ b/ecs/jskult-webapp/src/model/request/master_mainte_csvdl.py @@ -2,20 +2,22 @@ from typing import Optional from fastapi import Form -from src.util.sanitize import sanitize - from src.model.request.request_base_model import RequestBaseModel +from src.util.sanitize import sanitize from src.util.string_util import is_not_empty @sanitize class MasterMainteCsvDlModel(RequestBaseModel): + # adaptは検索に使用する値 ta_cd: Optional[str] adapt_ta_cd: Optional[str] inst_cd: Optional[str] adapt_inst_cd: Optional[str] emp_cd: Optional[str] adapt_emp_cd: Optional[str] + emp_chg_type_cd: Optional[str] + adapt_emp_chg_type_cd: Optional[str] apply_date_from: Optional[str] adapt_apply_date_from: Optional[str] start_date_from: Optional[str] @@ -42,6 +44,7 @@ class MasterMainteCsvDlModel(RequestBaseModel): ctrl_ta_cd: Optional[str] = Form(None), ctrl_inst_cd: Optional[str] = Form(None), ctrl_emp_cd: Optional[str] = Form(None), + ctrl_emp_chg_type_cd: Optional[str] = Form(None), ctrl_apply_date_from: Optional[str] = Form(None), ctrl_start_date_from: Optional[str] = Form(None), ctrl_start_date_to: Optional[str] = Form(None), @@ -58,6 +61,7 @@ class MasterMainteCsvDlModel(RequestBaseModel): ctrl_ta_cd, ctrl_inst_cd, ctrl_emp_cd, + ctrl_emp_chg_type_cd, ctrl_apply_date_from, ctrl_start_date_from, ctrl_start_date_to, @@ -75,6 +79,7 @@ class MasterMainteCsvDlModel(RequestBaseModel): ctrl_ta_cd: str, ctrl_inst_cd: str, ctrl_emp_cd: str, + ctrl_emp_chg_type_cd, ctrl_apply_date_from: str, ctrl_start_date_from: str, ctrl_start_date_to: str, @@ -89,6 +94,7 @@ class MasterMainteCsvDlModel(RequestBaseModel): ctrl_ta_cd = ctrl_ta_cd if is_not_empty(ctrl_ta_cd) else '' ctrl_inst_cd = ctrl_inst_cd if is_not_empty(ctrl_inst_cd) else '' ctrl_emp_cd = ctrl_emp_cd if is_not_empty(ctrl_emp_cd) else '' + ctrl_emp_chg_type_cd = ctrl_emp_chg_type_cd if is_not_empty(ctrl_emp_chg_type_cd) else '' adapt_apply_date_from = '' if is_not_empty(ctrl_apply_date_from): @@ -147,6 +153,8 @@ class MasterMainteCsvDlModel(RequestBaseModel): adapt_inst_cd=ctrl_inst_cd, emp_cd=ctrl_emp_cd, adapt_emp_cd=ctrl_emp_cd, + emp_chg_type_cd=ctrl_emp_chg_type_cd, + adapt_emp_chg_type_cd=ctrl_emp_chg_type_cd, apply_date_from=ctrl_apply_date_from, adapt_apply_date_from=adapt_apply_date_from, start_date_from=ctrl_start_date_from, diff --git a/ecs/jskult-webapp/src/model/view/inst_emp_csv_download_view_model.py b/ecs/jskult-webapp/src/model/view/inst_emp_csv_download_view_model.py index aa04068d..e7ac331a 100644 --- a/ecs/jskult-webapp/src/model/view/inst_emp_csv_download_view_model.py +++ b/ecs/jskult-webapp/src/model/view/inst_emp_csv_download_view_model.py @@ -9,6 +9,7 @@ class InstEmpCsvDownloadViewModel(BaseModel): ta_cd: str = '' inst_cd: str = '' emp_cd: str = '' + emp_chg_type_cd: str = '' apply_date_from: str = '' start_date_from: str = '' start_date_to: str = '' diff --git a/ecs/jskult-webapp/src/repositories/emp_chg_inst_repository.py b/ecs/jskult-webapp/src/repositories/emp_chg_inst_repository.py index d91be694..fe5329f2 100644 --- a/ecs/jskult-webapp/src/repositories/emp_chg_inst_repository.py +++ b/ecs/jskult-webapp/src/repositories/emp_chg_inst_repository.py @@ -1,10 +1,10 @@ -from src.repositories.base_repository import BaseRepository -from src.db.sql_condition import SQLCondition from src.db import sql_condition as condition +from src.db.sql_condition import SQLCondition +from src.logging.get_logger import get_logger from src.model.db.master_mente_count import MasterMenteCountModel from src.model.request.master_mainte_csvdl import MasterMainteCsvDlModel +from src.repositories.base_repository import BaseRepository from src.util.string_util import is_not_empty -from src.logging.get_logger import get_logger logger = get_logger('従業員担当施設マスタ') @@ -28,6 +28,7 @@ class EmpChgInstRepository(BaseRepository): ( inst_cd, ta_cd, + emp_chg_type_cd, emp_cd, bu_cd, start_date, @@ -42,6 +43,7 @@ class EmpChgInstRepository(BaseRepository): VALUES ( :inst_cd, :ta_cd, + :emp_chg_type_cd, :emp_cd, :bu_cd, :start_date, @@ -55,13 +57,14 @@ class EmpChgInstRepository(BaseRepository): ) """ - def insert_emp_chg_inst(self, inst_cd, ta_cd, emp_cd, bu_cd, start_date, + def insert_emp_chg_inst(self, inst_cd, ta_cd, emp_chg_type_cd, emp_cd, bu_cd, start_date, end_date, create_user_name, table_name): try: query = self.INSERT_SQL.format(table_name=table_name) self._database.execute(query, { 'inst_cd': inst_cd, 'ta_cd': ta_cd, + 'emp_chg_type_cd': emp_chg_type_cd, 'emp_cd': emp_cd, 'bu_cd': bu_cd, 'start_date': start_date, @@ -82,17 +85,19 @@ class EmpChgInstRepository(BaseRepository): update_date = NOW() WHERE inst_cd = :inst_cd - and ta_cd = :ta_cd - and start_date = :start_date + AND ta_cd = :ta_cd + AND emp_chg_type_cd = :emp_chg_type_cd + AND start_date = :start_date """ - def end_emp_chg_inst(self, inst_cd, ta_cd, start_date, + def end_emp_chg_inst(self, inst_cd, ta_cd, emp_chg_type_cd, start_date, end_date, update_user_name, table_name): try: query = self.UPDATE_END_DATE_SQL.format(table_name=table_name) self._database.execute(query, { 'inst_cd': inst_cd, 'ta_cd': ta_cd, + 'emp_chg_type_cd': emp_chg_type_cd, 'start_date': start_date, 'end_date': end_date, 'update_user_name': update_user_name @@ -110,16 +115,18 @@ class EmpChgInstRepository(BaseRepository): update_date = NOW() where inst_cd = :inst_cd - and ta_cd = :ta_cd - and start_date = :start_date + AND ta_cd = :ta_cd + AND emp_chg_type_cd = :emp_chg_type_cd + AND start_date = :start_date """ - def modify_emp_chg_inst(self, inst_cd, ta_cd, start_date, emp_cd, update_user_name, table_name): + def modify_emp_chg_inst(self, inst_cd, ta_cd, start_date, emp_chg_type_cd, emp_cd, update_user_name, table_name): try: query = self.UPDATE_EMP_CD_SQL.format(table_name=table_name) self._database.execute(query, { 'inst_cd': inst_cd, 'ta_cd': ta_cd, + 'emp_chg_type_cd': emp_chg_type_cd, 'start_date': start_date, 'emp_cd': emp_cd, 'update_user_name': update_user_name @@ -136,14 +143,15 @@ class EmpChgInstRepository(BaseRepository): WHERE inst_cd = :inst_cd AND ta_cd = :ta_cd + AND emp_chg_type_cd = :emp_chg_type_cd AND start_date = :start_date """ - def fetch_count(self, inst_cd, ta_cd, start_date, table_name) -> MasterMenteCountModel: + def fetch_count(self, inst_cd, ta_cd, emp_chg_type_cd, start_date, table_name) -> MasterMenteCountModel: try: query = self.FETCH_COUNT_SQL.format(table_name=table_name) result = self._database.execute_select(query, {'inst_cd': inst_cd, 'ta_cd': ta_cd, - 'start_date': start_date}) + 'emp_chg_type_cd': emp_chg_type_cd, 'start_date': start_date}) models = [MasterMenteCountModel(**r) for r in result] if len(models) == 0: return 0 @@ -157,6 +165,7 @@ class EmpChgInstRepository(BaseRepository): eci.inst_cd AS inst_cd, mi.inst_name AS inst_name, eci.ta_cd AS ta_cd, + eci.emp_chg_type_cd AS emp_chg_type_cd, eci.emp_cd AS emp_cd, CONCAT(emp.emp_name_family, " ", emp.emp_name_first) AS emp_name_full, eci.bu_cd AS bu_cd, @@ -212,6 +221,11 @@ class EmpChgInstRepository(BaseRepository): parameter.adapt_emp_cd = f'%{parameter.emp_cd}%' where_clauses.append(SQLCondition('eci.emp_cd', condition.LIKE, 'adapt_emp_cd')) + # 担当者種別コードが入力されていた場合 + if is_not_empty(parameter.emp_chg_type_cd): + parameter.adapt_emp_chg_type_cd = f'%{parameter.emp_chg_type_cd}%' + where_clauses.append(SQLCondition('eci.emp_chg_type_cd', condition.LIKE, 'adapt_emp_chg_type_cd')) + # 適用期間内が入力されていた場合 if is_not_empty(parameter.adapt_apply_date_from): where_clauses.append(SQLCondition('eci.start_date', condition.LE, 'adapt_apply_date_from')) diff --git a/ecs/jskult-webapp/src/repositories/generic_kbn_mst_repository.py b/ecs/jskult-webapp/src/repositories/generic_kbn_mst_repository.py new file mode 100644 index 00000000..e2d210c4 --- /dev/null +++ b/ecs/jskult-webapp/src/repositories/generic_kbn_mst_repository.py @@ -0,0 +1,33 @@ +from src.repositories.base_repository import BaseRepository +from src.model.db.master_mente_count import MasterMenteCountModel +from src.logging.get_logger import get_logger + +logger = get_logger('汎用区分マスタ') + +class GenericKbnMstRepository(BaseRepository): + + FETCH_SQL = """\ + SELECT + COUNT(*) AS count + FROM + src05.generic_kbn_mst + WHERE + generic_kbn_mst.generic_kbn_cd = :generic_kbn_cd + AND + generic_kbn_mst.kbn_cd = :kbn_cd + AND + STR_TO_DATE( :start_date , '%Y%m%d') BETWEEN generic_kbn_mst.start_date AND generic_kbn_mst.end_date\ + """ + + + def fetch_count(self, generic_kbn_cd: str, kbn_cd: str, start_date: str) -> MasterMenteCountModel: + try: + query = self.FETCH_SQL + result = self._database.execute_select(query, {'generic_kbn_cd': generic_kbn_cd, 'kbn_cd': kbn_cd, 'start_date' : start_date}) + models = [MasterMenteCountModel(**r) for r in result] + if len(models) == 0: + return 0 + return models[0].count + except Exception as e: + logger.error(f"DB Error : Exception={e.args}") + raise e \ No newline at end of file diff --git a/ecs/jskult-webapp/src/repositories/user_master_repository.py b/ecs/jskult-webapp/src/repositories/user_master_repository.py index 7edde00a..3acbc105 100644 --- a/ecs/jskult-webapp/src/repositories/user_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/user_master_repository.py @@ -6,6 +6,19 @@ logger = get_logger('ユーザー取得') class UserMasterRepository(BaseRepository): + + def to_jst(self): + self._database.to_jst() + + def begin(self): + self._database.begin() + + def commit(self): + self._database.commit() + + def rollback(self): + self._database.rollback() + FETCH_SQL = """\ SELECT * @@ -26,3 +39,46 @@ class UserMasterRepository(BaseRepository): except Exception as e: logger.exception(f"DB Error : Exception={e}") raise e + + def increase_login_failed_count(self, parameter: dict) -> UserMasterModel: + try: + query = """\ + UPDATE + src05.user_mst + SET + mntuser_login_failed_cnt = + CASE + WHEN + DATE(mntuser_last_login_failed_datetime) = DATE(CURRENT_TIMESTAMP()) + THEN + mntuser_login_failed_cnt + 1 + ELSE + 1 + END, + mntuser_last_login_failed_datetime = CURRENT_TIMESTAMP() + WHERE + user_id = :user_id + AND + mntuser_flg = 1;\ + """ + self._database.execute(query, parameter) + except Exception as e: + logger.exception(f"DB Error : Exception={e}") + raise e + + def disable_mnt_user(self, parameter: dict) -> UserMasterModel: + try: + query = """\ + UPDATE + src05.user_mst + SET + enabled_flg = 'N' + WHERE + user_id = :user_id + AND + mntuser_flg = 1\ + """ + self._database.execute(query, parameter) + except Exception as e: + logger.exception(f"DB Error : Exception={e}") + raise e diff --git a/ecs/jskult-webapp/src/services/login_service.py b/ecs/jskult-webapp/src/services/login_service.py index aa1f37fd..3a2715b2 100644 --- a/ecs/jskult-webapp/src/services/login_service.py +++ b/ecs/jskult-webapp/src/services/login_service.py @@ -49,6 +49,27 @@ class LoginService(BaseService): user_record: UserMasterModel = self.user_repository.fetch_one({'user_id': user_id}) return user_record + def increase_login_failed_count(self, user_id: str): + + try: + # セッション内のタイムゾーン変更のため、明示的にトランザクションを開始する + self.user_repository.begin() + self.user_repository.to_jst() + self.user_repository.increase_login_failed_count({'user_id': user_id}) + self.user_repository.commit() + except Exception as e: + self.user_repository.rollback() + raise e + + def on_login_fail_limit_exceeded(self, user_id: str): + self.user_repository.disable_mnt_user({'user_id': user_id}) + + def is_login_failed_limit_exceeded(self, user_id: str): + user_record: UserMasterModel = self.user_repository.fetch_one({'user_id': user_id}) + if user_record is None: + return False + return user_record.is_login_failed_limit_exceeded() + def __secret_hash(self, username: str): # see - https://aws.amazon.com/jp/premiumsupport/knowledge-center/cognito-unable-to-verify-secret-hash/ # noqa message = bytes(username + environment.COGNITO_CLIENT_ID, 'utf-8') diff --git a/ecs/jskult-webapp/src/services/master_mainte_service.py b/ecs/jskult-webapp/src/services/master_mainte_service.py index f3bbdb1a..6f612a87 100644 --- a/ecs/jskult-webapp/src/services/master_mainte_service.py +++ b/ecs/jskult-webapp/src/services/master_mainte_service.py @@ -19,6 +19,7 @@ from src.repositories.mst_inst_repository import MstInstRepository from src.repositories.bu_master_cd_repository import BuMasterRepository from src.repositories.emp_master_repository import EmpMasterRepository from src.repositories.emp_chg_inst_repository import EmpChgInstRepository +from src.repositories.generic_kbn_mst_repository import GenericKbnMstRepository from src.model.internal.master_mainte_csv import MasterMainteCSVItems from src.model.internal.master_mainte_emp_chg_inst_function import NewEmpChgInstFunction from src.model.internal.master_mainte_emp_chg_inst_function import ChangeEmpChgInstFunction @@ -38,6 +39,7 @@ class MasterMainteService(BaseService): 'emp_master_repository': EmpMasterRepository, 'bu_master_repository': BuMasterRepository, 'emp_chginst_repository': EmpChgInstRepository, + 'generic_kbn_mst_repository': GenericKbnMstRepository, } CLIENTS = { @@ -48,6 +50,7 @@ class MasterMainteService(BaseService): emp_master_repository: EmpMasterRepository bu_master_repository: BuMasterRepository emp_chginst_repository: EmpChgInstRepository + generic_kbn_mst_repository: GenericKbnMstRepository s3_client: S3Client def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None: @@ -56,6 +59,7 @@ class MasterMainteService(BaseService): self.emp_master_repository = repositories['emp_master_repository'] self.bu_master_repository = repositories['bu_master_repository'] self.emp_chginst_repository = repositories['emp_chginst_repository'] + self.generic_kbn_mst_repository = repositories['generic_kbn_mst_repository'] self.s3_client = clients['s3_client'] def prepare_mainte_csv_up_view(self, @@ -77,7 +81,8 @@ class MasterMainteService(BaseService): self.mst_inst_repository, self.emp_master_repository, self.bu_master_repository, - self.emp_chginst_repository + self.emp_chginst_repository, + self.generic_kbn_mst_repository ) error_message_list = [] @@ -148,8 +153,8 @@ class MasterMainteService(BaseService): def copy_data_real_to_dummy(self) -> TableOverrideViewModel: try: - self.emp_chginst_repository.to_jst() self.emp_chginst_repository.begin() + self.emp_chginst_repository.to_jst() self.emp_chginst_repository.delete_dummy_table() self.emp_chginst_repository.copy_real_to_dummy() self.emp_chginst_repository.commit() diff --git a/ecs/jskult-webapp/src/system_var/constants.py b/ecs/jskult-webapp/src/system_var/constants.py index 3756facb..c3d5f4ec 100644 --- a/ecs/jskult-webapp/src/system_var/constants.py +++ b/ecs/jskult-webapp/src/system_var/constants.py @@ -63,6 +63,7 @@ LOGOUT_REASON_BACKUP_PROCESSING = 'dump_processing' LOGOUT_REASON_NOT_LOGIN = 'not_login' LOGOUT_REASON_DB_ERROR = 'db_error' LOGOUT_REASON_UNEXPECTED = 'unexpected' +LOGOUT_REASON_LOGIN_FAILED_LIMIT_EXCEEDED = 'login_failed_limit_exceeded' LOGOUT_REASON_MESSAGE_MAP = { LOGOUT_REASON_DO_LOGOUT: 'Logoutしました。', @@ -72,7 +73,8 @@ LOGOUT_REASON_MESSAGE_MAP = { LOGOUT_REASON_BACKUP_PROCESSING: 'バックアップ取得を開始しました。
日次バッチ更新が終了するまでマスターメンテは使用できません', LOGOUT_REASON_NOT_LOGIN: 'Loginしてからページにアクセスしてください。', LOGOUT_REASON_DB_ERROR: 'DB接続に失敗しました。
再度Loginするか、
管理者にお問い合わせください。', - LOGOUT_REASON_UNEXPECTED: '予期しないエラーが発生しました。
再度Loginするか、
管理者に問い合わせてください。' + LOGOUT_REASON_UNEXPECTED: '予期しないエラーが発生しました。
再度Loginするか、
管理者に問い合わせてください。', + LOGOUT_REASON_LOGIN_FAILED_LIMIT_EXCEEDED: 'ログイン失敗回数の上限を超えましたので
アカウントをロックしました。
管理者に連絡してください' } # 新規施設担当者登録CSV(マスターメンテ) @@ -80,6 +82,7 @@ NEW_INST_EMP_CSV_LOGICAL_NAMES = [ '施設コード', '施設名', '領域コード', + '担当者種別コード', 'MUID', '担当者名(姓)', '担当者名(名)', @@ -93,18 +96,20 @@ CSV_NEW_INST_CD_COL_NO = 0 CSV_NEW_INST_NAME_COL_NO = 1 # 領域コードの列No CSV_NEW_TA_CD_COL_NO = 2 +# 担当者種別コードの列No +CSV_NEW_EMP_CHG_TYPE_CD_COL_NO = 3 # MUIDの列No -CSV_NEW_EMP_CD_COL_NO = 3 +CSV_NEW_EMP_CD_COL_NO = 4 # 担当者名(姓)の列No -CSV_NEW_EMP_NAME_FAMILY_COL_NO = 4 +CSV_NEW_EMP_NAME_FAMILY_COL_NO = 5 # 担当者名(名)の列No -CSV_NEW_EMP_NAME_FIRST_COL_NO = 5 +CSV_NEW_EMP_NAME_FIRST_COL_NO = 6 # ビジネスユニットコードの列No -CSV_NEW_BU_CD_COL_NO = 6 +CSV_NEW_BU_CD_COL_NO = 7 # 適用開始日の列No -CSV_NEW_START_DATE = 7 +CSV_NEW_START_DATE = 8 # 適用終了日の列No -CSV_NEW_END_DATE = 8 +CSV_NEW_END_DATE = 9 # 施設担当者変更登録CSV(マスターメンテ) CHANGE_INST_CSV_LOGICAL_NAMES = [ @@ -116,6 +121,7 @@ CHANGE_INST_CSV_LOGICAL_NAMES = [ '施設名', '領域コード', '説明', + '担当者種別コード', 'MUID', '担当者名', '施設担当_開始日', @@ -139,18 +145,20 @@ CSV_CHANGE_INST_NAME_COL_NO = 5 CSV_CHANGE_TA_CD_COL_NO = 6 # 説明の列No CSV_CHANGE_EXPLAIN_COL_NO = 7 +# 担当者種別コード +CSV_CHANGE_EMP_CHG_TYPE_CD_COL_NO = 8 # MUIDの列No -CSV_CHANGE_EMP_CD_COL_NO = 8 +CSV_CHANGE_EMP_CD_COL_NO = 9 # 担当者名の列No -CSV_CHANGE_EMP_FULL_NAME_COL_NO = 9 +CSV_CHANGE_EMP_FULL_NAME_COL_NO = 10 # 施設担当_開始日の列No -CSV_CHANGE_INST_EMP_START_DATE_COL_NO = 10 +CSV_CHANGE_INST_EMP_START_DATE_COL_NO = 11 # 施設担当_終了日の列No -CSV_CHANGE_INST_EMP_END_DATE_COL_NO = 11 +CSV_CHANGE_INST_EMP_END_DATE_COL_NO = 12 # 終了日の変更の列No -CSV_CHANGE_CHANGE_END_DATE_COL_NO = 12 +CSV_CHANGE_CHANGE_END_DATE_COL_NO = 13 # コメントの列No -CSV_CHANGE_COMMENT = 13 +CSV_CHANGE_COMMENT = 14 # CSVアップロードテーブル名(マスターメンテ) CSV_REAL_TABLE_NAME = '本番テーブル' @@ -162,6 +170,7 @@ MENTE_CSV_DOWNLOAD_EXTRACT_COLUMNS = [ 'inst_cd', 'inst_name', 'ta_cd', + 'emp_chg_type_cd', 'emp_cd', 'emp_name_full', 'bu_cd', @@ -178,6 +187,7 @@ MENTE_CSV_DOWNLOAD_HEADER = [ '施設コード', '施設名', '領域コード', + '担当者種別コード', 'MUID', '担当者名', 'ビジネスユニットコード', @@ -207,3 +217,6 @@ DISPLAY_USER_STOP_DIV_SHORT = { '03': '特定項目停止', '04': '全DM停止' } + +# ログイン失敗回数上限(保守ユーザー) +LOGIN_FAIL_LIMIT = 10 \ No newline at end of file diff --git a/ecs/jskult-webapp/src/templates/instEmpCsvDL.html b/ecs/jskult-webapp/src/templates/instEmpCsvDL.html index 93ecf332..b0b50fe8 100644 --- a/ecs/jskult-webapp/src/templates/instEmpCsvDL.html +++ b/ecs/jskult-webapp/src/templates/instEmpCsvDL.html @@ -81,6 +81,17 @@ + + + 担当者種別コード: + + + + + 適用期間内: @@ -117,7 +128,7 @@ > - + 対象テーブル: @@ -160,7 +171,7 @@ > - +