diff --git a/actions/questionnaireActions.js b/actions/questionnaireActions.js index fa22821..dc2c34e 100644 --- a/actions/questionnaireActions.js +++ b/actions/questionnaireActions.js @@ -334,6 +334,7 @@ export const resetQuestionnaire = ({ dispatch, id }) => { const template = JSON.parse(JSON.stringify(questionnaire_template)); dispatch({ type: actionTypes.QUESTIONNAIRE_UPDATE, data: { questionnaire: template, } }); + eraseQuestionnaire() .then(() => { @@ -341,13 +342,18 @@ export const resetQuestionnaire = ({ dispatch, id }) => }); } -export const defaultQuestionnaire = ({ dispatch }) => +export const defaultQuestionnaire = ({ dispatch, sign }) => { //console.log("defaultQuestionnaire"); - const template = JSON.parse(JSON.stringify(questionnaire_template)); + if(sign !== undefined) + { + template.sign = sign; + } dispatch({ type: actionTypes.QUESTIONNAIRE_UPDATE, data: { questionnaire: template, } }); + + eraseQuestionnaire().then(() => {}).catch(() => {}); } diff --git a/components/questionnaire/forms/DigitalCertificates.js b/components/questionnaire/forms/DigitalCertificates.js index 96aa769..ea0c7eb 100644 --- a/components/questionnaire/forms/DigitalCertificates.js +++ b/components/questionnaire/forms/DigitalCertificates.js @@ -24,6 +24,7 @@ export default class DigitalCertificates extends React.Component certificates_error: null, certificate_selected: undefined, signing: false, + show_all: false, }; this.cryptopro = null; } @@ -41,11 +42,17 @@ export default class DigitalCertificates extends React.Component .then((info) => { //console.log('Initialized', info); +// console.log('-'.repeat(20)); +// for(let i in this.cryptopro) +// { +// console.log(i); +// } +// console.log('-'.repeat(20)); this.cryptopro.listCertificates() .then((certificates_list) => { - //console.log({ certificates_list }); + console.log({ certificates_list }); console.log("all certificates", JSON.stringify(certificates_list)); Sentry.captureMessage("Client digital sign step, list of all digital certificates"); @@ -57,63 +64,81 @@ export default class DigitalCertificates extends React.Component this.cryptopro.certificateInfo(certificate.id) .then((cert) => { + const errors = []; + let apply = true; + console.log("certificate", JSON.stringify(cert)); console.log("company", JSON.stringify(company)); //console.log({ cert }); - if(cert.IsValid) + if(cert.Subject['OGRNIP'] !== undefined && cert.Subject['OGRNIP'] !== null && cert.Subject['OGRNIP'] !== "") { - if(cert.Subject['OGRNIP'] !== undefined && cert.Subject['OGRNIP'] !== null && cert.Subject['OGRNIP'] !== "") + if(cert.Subject['INN'] !== undefined && cert.Subject['INN'] !== null && cert.Subject['INN'] !== "") { - if(cert.Subject['INN'] !== undefined && cert.Subject['INN'] !== null && cert.Subject['INN'] !== "") + if(main.inn !== cert.Subject['INN']) { - if(main.inn === cert.Subject['INN']) - { - callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY") } ]); - } - else - { - callback(null, []); - } - } - else - { - callback(null, []); + errors.push('inn'); } } else { - if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "") + apply = false; + } + } + else + { + if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "") + { + if(main.inn !== cert.Subject['INNLE']) { - let owner_valid = true; - const fields = [ "lastname", "firstname", "middlename", ]; - const cert_owner = `${ cert.Subject['SN']} ${ cert.Subject['G']}`.toUpperCase(); - - for(let i in fields) - { - if(head_person[fields[i]] !== null && head_person[fields[i]] !== "") - { - if(cert_owner.indexOf(head_person[fields[i]].toUpperCase().trim()) < 0) - { - owner_valid = false; - } - } - } - - if(owner_valid) - { - callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY") } ]); - } - else - { - callback(null, []); - } - } - else - { - callback(null, []); + errors.push('inn'); } } + else + { + apply = false; + } + } + + let owner_valid = true; + const fields = [ "lastname", "firstname", "middlename", ]; + const cert_owner = `${ cert.Subject['SN']} ${ cert.Subject['G']}`.toUpperCase(); + + for(let i in fields) + { + if(head_person[fields[i]] !== null && head_person[fields[i]] !== "") + { + if(cert_owner.indexOf(head_person[fields[i]].toUpperCase().trim()) < 0) + { + owner_valid = false; + } + } + } + + if(!owner_valid) + { + errors.push('name'); + } + + if(cert.ValidToDate !== null && cert.ValidToDate !== undefined && cert.ValidToDate !== "") + { + if(moment(cert.ValidToDate) < moment()) + { + errors.push('date'); + } + } + else + { + errors.push('date'); + } + + if(!cert.IsValid) + { + errors.push('invalid'); + } + if(apply) + { + callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY"), errors } ]); } else { @@ -131,14 +156,34 @@ export default class DigitalCertificates extends React.Component { console.log("applicable certificates", JSON.stringify(certificates)); Sentry.captureMessage("Client digital sign step, list of applicable digital certificates"); - + if(certificates.length === 0) { - this.setState({ loading: false, certificates_error: "ISSUED" }); + this.setState({ loading: false, certificates_error: "INVALID" }); } else { - this.setState({ loading: false, certificates }); + const list = []; + let valid_certificate_exists = false; + + for(let i in certificates) { if(certificates[i].errors.length === 0) { list.push(certificates[i]); valid_certificate_exists = true; } } + for(let i in certificates) { if(certificates[i].errors.length !== 0) { list.push(certificates[i]); } } + + const update = { loading: false, certificates: list, certificates_error: null }; + if(list[0] !== undefined) + { + if(list[0].errors.length === 0) + { + update.certificate_selected = list[0]; + } + } + + if(!valid_certificate_exists) + { + update.certificates_error = "INVALID"; + } + + this.setState(update); } }); }) @@ -201,9 +246,11 @@ export default class DigitalCertificates extends React.Component render() { const { company, main, head_person } = this.props; - const { loading, certificates, certificates_error, certificate_selected, signing } = this.state; + const { loading, certificates, certificates_error, certificate_selected, signing, show_all } = this.state; //console.log("render()", { certificates }); + console.log({ certificates_error }); + if(loading) { return ( @@ -214,88 +261,107 @@ export default class DigitalCertificates extends React.Component } else { - if(certificates_error === null) - { - console.log({ certificates }); + return ( + <> + { certificates_error !== null && ( + <> + { (certificates_error === "NOT_INSTALLED" || certificates_error === "CERTIFICATES") && ( + + Плагин КриптоПРО не установлен или не активирован. Посмотите инструкцию как установить, активировать и проверить работу плагина КриптоПро.
+
+ После активации плагина, пожалуйста, обновите страницу. + }/> + ) } + { certificates_error === "INVALID" && ( + + Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН:
+ { main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }.
+ Такой сертификат не найден!

+ Если Вы уверены в наличии такого сертификата: +
    +
  1. Проверьте, вставлен ли ключ с этим сертификатом.
  2. +
  3. Проверьте возможность подписания этим сертификатом с помощью средства проверки КриптоПро.
  4. +
  5. Возможно, сертификат нужно установить. Воспользуйтесь инструкцией.
  6. +
+ }/> + ) } + { certificates_error === "MISMATCH" && ( + Подписант не соответствует указанному подписанту в анкете. }/> + ) } + + ) + } + { certificates.length > 0 && ( + <> +
+

Список сертификатов, доступных на Вашем компьютере:

+
- return ( - -
-

Выберите подписанта

-
- - { certificates.map((certificate, index) => ( -
- { this.setState({ certificate_selected: certificate }) } } - /> -
-
-
-
- -
-
- ) - } - else - { - return ( - - { certificates_error === "NOT_INSTALLED" && ( - Плагин КриптоПРО не установлен, посмотрите инструкцию как установить плагин КриптоПРО. }/> - ) } - { certificates_error === "CERTIFICATES" && ( - Плагин КриптоПРО не активирован, пожалуйста, обновите страницу и подтвердите разрешение для сайта на доступ к списку сертификатов. }/> - ) } - { certificates_error === "ISSUED" && ( - Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН: { main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }. Такой сертификат не найден. }/> - ) } - { certificates_error === "MISMATCH" && ( - Подписант не соответствует указанному подписанту в анкете. }/> - ) } - - - ) - } +
+
+ +
+ + ) } + + ) } } } \ No newline at end of file diff --git a/components/questionnaire/forms/Form_4_Shareholders/index.js b/components/questionnaire/forms/Form_4_Shareholders/index.js index 0ff4f9b..afcb90d 100644 --- a/components/questionnaire/forms/Form_4_Shareholders/index.js +++ b/components/questionnaire/forms/Form_4_Shareholders/index.js @@ -359,7 +359,7 @@ class ShareholderForm extends React.Component ) }
- + {/*} -1 ? "error" : "" } @@ -791,6 +791,14 @@ class Form_4_Shareholders extends QuestionnaireForm if(v !== "" && v !== null && !isNaN(parseFloat(v))) { total_parts = total_parts + parseFloat(v) + + if(i > 0) + { + if(parseFloat(v) < 25) + { + errors[f].push(`founder_part`); + } + } } else { diff --git a/components/questionnaire/forms/Form_8_Signing/index.js b/components/questionnaire/forms/Form_8_Signing/index.js index 287da96..398759c 100644 --- a/components/questionnaire/forms/Form_8_Signing/index.js +++ b/components/questionnaire/forms/Form_8_Signing/index.js @@ -53,40 +53,28 @@ class Form_8_Signing extends QuestionnaireForm componentDidMount() { - //console.log("Form_8_Signing", "CDM"); + const { main, head_person, signatory_person } = this.state; + const update = { filename: `${ main.inn }_questionnaire_${ moment().format("DDMMYYYY_HHmmss") }.pdf` }; + let digital = true; - //setTimeout(() => - //{ - //console.log("Form_8_Signing", "CDM", { state: this.state }); + if(signatory_person.not_head_person) + { + update.digital_disabled = true; + digital = false; + } - - const { main, head_person, signatory_person } = this.state; - - const update = { filename: `${ main.inn }_questionnaire_${ moment().format("DDMMYYYY_HHmmss") }.pdf` }; - let digital = true; - - if(signatory_person.not_head_person) - { - update.digital_disabled = true; - digital = false; - } - - this.setState({ ...this.state, ...update, ...{ loading: false } }, () => - { - this._handle_onBranchChange([ - { name: `sign.digital`, value: digital }, - ]); - }); - - //}, 50); + this.setState({ ...this.state, ...update, ...{ loading: false } }, () => + { + this._handle_onBranchChange([ + { name: `sign.digital`, value: digital }, + ]); + }); } _handle_onFormSubmit = (event) => { event.preventDefault(); - //console.log("FormAddress", "_handle_onFormSubmit"); - }; _check_fields_disabled = (values) => @@ -147,9 +135,6 @@ class Form_8_Signing extends QuestionnaireForm { const { loading, filename, sign_digital, sign, head_person, mobile, company, main, downloading, error, digital_disabled } = this.state; - //console.log({ sign }); - - if(loading) { return ( diff --git a/components/questionnaire/forms/Form_9_Status/index.js b/components/questionnaire/forms/Form_9_Status/index.js index 83eef7e..2938e5d 100644 --- a/components/questionnaire/forms/Form_9_Status/index.js +++ b/components/questionnaire/forms/Form_9_Status/index.js @@ -44,7 +44,13 @@ class Form_9_Status extends QuestionnaireForm } componentDidMount() - { + { + const { sign } = this.state; + + if(sign.digital) + { + this.props.onSuccess(this.state.sign); + } } _handle_onFileUpload = () => @@ -73,7 +79,7 @@ class Form_9_Status extends QuestionnaireForm { this.setState({ uploading: false, sent: true, error_send: false, }, () => { - this.props.onSuccess(); + this.props.onSuccess(this.state.sign); }); }) .catch(() => @@ -89,10 +95,9 @@ class Form_9_Status extends QuestionnaireForm const { sign, file, uploading, sent, error_file_size, error_send, filename } = this.state; //console.log("Form_9_Status", "render", { sign }); - return ( - { sign.digital && ( + { sign.digital ? ( this.props.router.push("/") }>Перейти на главную страницу
- ) } - { !sign.digital && ( + ) : ( { file === null ? ( diff --git a/components/questionnaire/forms/QuestionnaireForm.js b/components/questionnaire/forms/QuestionnaireForm.js index 112009b..719432c 100644 --- a/components/questionnaire/forms/QuestionnaireForm.js +++ b/components/questionnaire/forms/QuestionnaireForm.js @@ -114,7 +114,6 @@ export default class QuestionnaireForm extends React.Component { //console.log("QuestionnaireForm", "_handle_onCheckboxFieldChange", { name, value }); - const update = { ...this.state }; _set(update, name, value); this._updateQuestionnaire(update); diff --git a/css/forms/style.css b/css/forms/style.css index 70861ec..dc3c0cc 100644 --- a/css/forms/style.css +++ b/css/forms/style.css @@ -1663,3 +1663,44 @@ .deal_offers_table { width: 100%; } +.digital_certificates_status { + position: relative; +} +.digital_certificates_status.error { + border: none !important; +} +.digital_certificates_status.error label { + pointer-events: none; + cursor: unset; +} +.digital_certificates_status.error label::before { + border: none !important; + width: 16px; + height: 16px; + background: url("/assets/images/icons/delete_red.svg") no-repeat center; + z-index: 2; + transform: scale(1.3); + margin-right: 12px; +} +.digital_certificates_status.show_all_holder { + margin-bottom: 40px !important; +} +.digital_certificates_status .show_all { + position: absolute; + left: 0px; + top: 10px; + width: 100%; + height: 100%; + display: flex; + align-items: flex-end; + justify-content: flex-start; + background: linear-gradient(0deg, #ffffff 0%, rgba(255, 255, 255, 0) 100%); +} +.digital_certificates_status .show_all span { + color: var(--blue); + text-decoration: underline; + cursor: pointer; +} +.digital_certificates_status .show_all span:hover { + text-decoration: none; +} diff --git a/css/forms/style.less b/css/forms/style.less index 018372b..1d6a071 100644 --- a/css/forms/style.less +++ b/css/forms/style.less @@ -1947,4 +1947,51 @@ .deal_offers_table { width: 100%; +} + +.digital_certificates_status { + position: relative; + + &.error { + border: none !important; + label { + pointer-events: none; + cursor: unset; + &::before { + border: none !important; + width: 16px; + height: 16px; + background: url("/assets/images/icons/delete_red.svg") no-repeat center; + z-index: 2; + transform: scale(1.3); + margin-right: 12px; + } + } + } + + &.show_all_holder { + margin-bottom: 40px !important; + } + + .show_all { + position: absolute; + left: 0px; + top: 10px; + width: 100%; + height: 100%; + display: flex; + align-items: flex-end; + justify-content: flex-start; + background: linear-gradient(0deg, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); + + span { + color: var(--blue); + text-decoration: underline; + cursor: pointer; + + &:hover { + text-decoration: none; + } + } + } } \ No newline at end of file diff --git a/css/main/style.css b/css/main/style.css index ad283ae..acd0c04 100644 --- a/css/main/style.css +++ b/css/main/style.css @@ -114,7 +114,7 @@ main .title_wrapper .right { } main .title_wrapper .company-dropdown { position: relative; - z-index: 100; + z-index: 90; } main .title_wrapper .company-dropdown .arrow { cursor: pointer; @@ -3598,7 +3598,7 @@ main .dropdown_blocks_list .dropdown_block .block_body .fines_detail ul li { bottom: -30px; right: 0; pointer-events: none; - z-index: 98; + z-index: 80; margin: 0 auto; max-width: 1310px; } diff --git a/css/main/style.less b/css/main/style.less index 775d6f6..0f6ca84 100644 --- a/css/main/style.less +++ b/css/main/style.less @@ -121,7 +121,7 @@ main { .company-dropdown { position: relative; - z-index: 100; + z-index: 90; .arrow { cursor: pointer; @@ -4019,7 +4019,7 @@ main .dropdown_blocks_list .dropdown_block .block_body { bottom: -30px; right: 0; pointer-events: none; - z-index: 98; + z-index: 80; margin: 0 auto; max-width: 1310px; diff --git a/pages/api/questionnaire/download.js b/pages/api/questionnaire/download.js index 678f761..e7398ca 100644 --- a/pages/api/questionnaire/download.js +++ b/pages/api/questionnaire/download.js @@ -298,6 +298,17 @@ export default async function handler(req, res) } } + if(group === "head_person") + { + if(field === "credentials_dateend") + { + if(questionnaire[group].indefinite) + { + continue; + } + } + } + if(field === "fullname") { fields[group][field].bind = form.getTextField(fields[group][field].name); diff --git a/pages/api/suggests/document/check.js b/pages/api/suggests/document/check.js index dc6602c..218a695 100644 --- a/pages/api/suggests/document/check.js +++ b/pages/api/suggests/document/check.js @@ -23,36 +23,44 @@ export default async function handler(req, res) { if(jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT)) { - try + if(parseInt(process.env.DADATA_API_CHECK_DOCUMENT_ENABLED, 10) === 1) { - const { seria, number } = req.body; + try + { + const { seria, number } = req.body; - axios.post(`https://cleaner.dadata.ru/api/v1/clean/passport`, [ `${ seria } ${ number }` ], { - httpAgent: keepAliveAgent, - headers: { - "Content-Type": "application/json", - "Authorization": `Token ${ process.env.DADATA_API_KEY }`, - "X-Secret": process.env.DADATA_SECRET_KEY - }, - }) - .then((api_response) => + axios.post(`https://cleaner.dadata.ru/api/v1/clean/passport`, [ `${ seria } ${ number }` ], { + httpAgent: keepAliveAgent, + headers: { + "Content-Type": "application/json", + "Authorization": `Token ${ process.env.DADATA_API_KEY }`, + "X-Secret": process.env.DADATA_SECRET_KEY + }, + }) + .then((api_response) => + { + res.status(200).send(api_response.data); + resolve(); + }) + .catch((error) => + { + console.log("error"); + console.error(error); + + res.status(500).send(); + resolve(); + }); + } + catch(e) { - res.status(200).send(api_response.data); - resolve(); - }) - .catch((error) => - { - console.log("error"); - console.error(error); - + console.error(e); res.status(500).send(); resolve(); - }); + } } - catch(e) + else { - console.error(e); - res.status(500).send(); + res.status(200).json([{ qc: 0 }]); resolve(); } } diff --git a/pages/questionnaire/index.js b/pages/questionnaire/index.js index c4b9237..1aa69b5 100644 --- a/pages/questionnaire/index.js +++ b/pages/questionnaire/index.js @@ -109,12 +109,12 @@ class QuestionnairePage extends React.Component this.props.router.push(`/questionnaire#${ path }`); } - _handle_onSuccess = () => + _handle_onSuccess = (sign) => { //console.log("_handle_onSuccess"); getCompanyInfo({ dispatch: this.props.dispatch }); - defaultQuestionnaire({ dispatch: this.props.dispatch }); + defaultQuestionnaire({ dispatch: this.props.dispatch, sign }); } _renderForm = () => diff --git a/pages/recovery.js b/pages/recovery.js index 5bc17f2..7f33814 100644 --- a/pages/recovery.js +++ b/pages/recovery.js @@ -29,7 +29,7 @@ export default class RecoveryPage extends React.Component email_error: false, recovery_form_step: 1, email_code_error: false, - email_send_disabled: false, + email_send_disabled: true, set_password_disabled: true, email_code_submit_disabled: true, email_code_resend_disabled: true, @@ -137,17 +137,26 @@ export default class RecoveryPage extends React.Component _handle_onEmailChange = (value) => { - this.setState({ email: value, email_send_disabled: this._check_fields_disabled([ value, this.state.email ]), email_error: false }); + this.setState({ email: value, email_error: false }, () => + { + this.setState({ email_send_disabled: this._check_fields_disabled([ value, this.state.email ]) }); + }); } _handle_onPasswordChange = (value) => { - this.setState({ password: value, set_password_disabled: this._check_fields_disabled([ value, this.state.password ]), password_error: false }); + this.setState({ password: value, password_error: false }, () => + { + this.setState({ set_password_disabled: this._check_fields_disabled([ value, this.state.password ]) }); + }); } _handle_onPasswordCheckChange = (value) => { - this.setState({ password_repeat: value, set_password_disabled: this._check_fields_disabled([ value, this.state.password_repeat ]), password_error: false }); + this.setState({ password_repeat: value, password_error: false }, () => + { + this.setState({ set_password_disabled: this._check_fields_disabled([ value, this.state.password_repeat ]) }); + }); } _handle_onEmailCodeChange = (value) => diff --git a/public/assets/images/icons/delete_red.svg b/public/assets/images/icons/delete_red.svg new file mode 100644 index 0000000..14b7c74 --- /dev/null +++ b/public/assets/images/icons/delete_red.svg @@ -0,0 +1,3 @@ + + + diff --git a/reducers/initialState.js b/reducers/initialState.js index 81cca2c..5ed6e72 100644 --- a/reducers/initialState.js +++ b/reducers/initialState.js @@ -1,3 +1,12 @@ +export const questionnaire_sign_template = { + digital: true, + uploading: false, + uploaded: false, + sent: false, + filename: null, + filedate: null, +}; + export const questionnaire_template = { step: 1, status: "empty", @@ -119,14 +128,7 @@ export const questionnaire_template = { fin_goals_special: null, }, personal_data_consent: true, - sign: { - digital: true, - uploading: false, - uploaded: false, - sent: false, - filename: null, - filedate: null, - }, + sign: questionnaire_sign_template, licenses: [], };