diff --git a/io.sc.platform.developer.doc/asciidoc/9999-appendix/appendix.adoc b/io.sc.platform.developer.doc/asciidoc/9999-appendix/appendix.adoc index 172f03ed..c1b4732a 100644 --- a/io.sc.platform.developer.doc/asciidoc/9999-appendix/appendix.adoc +++ b/io.sc.platform.developer.doc/asciidoc/9999-appendix/appendix.adoc @@ -12,5 +12,6 @@ include::mac/mac.adoc[] include::linux/linux.adoc[] include::oauth2/oauth2.adoc[] include::java/java.adoc[] +include::rwa/rwa.adoc[] diff --git a/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/rwa.adoc b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/rwa.adoc new file mode 100644 index 00000000..0a8ee504 --- /dev/null +++ b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/rwa.adoc @@ -0,0 +1,12 @@ +[appendix] += RWA +2017年12月,国际巴塞尔委员会发布《巴塞尔协议Ⅲ终版》,明确了信息披露的内容、模板和表格、披露频率等, +并要求各成员国于2023年1月1日正式启动实施。中国银保监会 (现金监局)积极对接国际监管, +于2023年2月18日发布了《巴III的落地监管要求商业银行资本管理办法(征求意见稿)》(下称“征求意见稿”或“资本新规”), +并明确正式实施时间为2024年1月1日。资本新规重构了商业银行风险加权资产的计量框架, +计量要求,较现行《商业银行资本管理办法(试行)》(2012年发布)提出更多精细化规定, +同时对银行数据治理、IT系统建设、业务流程改造、监管信息披露、内部资本管理等方面均提出了更高的要求。 + +include::信用风险/信用风险.adoc[leveloffset=+1] +include::操作风险/操作风险.adoc[leveloffset=+1] +include::市场风险/市场风险.adoc[leveloffset=+1] diff --git a/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/信用风险/信用风险.adoc b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/信用风险/信用风险.adoc new file mode 100644 index 00000000..e69de29b diff --git a/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/市场风险/市场风险.adoc b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/市场风险/市场风险.adoc new file mode 100644 index 00000000..88f083a1 --- /dev/null +++ b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/市场风险/市场风险.adoc @@ -0,0 +1,94 @@ += 市场风险 +== 简化标准法资本计量方案 +简化标准法下,商业银行市场风险资本要求计算规则较现行标准法新增了监管指定系数, +通过对各类风险资本要求附加不同监管系数,以适当提高市场风险资本要求。 + +简化标准法下市场风险资本要求=利率风险资本要求(含利率类期权资本要求)×1.3+汇率风险资本要求(含汇率类期权资本要求)×1.2+商品风险资本要求(含商品类期权资本要求)×1.9+股票风险资本要求(含股票类期权资本要求)×3.5。 +其中,利率风险资本要求和股票风险资本要求为一般市场风险资本要求和特定风险资本要求之和。 +期权风险资本要求纳入其标的对应风险类别进行资本要求汇总。 +市场风险资本要求乘以12.5倍,得到简化标准法下市场风险加权资产。 + +按照现行资本管理办法,使用简化标准法计量市场风险资本需填报报表G4C-1,为市场风险资本汇总表: + +image::9999-appendix/rwa/market-risk/001.png[,50%] + +本报表为G4C-1(a)至G4C-1(l)报表结果汇总表。 + +=== 简化标准法采用前提 +相较于采用现行标准法计量市场风险资本,监管在资本新规中,明确了对使用简化标准法计量市场风险资本要求的商业银行应同时满足的条件: + +* 简化标准法下,市场风险加权资产不超过150亿元; +* 非中央交易对手衍生工具的名义本金(全账簿)不超过4000亿元; +* 银行及其任何附属子公司未使用内部模型法计量市场风险资本要求; +* 非全球系统重要性银行(G-SIB)或国内系统重要性银行(D-SIB); +* 未持有任何相关性交易头寸; + +相关性交易是指同时符合以下条件的证券化头寸,以及对冲上述头寸的非证券化工具: + +. 符合附件11定义的证券化头寸,但不含再证券化头寸和不按比例获得分档收益的证券化衍生工具; +. 证券化头寸的参考实体为单名产品,包括流动市场中的单名信用衍生工具,以及参考实体的交易指数; +. 证券化头寸的基础资产不包括以下风险暴露: 符合附件2标准的个人风险暴露、居住用房地产风险暴露、商用房地产风险暴露; +. 证券化头寸的参考实体非特殊目的实体。 + +=== 计量范围 +根据银行并表口径下目前已开展的金融市场业务的产品清单,以及相应账簿划分现状、账簿划分建议、是否纳入市场风险资本计量范围整理, 示例如下: + +image::9999-appendix/rwa/market-risk/002.png[,50%] + +=== 利率风险 +利率风险是指交易账簿中的债券(固定利率和浮动利率债券、央行票据、可转让存单、不可转换优先股及按照债券交易规则进行交易的可转换债券等) +、利率及债券衍生工具头寸所涉及市场利率变动的不确定性造成损失的可能性。 +除债券外,以下类型的交易需要进一步计算利率风险: + +* 买断式逆回购交易,由于属于形成信用净空头的头寸,应该划分至交易账簿,具体的计量方法详见 <<买断式逆回购>>。 +* 采取包销方式承销债券产生的头寸、交易账簿中的信用衍生产品头寸,也应计提利率风险,具体的计量方法详见 <<承销包销业务处理>>。 +* 基金在穿透后基本属于利率类产品,若划分至交易账簿,需要计算利率风险,详见 <<基金>>。若穿透后基金底层涉及股票风险,计量方法可以参考 <<汇率风险>>。 + +利率衍生工具包括受利率变化影响的衍生金融工具,如:利率期货、远期利率协议、利率互换及交叉货币互换合约、利率期权及远期外汇头寸。 +债券衍生工具包括债券的远期、期货和债券期权。 +衍生工具应转换为基础工具,并按基础工具的特定市场风险和一般市场风险的方法计算资本要求。 +利率和货币互换、远期利率协议、远期外汇合约、利率期货及利率指数期货不必计算特定市场风险的资本要求; +如果期货合约的基础工具是债券或代表债券组合的指数,则应根据发行主体的信用风险计算特定市场风险资本要求. + +利率风险的资本要求包括特定市场风险和一般市场风险的资本要求两部分。具体的计量方法详见下文: + +[[一般债券]] +==== 一般债券 +===== 特定风险 + +===== 一般市场风险 +====== 到期日法 +====== 久期法 +====== 到期日法计量示例 + + + +[[买断式逆回购]] +==== 买断式逆回购 + + +[[承销包销业务处理]] +==== 承销包销业务处理 + + +[[基金]] +==== 基金 + + +[[汇率风险]] +=== 汇率风险 + +[[股票风险]] +=== 股票风险 + +[[商品风险]] +=== 商品风险 + +[[期权风险]] +=== 期权风险 + +[[交易账簿信用衍生品计量]] +=== 交易账簿信用衍生品计量 + +[[交易账簿证券化风险暴露的特定风险]] +=== 交易账簿证券化风险暴露的特定风险 diff --git a/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/操作风险/操作风险.adoc b/io.sc.platform.developer.doc/asciidoc/9999-appendix/rwa/操作风险/操作风险.adoc new file mode 100644 index 00000000..e69de29b diff --git a/io.sc.platform.developer.doc/asciidoc/index.adoc b/io.sc.platform.developer.doc/asciidoc/index.adoc index c2b82afd..85472404 100644 --- a/io.sc.platform.developer.doc/asciidoc/index.adoc +++ b/io.sc.platform.developer.doc/asciidoc/index.adoc @@ -2,9 +2,9 @@ :backend: html5 :toc: right :toc-title: 目录 -:toclevels: 5 +:toclevels: 7 :sectnums: -:sectnumlevels: 5 +:sectnumlevels: 7 :sectanchors: :appendix-caption: 附录 diff --git a/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/001.png b/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/001.png new file mode 100644 index 00000000..22ad9492 Binary files /dev/null and b/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/001.png differ diff --git a/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/002.png b/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/002.png new file mode 100644 index 00000000..d30ad03f Binary files /dev/null and b/io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/rwa/market-risk/002.png differ diff --git a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryParameter.java b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryParameter.java index 4c76659f..054a5b4e 100644 --- a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryParameter.java +++ b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryParameter.java @@ -23,16 +23,16 @@ public class QueryParameter { protected String exportFilename; //导出文件名 public Pageable getJpaPageable(){ - if(firstPage==1){ //如果起始页从1开始,真实起始页需减一 - page =page>0? page-1 : 0; - }else{ - page =page>=0? page : 0; + page =page<0? 0 : page; // 最小设置为 0 + if(firstPage==1){ + page =page<1? 1 : page; // 首页从1开始的,最小值为 1 } Sort sort =getSort(); + int jpaPage =firstPage==1? page-1: page; if(sort!=null){ - return PageRequest.of(page,size,sort); + return PageRequest.of(jpaPage,size,sort); }else{ - return PageRequest.of(page,size); + return PageRequest.of(jpaPage,size); } } diff --git a/io.sc.platform.system.frontend/src/views/user/User.vue b/io.sc.platform.system.frontend/src/views/user/User.vue index c1b7334d..3cd5db22 100644 --- a/io.sc.platform.system.frontend/src/views/user/User.vue +++ b/io.sc.platform.system.frontend/src/views/user/User.vue @@ -24,7 +24,7 @@ 'separator', { name: 'setPassword', - label: t('system.user.action.setPassword'), + label: $t('system.user.action.setPassword'), icon: 'bi-shield-check', enableIf: function (selecteds) { return selecteds.length > 0; @@ -35,7 +35,7 @@ }, { name: 'setAllPassword', - label: t('system.user.action.setAllPassword'), + label: $t('system.user.action.setAllPassword'), icon: 'bi-shield', enableIf: function (selecteds) { return true; @@ -47,7 +47,7 @@ 'separator', { name: 'resetPassword', - label: t('system.user.action.resetPassword'), + label: $t('system.user.action.resetPassword'), icon: 'bi-shield-fill-check', enableIf: function (selecteds) { return selecteds.length > 0; @@ -63,16 +63,16 @@ }, { name: 'resetAllPassword', - label: t('system.user.action.resetAllPassword'), + label: $t('system.user.action.resetAllPassword'), icon: 'bi-shield-fill', enableIf: function (selecteds) { return true; }, click: function () { - DialogManager.confirm(t('system.user.confirm.resetAllPassword'), () => { + DialogManager.confirm($t('system.user.confirm.resetAllPassword'), () => { axios.post(Environment.apiContextPath('/api/system/user/resetAllPassword')).then(() => { setPasswordDialogRef.value.hide(); - NotifyManager.info(t('operationSuccess')); + NotifyManager.info($t('operationSuccess')); }); }); }, @@ -84,11 +84,11 @@ :data-url="Environment.apiContextPath('/api/system/user')" row-key="id" :columns="[ - { name: 'loginName', label: t('loginName') }, - { name: 'userName', label: t('userName') }, + { name: 'loginName', label: $t('loginName') }, + { name: 'userName', label: $t('userName') }, { name: 'status', - label: t('status'), + label: $t('status'), format: (value, row) => { return { componentType: UserStatusTag, @@ -96,9 +96,9 @@ }; }, }, - { name: 'dataComeFrom', label: t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, - { name: 'lastModifier', label: t('lastModifier') }, - { name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() }, + { name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, + { name: 'lastModifier', label: $t('lastModifier') }, + { name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() }, ]" :editor="{ dialog: { @@ -108,33 +108,33 @@ form: { colsNum: 4, fields: [ - { name: 'loginName', label: t('loginName'), type: 'text', required: true, colspan: 4 }, - { name: 'userName', label: t('userName'), type: 'text', required: true, colspan: 4 }, - { name: 'description', label: t('description'), type: 'textarea', rows: 1, colspan: 4 }, - { name: 'password', label: t('password'), type: 'password', colspan: 4 }, + { name: 'loginName', label: $t('loginName'), type: 'text', required: true, colspan: 4 }, + { name: 'userName', label: $t('userName'), type: 'text', required: true, colspan: 4 }, + { name: 'description', label: $t('description'), type: 'textarea', rows: 1, colspan: 4 }, + { name: 'password', label: $t('password'), type: 'password', colspan: 4 }, { name: 'confirmPassword', - label: t('confirmPassword'), + label: $t('confirmPassword'), type: 'password', colspan: 4, rules: [ (value) => { return Tools.stringEquals(userGridRef.getAddEditFormRef().value.getData().password, value) ? true - : t('passwordAndConfirmPasswordMustEqual'); + : $t('passwordAndConfirmPasswordMustEqual'); }, ], }, - { name: 'mobile', label: t('mobile'), type: 'text', colsFirst: true, colspan: 4 }, - { name: 'phone', label: t('phone'), type: 'text', colsFirst: true, colspan: 4 }, - { name: 'email', label: t('email'), type: 'text', colsFirst: true, colspan: 4 }, - { name: 'weixin', label: t('weixin'), type: 'text', colsFirst: true, colspan: 4 }, - { name: 'qq', label: t('qq'), type: 'text', colsFirst: true, colspan: 4 }, + { name: 'mobile', label: $t('mobile'), type: 'text', colsFirst: true, colspan: 4 }, + { name: 'phone', label: $t('phone'), type: 'text', colsFirst: true, colspan: 4 }, + { name: 'email', label: $t('email'), type: 'text', colsFirst: true, colspan: 4 }, + { name: 'weixin', label: $t('weixin'), type: 'text', colsFirst: true, colspan: 4 }, + { name: 'qq', label: $t('qq'), type: 'text', colsFirst: true, colspan: 4 }, - { name: 'enable', label: t('enable'), type: 'checkbox', defaultValue: true }, - { name: 'accountExpired', label: t('accountExpired'), type: 'checkbox', defaultValue: false }, - { name: 'accountLocked', label: t('accountLocked'), type: 'checkbox', defaultValue: false }, - { name: 'credentialsExpired', label: t('credentialsExpired'), type: 'checkbox', defaultValue: false }, + { name: 'enable', label: $t('enable'), type: 'checkbox', defaultValue: true }, + { name: 'accountExpired', label: $t('accountExpired'), type: 'checkbox', defaultValue: false }, + { name: 'accountLocked', label: $t('accountLocked'), type: 'checkbox', defaultValue: false }, + { name: 'credentialsExpired', label: $t('credentialsExpired'), type: 'checkbox', defaultValue: false }, ], }, }" @@ -142,39 +142,34 @@ panel: { columnNum: 1, fields: [ - { name: 'id', label: t('id') }, - { name: 'loginName', label: t('loginName') }, - { name: 'userName', label: t('userName') }, - { name: 'description', label: t('description') }, - { name: 'enable', label: t('enable'), format: Formater.yesNo() }, - { name: 'accountExpired', label: t('accountExpired'), format: Formater.yesNo() }, - { name: 'accountLocked', label: t('accountLocked'), format: Formater.yesNo() }, - { name: 'credentialsExpired', label: t('credentialsExpired'), format: Formater.yesNo() }, - { name: 'email', label: t('email') }, - { name: 'phone', label: t('phone') }, - { name: 'mobile', label: t('mobile') }, - { name: 'weixin', label: t('weixin') }, - { name: 'qq', label: t('qq') }, - { name: 'dataComeFrom', label: t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, - { name: 'creator', label: t('creator') }, - { name: 'createDate', label: t('createDate') }, - { name: 'lastModifier', label: t('lastModifier') }, - { name: 'lastModifyDate', label: t('lastModifyDate') }, - { name: 'corporationCode', label: t('corporationCode') }, + { name: 'id', label: $t('id') }, + { name: 'loginName', label: $t('loginName') }, + { name: 'userName', label: $t('userName') }, + { name: 'description', label: $t('description') }, + { name: 'enable', label: $t('enable'), format: Formater.yesNo() }, + { name: 'accountExpired', label: $t('accountExpired'), format: Formater.yesNo() }, + { name: 'accountLocked', label: $t('accountLocked'), format: Formater.yesNo() }, + { name: 'credentialsExpired', label: $t('credentialsExpired'), format: Formater.yesNo() }, + { name: 'email', label: $t('email') }, + { name: 'phone', label: $t('phone') }, + { name: 'mobile', label: $t('mobile') }, + { name: 'weixin', label: $t('weixin') }, + { name: 'qq', label: $t('qq') }, + { name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, + { name: 'creator', label: $t('creator') }, + { name: 'createDate', label: $t('createDate') }, + { name: 'lastModifier', label: $t('lastModifier') }, + { name: 'lastModifyDate', label: $t('lastModifyDate') }, + { name: 'corporationCode', label: $t('corporationCode') }, ], }, }" @row-click=" (evt, row, index) => { + console.log(row.id); currentSelectedUserId = row.id; - if (roleGridRef) { - roleGridRef.setFetchDataUrl(Environment.apiContextPath('/api/system/role/queryRolesByUser?userId=') + row.id); - roleGridRef.refresh(); - } - if (orgTreeGridRef) { - orgTreeGridRef.setFetchDataUrl(Environment.apiContextPath('/api/system/org/listAllOrgsWithSelectedStatusByUser?userId=') + row.id); - orgTreeGridRef.refresh(); - } + roleGridRef?.refresh(); + orgTreeGridRef?.refresh(); } " > @@ -215,7 +210,7 @@ }, { name: 'addAllRole', - label: t('system.role.action.addAllRole'), + label: $t('system.role.action.addAllRole'), enableIf: () => { if (userGridRef) { return userGridRef.getSelectedRows().length > 0; @@ -225,7 +220,7 @@ click: () => { const selectedUser = userGridRef.getSelectedRows()[0]; DialogManager.confirm( - t('system.role.action.addAllRole.confirm', { userLoginName: selectedUser.loginName, userName: selectedUser.userName }), + $t('system.role.action.addAllRole.confirm', { userLoginName: selectedUser.loginName, userName: selectedUser.userName }), () => { axios .post(Environment.apiContextPath('/api/system/user/addAllRoles'), { @@ -244,7 +239,7 @@ 'separator', { name: 'removeRole', - label: t('system.role.action.removeRole'), + label: $t('system.role.action.removeRole'), enableIf: () => { if (userGridRef && roleGridRef) { return userGridRef.getSelectedRows().length > 0 && roleGridRef.getSelectedRows().length > 0; @@ -315,7 +310,7 @@ { width: 60, name: 'status', - label: t('status'), + label: $t('status'), format: (value, row) => { return { componentType: RoleStatusTag, @@ -328,17 +323,17 @@ panel: { columnNum: 1, fields: [ - { name: 'id', label: t('id') }, - { name: 'code', label: t('code') }, - { name: 'name', label: t('name') }, - { name: 'description', label: t('description') }, - { name: 'enable', label: t('enable'), format: Formater.yesNo() }, - { name: 'dataComeFrom', label: t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, - { name: 'creator', label: t('creator') }, - { name: 'createDate', label: t('createDate') }, - { name: 'lastModifier', label: t('lastModifier') }, - { name: 'lastModifyDate', label: t('lastModifyDate') }, - { name: 'corporationCode', label: t('corporationCode') }, + { name: 'id', label: $t('id') }, + { name: 'code', label: $t('code') }, + { name: 'name', label: $t('name') }, + { name: 'description', label: $t('description') }, + { name: 'enable', label: $t('enable'), format: Formater.yesNo() }, + { name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, + { name: 'creator', label: $t('creator') }, + { name: 'createDate', label: $t('createDate') }, + { name: 'lastModifier', label: $t('lastModifier') }, + { name: 'lastModifyDate', label: $t('lastModifyDate') }, + { name: 'corporationCode', label: $t('corporationCode') }, ], }, }" @@ -359,7 +354,14 @@ { name: 'save', label: $t('save'), - click: () => {}, + click: () => { + axios + .post(Environment.apiContextPath('/api/system/user/updateOrgs'), { + one: currentSelectedUserId, + many: orgTreeGridRef.value.getTicked(), + }) + .then((response) => {}); + }, }, 'view', ]" @@ -375,7 +377,7 @@ { width: 60, name: 'status', - label: t('status'), + label: $t('status'), format: (value, row) => { return { componentType: RoleStatusTag, @@ -388,17 +390,17 @@ panel: { columnNum: 1, fields: [ - { name: 'id', label: t('id') }, - { name: 'code', label: t('code') }, - { name: 'name', label: t('name') }, - { name: 'description', label: t('description') }, - { name: 'enable', label: t('enable'), format: Formater.yesNo() }, - { name: 'dataComeFrom', label: t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, - { name: 'creator', label: t('creator') }, - { name: 'createDate', label: t('createDate') }, - { name: 'lastModifier', label: t('lastModifier') }, - { name: 'lastModifyDate', label: t('lastModifyDate') }, - { name: 'corporationCode', label: t('corporationCode') }, + { name: 'id', label: $t('id') }, + { name: 'code', label: $t('code') }, + { name: 'name', label: $t('name') }, + { name: 'description', label: $t('description') }, + { name: 'enable', label: $t('enable'), format: Formater.yesNo() }, + { name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) }, + { name: 'creator', label: $t('creator') }, + { name: 'createDate', label: $t('createDate') }, + { name: 'lastModifier', label: $t('lastModifier') }, + { name: 'lastModifyDate', label: $t('lastModifyDate') }, + { name: 'corporationCode', label: $t('corporationCode') }, ], }, }" @@ -413,15 +415,12 @@