Client and Group timeline Implementation

This commit is contained in:
CieyouRaoul
2013-11-13 17:04:11 +01:00
parent aa0df6a0c3
commit 5da1221159
16 changed files with 432 additions and 44 deletions
@@ -0,0 +1,9 @@
ALTER TABLE `m_client` ADD `submittedon_date` DATE NULL DEFAULT NULL ,
ADD `submittedon_userid` BIGINT( 20 ) NULL DEFAULT NULL ,
ADD `activatedon_userid` BIGINT( 20 ) NULL DEFAULT NULL ,
ADD `closedon_userid` BIGINT( 20 ) NULL DEFAULT NULL ;
ALTER TABLE `m_group` ADD `activatedon_userid` BIGINT( 20 ) NULL ,
ADD `submittedon_date` DATE NULL ,
ADD `submittedon_userid` BIGINT( 20 ) NULL ,
ADD `closedon_userid` BIGINT( 20 ) NULL ;
@@ -37,6 +37,7 @@ public class ClientApiConstants {
public static final String staffIdParamName = "staffId";
public static final String closureDateParamName = "closureDate";
public static final String closureReasonIdParamName = "closureReasonId";
public static final String submittedOnDateParamName="submittedOnDate";
// response parameters
public static final String statusParamName = "status";
public static final String hierarchyParamName = "hierarchy";
@@ -135,6 +135,7 @@ public class ClientsApiResource {
clientData = ClientData.templateOnTop(clientData, templateData);
}
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
}
@@ -48,6 +48,7 @@ final public class ClientData implements Comparable<ClientData> {
private final Boolean imagePresent;
private final Long staffId;
private final String staffName;
private final ClientTimelineData timeline;
// associations
private final Collection<GroupGeneralData> groups;
@@ -61,7 +62,8 @@ final public class ClientData implements Comparable<ClientData> {
public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection<OfficeData> officeOptions,
final Collection<StaffData> staffOptions, final Collection<CodeValueData> closureReasons) {
return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, null, null, null, joinedDate, null,
null, null, officeOptions, null, staffOptions, closureReasons);
null, null, officeOptions, null, staffOptions, closureReasons,null);
}
public static ClientData templateOnTop(final ClientData clientData, final ClientData templateData) {
@@ -70,7 +72,8 @@ final public class ClientData implements Comparable<ClientData> {
clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, templateData.officeOptions,
clientData.groups, templateData.staffOptions, null);
clientData.groups, templateData.staffOptions, null,null);
}
public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) {
@@ -78,7 +81,8 @@ final public class ClientData implements Comparable<ClientData> {
clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions,
parentGroups, clientData.staffOptions, null);
parentGroups, clientData.staffOptions, null,clientData.timeline);
}
public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname,
@@ -86,20 +90,23 @@ final public class ClientData implements Comparable<ClientData> {
final String officeName) {
return new ClientData(accountNo, status, officeId, officeName, null, null, id, firstname, middlename, lastname, fullname,
displayName, null, null, null, null, null, null, null, null, null, null);
displayName, null, null, null, null, null, null, null, null, null, null,null);
}
public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) {
return new ClientData(null, null, officeId, officeName, null, null, id, null, null, null, null, displayName, null, null, null,
null, null, null, null, null, null, null);
null, null, null, null, null, null, null,null);
}
public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName,
final Long transferToOfficeId, final String transferToOfficeName, final Long id, final String firstname,
final String middlename, final String lastname, final String fullname, final String displayName, final String externalId,
final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName) {
final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName,final ClientTimelineData timeline) {
return new ClientData(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, middlename,
lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, null, null, null, null);
lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, null, null, null, null,timeline);
}
private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName,
@@ -107,7 +114,7 @@ final public class ClientData implements Comparable<ClientData> {
final String middlename, final String lastname, final String fullname, final String displayName, final String externalId,
final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName,
final Collection<OfficeData> allowedOffices, final Collection<GroupGeneralData> groups,
final Collection<StaffData> staffOptions, final Collection<CodeValueData> closureReasons) {
final Collection<StaffData> staffOptions, final Collection<CodeValueData> closureReasons,final ClientTimelineData timeline) {
this.accountNo = accountNo;
this.status = status;
if (status != null) {
@@ -144,6 +151,8 @@ final public class ClientData implements Comparable<ClientData> {
this.officeOptions = allowedOffices;
this.staffOptions = staffOptions;
this.closureReasons = closureReasons;
this.timeline = timeline;
}
public Long id() {
@@ -170,6 +179,10 @@ final public class ClientData implements Comparable<ClientData> {
return this.imagePresent;
}
public ClientTimelineData getTimeline() {
return this.timeline;
}
@Override
public int compareTo(final ClientData obj) {
if (obj == null) { return -1; }
@@ -0,0 +1,60 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.data;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import java.math.BigDecimal;
/**
* Immutable data object represent the important time-line events of a loan
* application and loan.
*/
@SuppressWarnings("unused")
public class ClientTimelineData {
private final LocalDate submittedOnDate;
private final String submittedByUsername;
private final String submittedByFirstname;
private final String submittedByLastname;
private final LocalDate activatedOnDate;
private final String activatedByUsername;
private final String activatedByFirstname;
private final String activatedByLastname;
private final LocalDate closedOnDate;
private final String closedByUsername;
private final String closedByFirstname;
private final String closedByLastname;
public ClientTimelineData(final LocalDate submittedOnDate, final String submittedByUsername,
final String submittedByFirstname, final String submittedByLastname, final LocalDate activatedOnDate,
final String activatedByUsername, final String activatedByFirstname, final String activatedByLastname,
final LocalDate closedOnDate, final String closedByUsername, final String closedByFirstname,
final String closedByLastname) {
this.submittedOnDate = submittedOnDate;
this.submittedByUsername = submittedByUsername;
this.submittedByFirstname = submittedByFirstname;
this.submittedByLastname = submittedByLastname;
this.activatedOnDate = activatedOnDate;
this.activatedByUsername = activatedByUsername;
this.activatedByFirstname = activatedByFirstname;
this.activatedByLastname = activatedByLastname;
this.closedOnDate = closedOnDate;
this.closedByUsername = closedByUsername;
this.closedByFirstname = closedByFirstname;
this.closedByLastname = closedByLastname;
}
}
@@ -27,6 +27,7 @@ import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
@@ -44,6 +45,7 @@ import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.api.ClientApiConstants;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -121,7 +123,27 @@ public final class Client extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE)
private Date closureDate;
public static Client createNew(final Office clientOffice, final Group clientParentGroup, final Staff staff, final JsonCommand command) {
@ManyToOne(optional = true)
@JoinColumn(name = "closedon_userid", nullable = true)
private AppUser closeddBy;
@Column(name = "submittedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date submittedOnDate;
@ManyToOne(optional = true)
@JoinColumn(name = "submittedon_userid", nullable = true)
private AppUser submittedBy;
@ManyToOne(optional = true)
@JoinColumn(name = "activatedon_userid", nullable = true)
private AppUser activatedBy;
public static Client createNew(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff, final JsonCommand command) {
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
@@ -132,12 +154,23 @@ public final class Client extends AbstractPersistable<Long> {
final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
LocalDate submittedOnDate = new LocalDate();
if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) {
submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
}
ClientStatus status = ClientStatus.PENDING;
boolean active = false;
if (command.hasParameter("active")) {
active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
}
LocalDate activationDate = null;
LocalDate officeJoiningDate = null;
if (active) {
@@ -146,23 +179,48 @@ public final class Client extends AbstractPersistable<Long> {
officeJoiningDate = activationDate;
}
return new Client(status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, activationDate,
officeJoiningDate, externalId, mobileNo, staff);
return new Client(currentUser,status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, activationDate,
officeJoiningDate, externalId, mobileNo, staff,submittedOnDate);
}
protected Client() {
//
}
private Client(final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo,
private Client(final AppUser currentUser,final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo,
final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate,
final LocalDate officeJoiningDate, final String externalId, final String mobileNo, final Staff staff) {
final LocalDate officeJoiningDate, final String externalId, final String mobileNo, final Staff staff,final LocalDate submittedOnDate) {
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
} else {
this.accountNumber = accountNo;
}
this.submittedBy = currentUser;
if (isDateInTheFuture(submittedOnDate)) {
final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.in.the.future",
defaultUserMessage, ClientApiConstants.submittedOnDateParamName,submittedOnDate);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.submittedOnDate = submittedOnDate.toDate();
this.status = status.getValue();
this.office = office;
if (StringUtils.isNotBlank(externalId)) {
@@ -246,7 +304,7 @@ public final class Client extends AbstractPersistable<Long> {
this.accountNumberRequiresAutoGeneration = false;
}
public void activate(final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
public void activate(final AppUser currentUser,final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
if (isActive()) {
final String defaultUserMessage = "Cannot activate client. Client is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.already.active", defaultUserMessage,
@@ -271,6 +329,7 @@ public final class Client extends AbstractPersistable<Long> {
}
this.activationDate = activationLocalDate.toDate();
this.activatedBy = currentUser;
this.officeJoiningDate = this.activationDate;
this.status = ClientStatus.ACTIVE.getValue();
@@ -449,6 +508,10 @@ public final class Client extends AbstractPersistable<Long> {
this.displayName = nameBuilder.toString();
}
public LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
public LocalDate getActivationLocalDate() {
LocalDate activationLocalDate = null;
if (this.activationDate != null) {
@@ -545,9 +608,10 @@ public final class Client extends AbstractPersistable<Long> {
return this.groups;
}
public void close(final CodeValue closureReason, final Date closureDate) {
public void close(final AppUser currentUser, final CodeValue closureReason, final Date closureDate) {
this.closureReason = closureReason;
this.closureDate = closureDate;
this.closeddBy = currentUser;
this.status = ClientStatus.CLOSED.getValue();
}
@@ -27,6 +27,7 @@ import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.data.ClientTimelineData;
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
import org.mifosplatform.portfolio.client.domain.ClientStatus;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
@@ -263,13 +264,32 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
sqlBuilder.append("c.mobile_no as mobileNo, ");
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName ");
sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
sqlBuilder.append("sbu.username as submittedByUsername, ");
sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
sqlBuilder.append("sbu.lastname as submittedByLastname, ");
sqlBuilder.append("c.closedon_date as closedOnDate, ");
sqlBuilder.append("clu.username as closedByUsername, ");
sqlBuilder.append("clu.firstname as closedByFirstname, ");
sqlBuilder.append("clu.lastname as closedByLastname, ");
sqlBuilder.append("acu.username as activatedByUsername, ");
sqlBuilder.append("acu.firstname as activatedByFirstname, ");
sqlBuilder.append("acu.lastname as activatedByLastname ");
sqlBuilder.append("from m_client c ");
sqlBuilder.append("join m_office o on o.id = c.office_id ");
sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id ");
sqlBuilder.append("left join m_staff s on s.id = c.staff_id ");
sqlBuilder.append("left join m_office transferToOffice on transferToOffice.id = c.transfer_to_office_id ");
sqlBuilder.append("left join m_appuser sbu on sbu.id = c.submittedon_userid ");
sqlBuilder.append("left join m_appuser acu on acu.id = c.activatedon_userid ");
sqlBuilder.append("left join m_appuser clu on clu.id = c.closedon_userid ");
this.schema = sqlBuilder.toString();
}
@@ -305,8 +325,28 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
final String closedByUsername = rs.getString("closedByUsername");
final String closedByFirstname = rs.getString("closedByFirstname");
final String closedByLastname = rs.getString("closedByLastname");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final String submittedByFirstname = rs.getString("submittedByFirstname");
final String submittedByLastname = rs.getString("submittedByLastname");
final String activatedByUsername = rs.getString("activatedByUsername");
final String activatedByFirstname = rs.getString("activatedByFirstname");
final String activatedByLastname = rs.getString("activatedByLastname");
final ClientTimelineData timeline = new ClientTimelineData (submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername,
closedByFirstname, closedByLastname);
return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName);
middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName,timeline);
}
}
@@ -338,12 +378,34 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
builder.append("c.fullname as fullname, c.display_name as displayName, ");
builder.append("c.mobile_no as mobileNo, ");
builder.append("c.submittedon_date as submittedOnDate, ");
builder.append("sbu.username as submittedByUsername, ");
builder.append("sbu.firstname as submittedByFirstname, ");
builder.append("sbu.lastname as submittedByLastname, ");
builder.append("c.closedon_date as closedOnDate, ");
builder.append("clu.username as closedByUsername, ");
builder.append("clu.firstname as closedByFirstname, ");
builder.append("clu.lastname as closedByLastname, ");
// builder.append("c.submittedon as submittedOnDate, ");
builder.append("acu.username as activatedByUsername, ");
builder.append("acu.firstname as activatedByFirstname, ");
builder.append("acu.lastname as activatedByLastname, ");
builder.append("c.activation_date as activationDate, c.image_id as imageId, ");
builder.append("c.staff_id as staffId, s.display_name as staffName ");
builder.append("from m_client c ");
builder.append("join m_office o on o.id = c.office_id ");
builder.append("left join m_staff s on s.id = c.staff_id ");
builder.append("left join m_office transferToOffice on transferToOffice.id = c.transfer_to_office_id ");
builder.append("left join m_appuser sbu on sbu.id = c.submittedon_userid ");
builder.append("left join m_appuser acu on acu.id = c.activatedon_userid ");
builder.append("left join m_appuser clu on clu.id = c.closedon_userid ");
this.schema = builder.toString();
}
@@ -378,8 +440,27 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
final String closedByUsername = rs.getString("closedByUsername");
final String closedByFirstname = rs.getString("closedByFirstname");
final String closedByLastname = rs.getString("closedByLastname");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final String submittedByFirstname = rs.getString("submittedByFirstname");
final String submittedByLastname = rs.getString("submittedByLastname");
final String activatedByUsername = rs.getString("activatedByUsername");
final String activatedByFirstname = rs.getString("activatedByFirstname");
final String activatedByLastname = rs.getString("activatedByLastname");
final ClientTimelineData timeline = new ClientTimelineData (submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername,
closedByFirstname, closedByLastname);
return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName);
middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName,timeline);
}
}
@@ -44,6 +44,7 @@ import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -136,6 +137,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
public CommandProcessingResult createClient(final JsonCommand command) {
try {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json());
final Long officeId = command.longValueOfParameterNamed(ClientApiConstants.officeIdParamName);
@@ -157,7 +161,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientOffice.getHierarchy());
}
final Client newClient = Client.createNew(clientOffice, clientParentGroup, staff, command);
final Client newClient = Client.createNew(currentUser,clientOffice, clientParentGroup, staff, command);
this.clientRepository.save(newClient);
@@ -235,7 +239,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
client.activate(fmt, activationDate);
final AppUser currentUser = this.context.authenticatedUser();
client.activate(currentUser, fmt, activationDate);
this.clientRepository.saveAndFlush(client);
@@ -328,6 +333,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Override
public CommandProcessingResult closeClient(final Long clientId, final JsonCommand command) {
try {
AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateClose(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
@@ -385,7 +392,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
}
}
client.close(closureReason, closureDate.toDate());
client.close(currentUser,closureReason, closureDate.toDate());
this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() //
@@ -42,6 +42,8 @@ public class GroupingTypesApiConstants {
public static final String activationDateParamName = "activationDate";
public static final String groupMembersParamName = "groupMembers";
public static final String submittedOnDateParamName = "submittedOnDate";
// group parameters
public static final String centerIdParamName = "centerId";
public static final String clientMembersParamName = "clientMembers";
@@ -55,6 +57,7 @@ public class GroupingTypesApiConstants {
public static final String staffOptionsParamName = "staffOptions";
public static final String clientOptionsParamName = "clientOptions";
public static final String collectionMeetingCalendar = "collectionMeetingCalendar";
public static final String timeLine ="timeline";
// group close parameters
public static final String closureDateParamName = "closureDate";
@@ -66,7 +69,7 @@ public class GroupingTypesApiConstants {
public static final Set<String> GROUP_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName, dateFormatParamName,
idParamName, nameParamName, externalIdParamName, centerIdParamName, officeIdParamName, staffIdParamName, activeParamName,
activationDateParamName, clientMembersParamName, collectionMeetingCalendar));
activationDateParamName, clientMembersParamName, collectionMeetingCalendar,submittedOnDateParamName));
public static final Set<String> GROUP_ROLES_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(roleParamName,
clientIdParamName));
@@ -86,7 +89,7 @@ public class GroupingTypesApiConstants {
public static final Set<String> GROUP_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName, nameParamName,
externalIdParamName, officeIdParamName, officeNameParamName, "parentId", "parentName", staffIdParamName, staffNameParamName,
hierarchyParamName, officeOptionsParamName, staffOptionsParamName, clientOptionsParamName));
hierarchyParamName, officeOptionsParamName, staffOptionsParamName, clientOptionsParamName,timeLine));
public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName));
@@ -52,13 +52,14 @@ public class GroupGeneralData {
private final Collection<CodeValueData> availableRoles;
private final GroupRoleData selectedRole;
private final Collection<CodeValueData> closureReasons;
private final GroupTimelineData timeline;
public static GroupGeneralData lookup(final Long groupId, final String groupName) {
final Collection<ClientData> clientMembers = null;
final Collection<GroupRoleData> groupRoles = null;
final Collection<CodeValueData> closureReasons = null;
return new GroupGeneralData(groupId, groupName, null, null, null, null, null, null, null, null, null, null, clientMembers, null,
null, null, null, groupRoles, null, null, null, null, closureReasons);
null, null, null, groupRoles, null, null, null, null, closureReasons,null);
}
public static GroupGeneralData template(final Long officeId, final Long centerId, final String centerName, final Long staffId,
@@ -72,7 +73,7 @@ public class GroupGeneralData {
return new GroupGeneralData(null, null, null, null, null, officeId, null, centerId, centerName, staffId, staffName, null,
clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null, null, null,
closureReasons);
closureReasons,null);
}
public static GroupGeneralData withTemplate(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping) {
@@ -80,7 +81,7 @@ public class GroupGeneralData {
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions,
templatedGrouping.staffOptions, templatedGrouping.clientOptions, grouping.groupRoles, templatedGrouping.availableRoles,
grouping.selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar, grouping.closureReasons);
grouping.selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar, grouping.closureReasons,templatedGrouping.timeline);
}
public static GroupGeneralData withAssocations(final GroupGeneralData grouping, final Collection<ClientData> membersOfGroup,
@@ -90,12 +91,12 @@ public class GroupGeneralData {
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole, calendarsData,
collectionMeetingCalendar, grouping.closureReasons);
collectionMeetingCalendar, grouping.closureReasons,grouping.timeline);
}
public static GroupGeneralData instance(final Long id, final String name, final String externalId, final EnumOptionData status,
final LocalDate activationDate, final Long officeId, final String officeName, final Long centerId, final String centerName,
final Long staffId, final String staffName, final String hierarchy) {
final Long staffId, final String staffName, final String hierarchy,final GroupTimelineData timeline) {
final Collection<ClientData> clientMembers = null;
final Collection<CenterData> centerOptions = null;
@@ -111,7 +112,7 @@ public class GroupGeneralData {
return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles,
role, calendarsData, collectionMeetingCalendar, closureReasons);
role, calendarsData, collectionMeetingCalendar, closureReasons,timeline);
}
private GroupGeneralData(final Long id, final String name, final String externalId, final EnumOptionData status,
@@ -121,7 +122,7 @@ public class GroupGeneralData {
final Collection<StaffData> staffOptions, final Collection<ClientData> clientOptions,
final Collection<GroupRoleData> groupRoles, final Collection<CodeValueData> availableRoles, final GroupRoleData role,
final Collection<CalendarData> calendarsData, final CalendarData collectionMeetingCalendar,
final Collection<CodeValueData> closureReasons) {
final Collection<CodeValueData> closureReasons,final GroupTimelineData timeline) {
this.id = id;
this.name = name;
this.externalId = externalId;
@@ -159,6 +160,7 @@ public class GroupGeneralData {
this.calendarsData = calendarsData;
this.collectionMeetingCalendar = collectionMeetingCalendar;
this.closureReasons = closureReasons;
this.timeline = timeline;
}
public Long getId() {
@@ -190,7 +192,7 @@ public class GroupGeneralData {
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole, grouping.calendarsData,
grouping.collectionMeetingCalendar, grouping.closureReasons);
grouping.collectionMeetingCalendar, grouping.closureReasons,null);
}
public static GroupGeneralData withClosureReasons(final Collection<CodeValueData> closureReasons) {
@@ -219,6 +221,6 @@ public class GroupGeneralData {
return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles,
role, calendarsData, collectionMeetingCalendar, closureReasons);
role, calendarsData, collectionMeetingCalendar, closureReasons,null);
}
}
@@ -0,0 +1,56 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.group.data;
import org.joda.time.LocalDate;
/**
* Immutable data object represent the important time-line events of a loan
* application and loan.
*/
@SuppressWarnings("unused")
public class GroupTimelineData {
private final LocalDate submittedOnDate;
private final String submittedByUsername;
private final String submittedByFirstname;
private final String submittedByLastname;
private final LocalDate activatedOnDate;
private final String activatedByUsername;
private final String activatedByFirstname;
private final String activatedByLastname;
private final LocalDate closedOnDate;
private final String closedByUsername;
private final String closedByFirstname;
private final String closedByLastname;
public GroupTimelineData(final LocalDate submittedOnDate, final String submittedByUsername,
final String submittedByFirstname, final String submittedByLastname, final LocalDate activatedOnDate,
final String activatedByUsername, final String activatedByFirstname, final String activatedByLastname,
final LocalDate closedOnDate, final String closedByUsername, final String closedByFirstname,
final String closedByLastname) {
this.submittedOnDate = submittedOnDate;
this.submittedByUsername = submittedByUsername;
this.submittedByFirstname = submittedByFirstname;
this.submittedByLastname = submittedByLastname;
this.activatedOnDate = activatedOnDate;
this.activatedByUsername = activatedByUsername;
this.activatedByFirstname = activatedByFirstname;
this.activatedByLastname = activatedByLastname;
this.closedOnDate = closedOnDate;
this.closedByUsername = closedByUsername;
this.closedByFirstname = closedByFirstname;
this.closedByLastname = closedByLastname;
}
}
@@ -43,6 +43,7 @@ import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants;
import org.mifosplatform.portfolio.group.exception.ClientExistInGroupException;
import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException;
import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
import com.google.common.collect.Sets;
@@ -64,6 +65,10 @@ public final class Group extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE)
private Date activationDate;
@ManyToOne(optional = true)
@JoinColumn(name = "activatedon_userid", nullable = true)
private AppUser activatedBy;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@@ -106,6 +111,18 @@ public final class Group extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE)
private Date closureDate;
@ManyToOne(optional = true)
@JoinColumn(name = "closedon_userid", nullable = true)
private AppUser closedBy;
@Column(name = "submittedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date submittedOnDate;
@ManyToOne(optional = true)
@JoinColumn(name = "submittedon_userid", nullable = true)
private AppUser submittedBy;
public Group() {
this.name = null;
this.externalId = null;
@@ -114,7 +131,7 @@ public final class Group extends AbstractPersistable<Long> {
public static Group newGroup(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel,
final String name, final String externalId, final boolean active, final LocalDate activationDate,
final Set<Client> clientMembers, final Set<Group> groupMembers) {
final Set<Client> clientMembers, final Set<Group> groupMembers,final LocalDate submittedOnDate,final AppUser currentUser) {
GroupingTypeStatus status = GroupingTypeStatus.PENDING;
LocalDate groupActivationDate = null;
@@ -124,12 +141,14 @@ public final class Group extends AbstractPersistable<Long> {
// group is made active
}
return new Group(office, staff, parent, groupLevel, name, externalId, status, groupActivationDate, clientMembers, groupMembers);
return new Group(office, staff, parent, groupLevel, name, externalId, status, groupActivationDate, clientMembers, groupMembers,submittedOnDate,currentUser);
}
public Group(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel, final String name,
final String externalId, final GroupingTypeStatus status, final LocalDate activationDate, final Set<Client> clientMembers,
final Set<Group> groupMembers) {
final Set<Group> groupMembers,final LocalDate submittedOnDate,final AppUser currentUser) {
this.office = office;
this.staff = staff;
this.groupLevel = groupLevel;
@@ -160,9 +179,25 @@ public final class Group extends AbstractPersistable<Long> {
if (groupMembers != null) {
this.groupMembers.addAll(groupMembers);
}
this.submittedBy = currentUser;
if (isDateInTheFuture(submittedOnDate)) {
final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.in.the.future",
defaultUserMessage,"SubmittedOnDate");
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.submittedOnDate = submittedOnDate.toDate();
}
public void activate(final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
public void activate(final AppUser currentUser,final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
if (isActive()) {
final String defaultUserMessage = "Cannot activate group. Group is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.already.active", defaultUserMessage,
@@ -188,6 +223,7 @@ public final class Group extends AbstractPersistable<Long> {
this.activationDate = activationLocalDate.toDate();
this.status = GroupingTypeStatus.ACTIVE.getValue();
this.activatedBy = currentUser;
}
public boolean isActivatedAfter(final LocalDate submittedOn) {
@@ -424,7 +460,7 @@ public final class Group extends AbstractPersistable<Long> {
return GroupingTypeStatus.fromInt(this.status).isClosed();
}
public void close(final CodeValue closureReason, final LocalDate closureDate) {
public void close(final AppUser currentUser,final CodeValue closureReason, final LocalDate closureDate) {
if (isClosed()) {
final String errorMessage = "Group with identifier " + getId() + " is alread closed.";
@@ -441,6 +477,7 @@ public final class Group extends AbstractPersistable<Long> {
this.closureReason = closureReason;
this.closureDate = closureDate.toDate();
this.status = GroupingTypeStatus.CLOSED.getValue();
this.closedBy = currentUser;
}
public boolean hasActiveClients() {
@@ -13,6 +13,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.data.GroupTimelineData;
import org.springframework.jdbc.core.RowMapper;
/**
@@ -29,11 +30,30 @@ public final class AllGroupTypesDataMapper implements RowMapper<GroupGeneralData
sqlBuilder.append("g.parent_id as centerId, pg.display_name as centerName, ");
sqlBuilder.append("g.staff_id as staffId, s.display_name as staffName, ");
sqlBuilder.append("g.status_enum as statusEnum, g.activation_date as activationDate, ");
sqlBuilder.append("g.closedon_date as closedOnDate, ");
sqlBuilder.append("g.submittedon_date as submittedOnDate, ");
sqlBuilder.append("sbu.username as submittedByUsername, ");
sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
sqlBuilder.append("sbu.lastname as submittedByLastname, ");
sqlBuilder.append("clu.username as closedByUsername, ");
sqlBuilder.append("clu.firstname as closedByFirstname, ");
sqlBuilder.append("clu.lastname as closedByLastname, ");
sqlBuilder.append("acu.username as activatedByUsername, ");
sqlBuilder.append("acu.firstname as activatedByFirstname, ");
sqlBuilder.append("acu.lastname as activatedByLastname, ");
sqlBuilder.append("g.hierarchy as hierarchy ");
sqlBuilder.append("from m_group g ");
sqlBuilder.append("join m_office o on o.id = g.office_id ");
sqlBuilder.append("left join m_staff s on s.id = g.staff_id ");
sqlBuilder.append("left join m_group pg on pg.id = g.parent_id ");
sqlBuilder.append("left join m_appuser sbu on sbu.id = g.submittedon_userid ");
sqlBuilder.append("left join m_appuser acu on acu.id = g.activatedon_userid ");
sqlBuilder.append("left join m_appuser clu on clu.id = g.closedon_userid ");
this.schemaSql = sqlBuilder.toString();
}
@@ -61,7 +81,26 @@ public final class AllGroupTypesDataMapper implements RowMapper<GroupGeneralData
final String staffName = rs.getString("staffName");
final String hierarchy = rs.getString("hierarchy");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
final String closedByUsername = rs.getString("closedByUsername");
final String closedByFirstname = rs.getString("closedByFirstname");
final String closedByLastname = rs.getString("closedByLastname");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final String submittedByFirstname = rs.getString("submittedByFirstname");
final String submittedByLastname = rs.getString("submittedByLastname");
final String activatedByUsername = rs.getString("activatedByUsername");
final String activatedByFirstname = rs.getString("activatedByFirstname");
final String activatedByLastname = rs.getString("activatedByLastname");
final GroupTimelineData timeline = new GroupTimelineData (submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername,
closedByFirstname, closedByLastname);
return GroupGeneralData.instance(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy);
staffName, hierarchy,timeline);
}
}
@@ -187,7 +187,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
final String hierarchy = rs.getString("hierarchy");
return GroupGeneralData.instance(id, name, externalId, status, activationDate, officeId, officeName, null, null, staffId,
staffName, hierarchy);
staffName, hierarchy,null);
}
}
@@ -51,6 +51,7 @@ import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -102,6 +103,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final String name = command.stringValueOfParameterNamed(GroupingTypesApiConstants.nameParamName);
final String externalId = command.stringValueOfParameterNamed(GroupingTypesApiConstants.externalIdParamName);
final AppUser currentUser = this.context.authenticatedUser();
Long officeId = null;
Group parentGroup = null;
@@ -130,9 +132,16 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final Set<Group> groupMembers = assembleSetOfChildGroups(officeId, command);
LocalDate submittedOnDate = new LocalDate();
if(command.hasParameter(GroupingTypesApiConstants.submittedOnDateParamName)){
submittedOnDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.submittedOnDateParamName);
}
final boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName);
final Group newGroup = Group.newGroup(groupOffice, staff, parentGroup, groupLevel, name, externalId, active, activationDate,
clientMembers, groupMembers);
clientMembers, groupMembers,submittedOnDate,currentUser);
// pre-save to generate id for use in group hierarchy
this.groupRepository.save(newGroup);
@@ -184,6 +193,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
try {
this.fromApiJsonDeserializer.validateForActivation(command, GroupingTypesApiConstants.GROUP_RESOURCE_NAME);
AppUser currentUser = this.context.authenticatedUser();
final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId);
final Locale locale = command.extractLocale();
@@ -191,7 +202,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(group.getOffice(), group.getGroupLevel(), activationDate);
group.activate(fmt, activationDate);
group.activate(currentUser,fmt, activationDate);
this.groupRepository.saveAndFlush(group);
@@ -416,6 +427,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final LocalDate closureDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.closureDateParamName);
final Long closureReasonId = command.longValueOfParameterNamed(GroupingTypesApiConstants.closureReasonIdParamName);
final AppUser currentUser = this.context.authenticatedUser();
final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId);
@@ -428,7 +441,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
validateLoansAndSavingsForGroupOrCenterClose(group, closureDate);
group.close(closureReason, closureDate);
group.close(currentUser,closureReason, closureDate);
this.groupRepository.saveAndFlush(group);
@@ -489,6 +502,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId);
final AppUser currentUser = this.context.authenticatedUser();
if (center.hasActiveGroups()) {
final String errorMessage = center.getGroupLevel().getLevelName()
+ " cannot be closed because of active groups associated with it.";
@@ -498,7 +513,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
validateLoansAndSavingsForGroupOrCenterClose(center, closureDate);
center.close(closureReason, closureDate);
center.close(currentUser,closureReason, closureDate);
this.groupRepository.saveAndFlush(center);
@@ -672,7 +672,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum);
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId,
null, groupParentId, null, groupStaffId, null, groupHierarchy);
null, groupParentId, null, groupStaffId, null, groupHierarchy,null);
}
final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter");