diff --git a/pc4mobx/hrmSalary/apis/datapush.js b/pc4mobx/hrmSalary/apis/datapush.js index 3b2adc5c..fb26cf9b 100644 --- a/pc4mobx/hrmSalary/apis/datapush.js +++ b/pc4mobx/hrmSalary/apis/datapush.js @@ -24,3 +24,23 @@ export const savePushItemList = (params) => { export const deletePushItemList = (params) => { return WeaTools.callApi("/api/bs/hrmsalary/push/item/delete", "GET", params); }; +// 推送记录列表 +export const getPushRecordList = (params) => { + return postFetch("/api/bs/hrmsalary/push/record/list", params); +}; +// 推送记录详细列表 +export const getPushRecordDetail = (params) => { + return postFetch("/api/bs/hrmsalary/push/record/detail", params); +}; +// 推送记录-推送 +export const pushRecords = (params) => { + return postFetch("/api/bs/hrmsalary/push/record/push", params); +}; +// 推送记录-撤回 +export const withdrawRecords = (params) => { + return postFetch("/api/bs/hrmsalary/push/record/withdraw", params); +}; +//创建推送记录 +export const createPushRecords = (params) => { + return postFetch("/api/bs/hrmsalary/push/record/create", params); +}; diff --git a/pc4mobx/hrmSalary/apis/mySalaryBenefits.js b/pc4mobx/hrmSalary/apis/mySalaryBenefits.js index f197ba27..75e3eb73 100644 --- a/pc4mobx/hrmSalary/apis/mySalaryBenefits.js +++ b/pc4mobx/hrmSalary/apis/mySalaryBenefits.js @@ -45,11 +45,13 @@ export const doSecondAuth = (params, headers) => { export const getPasswordForm = params => { return WeaTools.callApi("/api/hrm/secondarypwd/getPasswordForm", "GET", params); }; -export const checkPassword = params => { - return WeaTools.callApi("/api/hrm/secondarypwd/checkPassword", "POST", params); +export const checkPassword = (params, headers) => { + return formHeaderPost("/api/hrm/secondarypwd/checkPassword", "POST", params, headers); + // return WeaTools.callApi("/api/hrm/secondarypwd/checkPassword", "POST", params); }; -export const saveSecondaryPwd = params => { - return WeaTools.callApi("/api/hrm/secondarypwd/saveSecondaryPwd", "POST", params); +export const saveSecondaryPwd = (params, headers) => { + return formHeaderPost("/api/hrm/secondarypwd/saveSecondaryPwd", "POST", params, headers); + // return WeaTools.callApi("/api/hrm/secondarypwd/saveSecondaryPwd", "POST", params); }; export const salaryBillGetToken = params => { return postFetch("/api/bs/hrmsalary/salaryBill/getToken", params); diff --git a/pc4mobx/hrmSalary/apis/payroll.js b/pc4mobx/hrmSalary/apis/payroll.js index 48fcecb2..fdef4e0e 100644 --- a/pc4mobx/hrmSalary/apis/payroll.js +++ b/pc4mobx/hrmSalary/apis/payroll.js @@ -202,26 +202,38 @@ export const salaryBillSendSum = (params) => { return postFetch("/api/bs/hrmsalary/salaryBill/send/sum", params); }; //工资单发放-发送短信验证码 -export const sendMobileCode = (params) => { - return postFetch("/api/bs/hrmsalary/salaryBill/sendMobileCode", params); +export const sendMobileCode = (params, header = {}) => { + return postFetch("/api/bs/hrmsalary/salaryBill/sendMobileCode", params, header); +}; +//工资单发放-发送短信验证码 +export const checkMobileCode = (params, header = {}) => { + return postFetch("/api/bs/hrmsalary/salaryBill/checkMobileCode", params, header); }; //工资单-验证方式 export const payrollCheckType = params => { return WeaTools.callApi("/api/bs/hrmsalary/salaryBill/payrollCheckType", "GET", params); }; //工资单-反馈验证 -export const feedBackSalaryBill = params => { +export const feedBackSalaryBill = async params => { const { header, ...payload } = params; - return fetch(`/api/bs/hrmsalary/salaryBill/feedBackSalaryBill?${convertToUrlString(payload)}`, { + const res = await fetch(`/api/bs/hrmsalary/salaryBill/feedBackSalaryBill?${convertToUrlString(payload)}`, { method: "GET", mode: "cors", headers: { "Content-Type": "application/json", ...header } - }).then(res => res.json()); + }); + return await res.json(); // return WeaTools.callApi("/api/bs/hrmsalary/salaryBill/feedBackSalaryBill", "GET", params); }; //工资单-确认 -export const confirmSalaryBill = params => { - return WeaTools.callApi("/api/bs/hrmsalary/salaryBill/confirmSalaryBill", "GET", params); +export const confirmSalaryBill = async params => { + const { header, ...payload } = params; + const res = await fetch(`/api/bs/hrmsalary/salaryBill/confirmSalaryBill?${convertToUrlString(payload)}`, { + method: "GET", + mode: "cors", + headers: { "Content-Type": "application/json", ...header } + }); + return await res.json(); + // return WeaTools.callApi("/api/bs/hrmsalary/salaryBill/confirmSalaryBill", "GET", params); }; // 工资单基础设置-获取设置列表 diff --git a/pc4mobx/hrmSalary/components/FormInfo/index.js b/pc4mobx/hrmSalary/components/FormInfo/index.js index d6940489..d0d87963 100644 --- a/pc4mobx/hrmSalary/components/FormInfo/index.js +++ b/pc4mobx/hrmSalary/components/FormInfo/index.js @@ -67,7 +67,7 @@ export default class FormInfo extends Component { } } coms != null && formItems.push({ - com: ({coms}), + com: ({coms}), hide: field.hide, col }); diff --git a/pc4mobx/hrmSalary/components/captchaModal/index.js b/pc4mobx/hrmSalary/components/captchaModal/index.js index a7f00b9b..87da6745 100644 --- a/pc4mobx/hrmSalary/components/captchaModal/index.js +++ b/pc4mobx/hrmSalary/components/captchaModal/index.js @@ -6,31 +6,32 @@ */ import React, { Component } from "react"; import { WeaDialog, WeaError, WeaFormItem, WeaInput, WeaLocaleProvider, WeaSearchGroup } from "ecCom"; -import { sendMobileCode } from "../../apis/payroll"; -import { Button } from "antd"; +import { WeaForm, WeaSwitch } from "comsMobx"; +import { checkMobileCode, sendMobileCode } from "../../apis/payroll"; +import { getQueryString } from "../../util/url"; +import FormInfo from "../FormInfo"; +import { captchaCondition } from "../../pages/mobilePayroll/pwdCondtion"; +import MobileModal from "../../pages/mobilePayroll/mobileModal"; +import { Button, message } from "antd"; import "./index.less"; +const form = new WeaForm(); const { getLabel } = WeaLocaleProvider; class Index extends Component { constructor(props) { super(props); - this.state = { - captcha: "", - time: 60 - }; + this.state = { captcha: "", time: 60 }; this.timeRef = null; } + componentDidMount() { + form.initFormFields(captchaCondition); + } + componentWillUnmount() { clearInterval(this.timeRef); - } - - componentWillReceiveProps(nextProps, nextContext) { - if (nextProps.visible !== this.props.visible && !nextProps.visible) { - clearInterval(this.timeRef); - this.setState({ captcha: "", time: 60 }); - } + this.setState({ captcha: "", time: 60 }); } handleSendCaptcha = () => { @@ -48,44 +49,73 @@ class Index extends Component { } }); }; - handleConfirm = () => { - if (!this.state.captcha) { + handleConfirm = async () => { + const type = getQueryString("type"), f = await form.validateForm(); + if (!this.state.captcha && type !== "phone") { this.refs.weaError.showError(); - // return + return; + } else if (!f.isValid && type === "phone") { + f.showErrors(); + return; } - this.props.onCancel(); - this.props.onConfirm(); + checkMobileCode({ id: this.props.id, mobileCode: this.state.captcha }).then(({ status, errormsg }) => { + if (status) { + this.props.onCancel(); + this.props.onConfirm(); + } else { + message.error(errormsg); + } + }); }; render() { - const { captcha, time } = this.state; - return ( - {getLabel(826, "确定")} - ]} - > - - - -
- this.setState({ captcha })}/> - -
-
-
-
-
+ const { captcha, time } = this.state, type = getQueryString("type"); + const itemRender = { + mobileCode: (field, textAreaProps, form, formParams) => { + return (
+ this.setState({ captcha: form.getFormParams().mobileCode })}/> + +
); + } + }; + return ( + { + type === "phone" ? + + : + {getLabel(826, "确定")} + ]} + > + + + +
+ this.setState({ captcha })}/> + +
+
+
+
+
+ } +
); } } diff --git a/pc4mobx/hrmSalary/components/captchaModal/index.less b/pc4mobx/hrmSalary/components/captchaModal/index.less index 4f8d23b7..2728112b 100644 --- a/pc4mobx/hrmSalary/components/captchaModal/index.less +++ b/pc4mobx/hrmSalary/components/captchaModal/index.less @@ -5,25 +5,25 @@ .wea-form-item-wrapper { .wea-error { width: 100%; - - .captchaInputBox { - display: flex; - align-items: center; - - .wea-input-normal { - flex: 1; - } - - button { - padding: 8px 10px; - border-radius: 0; - min-width: 80px; - text-align: center; - height: 30px; - line-height: 15px; - } - } } } } } + +.captchaInputBox { + display: flex; + align-items: center; + + .wea-input-normal { + flex: 1; + } + + button { + padding: 8px 10px; + border-radius: 0; + min-width: 80px; + text-align: center; + height: 30px; + line-height: 15px; + } +} diff --git a/pc4mobx/hrmSalary/pages/analysisOfSalaryStatistics/components/salaryDetails.js b/pc4mobx/hrmSalary/pages/analysisOfSalaryStatistics/components/salaryDetails.js index 1893e77e..1775ee97 100644 --- a/pc4mobx/hrmSalary/pages/analysisOfSalaryStatistics/components/salaryDetails.js +++ b/pc4mobx/hrmSalary/pages/analysisOfSalaryStatistics/components/salaryDetails.js @@ -166,7 +166,7 @@ class SalaryDetails extends Component { this.postMessageToChild({ dataSource, pageInfo, selectedRowKeys, showTotalCell, calcDetail: true, tableScrollHeight: 154, sumRow, columns: _.map(columns, (it, idx) => ({ - dataIndex: it.column || it.dataIndex, title: it.text || it.title, calcDetail: true, + dataIndex: it.column || it.dataIndex, title: it.text || it.title, calcDetail: true, showSee: false, width: (it.dataIndex === "taxAgent" || it.dataIndex === "salarySob") ? 176 : (it.width || it.oldWidth), fixed: (idx === 1 || idx === 0 || idx === 2) ? "left" : "", ellipsis: true diff --git a/pc4mobx/hrmSalary/pages/calculate/calculate.js b/pc4mobx/hrmSalary/pages/calculate/calculate.js index 5d7b516d..3a7b563d 100644 --- a/pc4mobx/hrmSalary/pages/calculate/calculate.js +++ b/pc4mobx/hrmSalary/pages/calculate/calculate.js @@ -6,7 +6,8 @@ */ import React, { Component } from "react"; import { inject, observer } from "mobx-react"; -import { WeaLocaleProvider, WeaTop } from "ecCom"; +import { WeaLocaleProvider, WeaTools, WeaTop } from "ecCom"; +import { WeaForm } from "comsMobx"; import { Button, message, Modal } from "antd"; import moment from "moment"; import CalculateQuery from "./components/calculateQuery"; @@ -15,9 +16,15 @@ import CalculateDialog from "./components/calculateDialog"; import ProgressModal from "../../components/progressModal"; import LogDialog from "../../components/logViewModal"; import { backCalculate, deleteSalaryacct, fileSalaryAcct, reAccounting } from "../../apis/calculate"; +import FormInfo from "../../components/FormInfo"; +import { queryConditions } from "./config"; +import { postFetch } from "../../util/request"; +import cs from "classnames"; import "./index.less"; +const getKey = WeaTools.getKey; const getLabel = WeaLocaleProvider.getLabel; +const form = new WeaForm(); @inject("calculateStore", "taxAgentStore") @observer @@ -31,17 +38,32 @@ class Calculate extends Component { moment(new Date()).subtract(1, "year").startOf("year").format("YYYY-MM"), moment(new Date()).endOf("year").format("YYYY-MM") ] - }, isRefresh: false, logDialogVisible: false, + }, isRefresh: false, logDialogVisible: false, conditions: [], progressModule: { visible: false, progress: 0, title: getLabel(111, "正在归档中请稍后") }, - calcDaialog: { visible: false, title: "" } + calcDaialog: { visible: false, title: "" }, showAdvance: false }; this.timer = null; this.handleDebounce = null; } + async componentDidMount() { + const { data } = await postFetch("/api/bs/hrmsalary/taxAgent/listAuth", { filterType: "QUERY_DATA" }); + this.setState({ + conditions: _.map(queryConditions, item => ({ + ...item, items: _.map(item.items, o => { + o = { ...o, label: getLabel(o.lanId, o.label) }; + if (getKey(o) === "taxAgentIds") { + return { ...o, options: _.map(data, k => ({ key: k.id + "", showname: k.name })) }; + } + return { ...o }; + }) + })) + }, () => form.initFormFields(this.state.conditions)); + } + renderCalculateOpts = () => { const { taxAgentStore: { PageAndOptAuth } } = this.props; - const { queryParams, isRefresh } = this.state; + const { queryParams, isRefresh, showAdvance } = this.state; const admin = PageAndOptAuth.opts.includes("admin"); let calculateOpts = [ , - this.setState({ - isRefresh: _.keys(v)[0] === "name" ? isRefresh : !isRefresh, - queryParams: { ...queryParams, ...v } - })} onSearch={() => this.setState({ isRefresh: !isRefresh })}/> + this.setState({ showAdvance: !showAdvance })} + onChange={v => this.setState({ + isRefresh: _.keys(v)[0] === "name" ? isRefresh : !isRefresh, + queryParams: { ...queryParams, ...v } + })} onSearch={() => this.setState({ isRefresh: !isRefresh })}/> ]; return !admin ? calculateOpts.slice(1) : calculateOpts; }; @@ -189,7 +212,9 @@ class Calculate extends Component { }; render() { - const { queryParams, isRefresh, calcDaialog, progressModule, logDialogVisible, filterConditions } = this.state; + const { + queryParams, isRefresh, calcDaialog, progressModule, logDialogVisible, filterConditions, conditions, showAdvance + } = this.state; return ( } iconBgcolor="#F14A2D" buttons={this.renderCalculateOpts()} className="calculate-main-layout" showDropIcon @@ -201,7 +226,18 @@ class Calculate extends Component { } ]}>
- +
+ +
+ + + +
+
+ this.setState({ calcDaialog: { ...calcDaialog, visible: false }, diff --git a/pc4mobx/hrmSalary/pages/calculate/components/calculateQuery/index.js b/pc4mobx/hrmSalary/pages/calculate/components/calculateQuery/index.js index 00cd202d..0c74ccd5 100644 --- a/pc4mobx/hrmSalary/pages/calculate/components/calculateQuery/index.js +++ b/pc4mobx/hrmSalary/pages/calculate/components/calculateQuery/index.js @@ -21,11 +21,14 @@ class Index extends Component { this.props.onChange({ dateRange: v })}/>
- this.props.onChange({ name: v })} - onSearch={this.props.onSearch} - /> +
+ this.props.onChange({ name: v })} + onSearch={this.props.onSearch} + /> + {getLabel(111, "高级搜索")} +
); } diff --git a/pc4mobx/hrmSalary/pages/calculate/components/calculateTablelist/index.js b/pc4mobx/hrmSalary/pages/calculate/components/calculateTablelist/index.js index 5059017f..0b4ea0a0 100644 --- a/pc4mobx/hrmSalary/pages/calculate/components/calculateTablelist/index.js +++ b/pc4mobx/hrmSalary/pages/calculate/components/calculateTablelist/index.js @@ -29,12 +29,14 @@ class Index extends Component { } getSalaryAcctList = (props) => { - const { pageInfo } = this.state; - const { queryParams } = props; + const { pageInfo } = this.state, { queryParams, form } = props; + const { taxAgentIds } = form.getFormParams(); const { dateRange, ...extra } = queryParams; const [startMonthStr, endMonthStr] = dateRange || []; const params = { startMonthStr, endMonthStr, ...extra }; - const payload = { ...pageInfo, ...params }; + const payload = { + ...pageInfo, ...params, taxAgentIds: taxAgentIds ? taxAgentIds.split(",") : [] + }; this.setState({ loading: true }); getSalaryAcctList(payload).then(({ status, data }) => { this.setState({ loading: false }); diff --git a/pc4mobx/hrmSalary/pages/calculate/config.js b/pc4mobx/hrmSalary/pages/calculate/config.js new file mode 100644 index 00000000..02da5b10 --- /dev/null +++ b/pc4mobx/hrmSalary/pages/calculate/config.js @@ -0,0 +1,19 @@ +export const queryConditions = [ + { + items: [ + { + conditionType: "SELECT", + domkey: ["taxAgentIds"], + fieldcol: 14, + label: "个税扣缴义务人", + lanI: 111, + multiple: true, + options: [], + labelcol: 6, + value: "", + viewAttr: 2 + } + ], + defaultshow: true + } +]; diff --git a/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/editSalaryCalcSlide.js b/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/editSalaryCalcSlide.js index 5deeaecd..239ccb9e 100644 --- a/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/editSalaryCalcSlide.js +++ b/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/editSalaryCalcSlide.js @@ -99,7 +99,8 @@ class EditSalaryCalcSlide extends Component { return; } const payload = { - salaryAcctEmpId, employeeInfos: baseInfo, + salaryAcctEmpId, + employeeInfos: _.map(baseInfo, o => ({ ...o, fieldValue: o.fieldValue.id || o.fieldValue })), items: [ ..._.reduce(itemsByGroup, (pre, cur) => { return [ diff --git a/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/index.less b/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/index.less index 4e3fb3b4..ff3ef53f 100644 --- a/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/index.less +++ b/pc4mobx/hrmSalary/pages/calculate/doCalc/components/salaryEditCalc/index.less @@ -150,6 +150,10 @@ .esf-base-info-form, .wea-title, .wea-content { padding: 0; + + .ant-row { + height: 100%; + } } .esf-form-content { diff --git a/pc4mobx/hrmSalary/pages/calculate/index.less b/pc4mobx/hrmSalary/pages/calculate/index.less index 045f4b8c..2e73f6b2 100644 --- a/pc4mobx/hrmSalary/pages/calculate/index.less +++ b/pc4mobx/hrmSalary/pages/calculate/index.less @@ -62,6 +62,24 @@ } } + .advance-custom { + display: flex; + align-items: center; + + & > a { + border-radius: 0; + height: 28px; + position: relative; + color: #474747; + padding: 4px 15px; + background-color: transparent; + display: flex; + align-items: center; + border: 1px solid #d9d9d9; + border-left: none + } + } + .wea-input-focus { margin-top: -4px; } @@ -72,6 +90,32 @@ overflow-y: hidden; } + .advance-calc { + display: none; + background: #FFF; + margin-bottom: 8px; + + .advance-calc-btns { + display: flex; + justify-content: center; + align-items: center; + padding: 15px 0; + border-top: 1px solid #dadada; + + button { + margin-right: 15px; + } + } + + .wea-search-group, .wea-content { + padding: 0; + } + } + + .show-advance-calc { + display: block; + } + .calculate-body { height: 100%; width: 100%; diff --git a/pc4mobx/hrmSalary/pages/dataAcquisition/attendance/components/attendanceDataViewSlide.js b/pc4mobx/hrmSalary/pages/dataAcquisition/attendance/components/attendanceDataViewSlide.js index be64c8e8..2f4804b6 100644 --- a/pc4mobx/hrmSalary/pages/dataAcquisition/attendance/components/attendanceDataViewSlide.js +++ b/pc4mobx/hrmSalary/pages/dataAcquisition/attendance/components/attendanceDataViewSlide.js @@ -5,9 +5,9 @@ * Date: 2023/3/7 */ import React, { Component } from "react"; -import { WeaInputSearch, WeaLocaleProvider, WeaSlideModal, WeaTable, WeaTop } from "ecCom"; -import { Button } from "antd"; +import { WeaInputSearch, WeaLocaleProvider, WeaSlideModal, WeaTop } from "ecCom"; import { viewAttendQuote } from "../../../../apis/attendance"; +import { Button, Spin } from "antd"; import "./index.less"; const { getLabel } = WeaLocaleProvider; @@ -16,11 +16,31 @@ class AttendanceDataViewSlide extends Component { constructor(props) { super(props); this.state = { - loading: { query: false }, keyword: "", dataSource: [], columns: [], - pageInfo: { current: 1, pageSize: 10, total: 0 } + loading: { query: false }, keyword: "", dataSource: [], pageInfo: { current: 1, pageSize: 10, total: 0 } }; } + componentDidMount() { + window.addEventListener("message", this.handleReceive, false); + } + + componentWillUnmount() { + window.removeEventListener("message", this.handleReceive, false); + } + + handleReceive = async ({ data }) => { + const { type, payload: { id, params } = {} } = data; + if (type === "turn") { + switch (id) { + case "PAGEINFO": + this.setState({ pageInfo: { ...this.state.pageInfo, ...params } }, () => this.viewAttendQuote()); + break; + default: + break; + } + } + }; + componentWillReceiveProps(nextProps, nextContext) { if (nextProps.visible !== this.props.visible && nextProps.visible) { document.querySelector(".attendanceRefWrapper").classList.add("zIndex0-attendance"); @@ -33,19 +53,29 @@ class AttendanceDataViewSlide extends Component { viewAttendQuote = (extraPayload = {}, props) => { const { loading, pageInfo, keyword } = this.state; - const { attendQuoteId } = props; + const { attendQuoteId } = props || this.props; this.setState({ loading: { ...loading, query: true } }); viewAttendQuote({ ...pageInfo, attendQuoteId, keyword, ...extraPayload }).then(({ status, data }) => { this.setState({ loading: { ...loading, query: false } }); if (status) { const { columns, list: dataSource, pageNum: current, pageSize, total } = data.pageInfo; this.setState({ - pageInfo: { ...pageInfo, current, pageSize, total }, dataSource, - columns: _.map(columns, (o, i) => ({ ...o, width: 150, fixed: i === 0 ? "left" : null })) - }); + pageInfo: { ...pageInfo, current, pageSize, total }, dataSource + }, () => this.postMessageToChild({ + pageInfo: this.state.pageInfo, dataSource, showRowSelection: false, unitTableType: "attendanceView", + columns: _.map(columns, (o, i) => ({ ...o, width: 150, fixed: i === 0 ? "left" : false })) + })); } }).catch(() => this.setState({ loading: { ...loading, query: false } })); }; + postMessageToChild = (payload = {}) => { + const i18n = { + "操作": getLabel(30585, "操作"), "编辑": getLabel(111, "编辑"), "共": getLabel(18609, "共"), + "条": getLabel(18256, "条") + }; + const childFrameObj = document.getElementById("attendanceViewTable"); + childFrameObj && childFrameObj.contentWindow.postMessage(JSON.stringify({ ...payload, i18n }), "*"); + }; handleExportAttendQuote = () => { if (!this.handleDebounce) { this.handleDebounce = _.debounce(() => { @@ -60,24 +90,7 @@ class AttendanceDataViewSlide extends Component { render() { const { showOperateBtn, salaryYearMonth, ...extra } = this.props; - const { columns, dataSource, loading, pageInfo, keyword } = this.state; - const pagination = { - ...pageInfo, - showTotal: (total) => `共 ${total} 条`, - pageSizeOptions: ["10", "20", "50", "100"], - showSizeChanger: true, - showQuickJumper: true, - onShowSizeChange: (current, pageSize) => { - this.setState({ - pageInfo: { ...pageInfo, current, pageSize } - }, () => this.viewAttendQuote({}, this.props)); - }, - onChange: (current) => { - this.setState({ - pageInfo: { ...pageInfo, current } - }, () => this.viewAttendQuote({}, this.props)); - } - }; + const { loading, keyword } = this.state; const btns = [ , {getLabel(543376, "考勤周期")}:{salaryYearMonth}
- +
+ +