feat(election): dynamic designations from m_code, email-based voting, vote remark field
- DesignationService reads designation limits and mandatory flags from DirectorDesignation m_code_value - DirectorAppointmentServiceImpl: replaced hardcoded DESIGNATION_LIMITS map with dynamic DB fetch - CommitteeServiceImpl: replaced hardcoded Chairman/Vice Chairman check with dynamic mandatory designation query - New GET /v1/election/directors/designations endpoint returns all active designations - Email-based voting module: MeetingPublicVoteApiResource, MeetingVoteAdminApiResource, MeetingVoteService - MeetingMemberVote: added remark (TEXT) column for member vote remarks - AgendaVoteRequest: added remark field to carry voice/typed remarks per agenda item - Liquibase migrations 0354 (vote/resolution tables), 0355 (DirectorDesignation code), 0356 (vote remark column) - SecurityConfig: permit public voting endpoints without authentication Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
cfcd24efad
commit
934a51c9ee
@@ -687,3 +687,6 @@ spring.mail.properties.mail.transport.protocol=smtp
|
||||
fineract.email.from-address=${FINERACT_EMAIL_FROM_ADDRESS:no-reply@cbsnexora.com}
|
||||
fineract.email.from-name=${FINERACT_EMAIL_FROM_NAME:CBSnexora}
|
||||
fineract.email.support-address=${FINERACT_EMAIL_SUPPORT_ADDRESS:support@cbsnexora.com}
|
||||
|
||||
# Frontend base URL — used for email links (voting, etc.)
|
||||
cbs.frontend.base-url=${CBS_FRONTEND_BASE_URL:http://localhost:4200}
|
||||
|
||||
@@ -381,4 +381,7 @@
|
||||
<include file="parts/0351_committee_meeting.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0352_committee_meeting_rename_audit_cols.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0353_committee_meeting_agenda.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0354_meeting_vote_resolution.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0355_director_designation_code.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0356_meeting_vote_remark.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- Vote token: one per member per meeting, carries the secure email link -->
|
||||
<changeSet id="0354-meeting-vote-token" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_election_meeting_vote_token"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_election_meeting_vote_token">
|
||||
<column name="token" type="VARCHAR(36)">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="meeting_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="member_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="member_name" type="VARCHAR(200)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="is_used" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="expires_at" type="TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_at" type="TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_election_meeting_vote_token"
|
||||
baseColumnNames="meeting_id"
|
||||
referencedTableName="m_election_committee_meeting"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_vote_token_meeting"
|
||||
onDelete="CASCADE"/>
|
||||
<createIndex tableName="m_election_meeting_vote_token" indexName="idx_vote_token_meeting_member">
|
||||
<column name="meeting_id"/>
|
||||
<column name="member_id"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<!-- Member vote: one row per member per agenda item -->
|
||||
<changeSet id="0354-meeting-member-vote" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_election_meeting_member_vote"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_election_meeting_member_vote">
|
||||
<column name="id" type="BIGSERIAL">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="meeting_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="agenda_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="member_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="vote" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="voted_at" type="TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_election_meeting_member_vote"
|
||||
baseColumnNames="meeting_id"
|
||||
referencedTableName="m_election_committee_meeting"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_member_vote_meeting"
|
||||
onDelete="CASCADE"/>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_election_meeting_member_vote"
|
||||
baseColumnNames="agenda_id"
|
||||
referencedTableName="m_election_committee_meeting_agenda"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_member_vote_agenda"
|
||||
onDelete="CASCADE"/>
|
||||
<addUniqueConstraint
|
||||
tableName="m_election_meeting_member_vote"
|
||||
columnNames="agenda_id, member_id"
|
||||
constraintName="uq_member_vote_agenda_member"/>
|
||||
<createIndex tableName="m_election_meeting_member_vote" indexName="idx_member_vote_meeting">
|
||||
<column name="meeting_id"/>
|
||||
<column name="member_id"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<!-- Agenda resolution: admin's final recorded decision per agenda item -->
|
||||
<changeSet id="0354-meeting-agenda-resolution" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_election_meeting_agenda_resolution"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_election_meeting_agenda_resolution">
|
||||
<column name="id" type="BIGSERIAL">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="meeting_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="agenda_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="resolution" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="discussion_notes" type="TEXT"/>
|
||||
<column name="resolved_at" type="TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_election_meeting_agenda_resolution"
|
||||
baseColumnNames="meeting_id"
|
||||
referencedTableName="m_election_committee_meeting"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_resolution_meeting"
|
||||
onDelete="CASCADE"/>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_election_meeting_agenda_resolution"
|
||||
baseColumnNames="agenda_id"
|
||||
referencedTableName="m_election_committee_meeting_agenda"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_resolution_agenda"
|
||||
onDelete="CASCADE"/>
|
||||
<addUniqueConstraint
|
||||
tableName="m_election_meeting_agenda_resolution"
|
||||
columnNames="agenda_id"
|
||||
constraintName="uq_resolution_agenda"/>
|
||||
<createIndex tableName="m_election_meeting_agenda_resolution" indexName="idx_resolution_meeting">
|
||||
<column name="meeting_id"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0355-director-designation-code" author="CBS">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'DirectorDesignation', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'DirectorDesignation');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0355-director-designation-values" author="CBS">
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory, code_score)
|
||||
SELECT c.id, v.code_value, v.order_position, true, v.is_mandatory, v.code_score
|
||||
FROM m_code c
|
||||
CROSS JOIN (VALUES
|
||||
('Chairman', 1, true, 1),
|
||||
('Vice Chairman', 2, true, 1),
|
||||
('Secretary', 3, false, NULL),
|
||||
('Treasurer', 4, false, NULL),
|
||||
('Director', 5, false, NULL),
|
||||
('Principal Director', 6, false, 2)
|
||||
) AS v(code_value, order_position, is_mandatory, code_score)
|
||||
WHERE c.code_name = 'DirectorDesignation'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0356-meeting-vote-remark" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_election_meeting_member_vote" columnName="remark"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_election_meeting_member_vote">
|
||||
<column name="remark" type="TEXT"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user