Merge pull request #666 from Musoni/MIFOSX-765

MIFOSX-765 - Client and Group timeline Implementation
This commit is contained in:
Keith Woodlock
2013-11-14 03:51:50 -08:00
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 staffIdParamName = "staffId";
public static final String closureDateParamName = "closureDate"; public static final String closureDateParamName = "closureDate";
public static final String closureReasonIdParamName = "closureReasonId"; public static final String closureReasonIdParamName = "closureReasonId";
public static final String submittedOnDateParamName="submittedOnDate";
// response parameters // response parameters
public static final String statusParamName = "status"; public static final String statusParamName = "status";
public static final String hierarchyParamName = "hierarchy"; public static final String hierarchyParamName = "hierarchy";
@@ -135,6 +135,7 @@ public class ClientsApiResource {
clientData = ClientData.templateOnTop(clientData, templateData); clientData = ClientData.templateOnTop(clientData, templateData);
} }
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); 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 Boolean imagePresent;
private final Long staffId; private final Long staffId;
private final String staffName; private final String staffName;
private final ClientTimelineData timeline;
// associations // associations
private final Collection<GroupGeneralData> groups; 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, public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection<OfficeData> officeOptions,
final Collection<StaffData> staffOptions, final Collection<CodeValueData> closureReasons) { 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, 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) { 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.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, templateData.officeOptions, 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) { 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.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, 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, 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) { final String officeName) {
return new ClientData(accountNo, status, officeId, officeName, null, null, id, firstname, middlename, lastname, fullname, 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) { 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, 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, 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 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 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, 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, 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 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 Collection<OfficeData> allowedOffices, final Collection<GroupGeneralData> groups, 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.accountNo = accountNo;
this.status = status; this.status = status;
if (status != null) { if (status != null) {
@@ -144,6 +151,8 @@ final public class ClientData implements Comparable<ClientData> {
this.officeOptions = allowedOffices; this.officeOptions = allowedOffices;
this.staffOptions = staffOptions; this.staffOptions = staffOptions;
this.closureReasons = closureReasons; this.closureReasons = closureReasons;
this.timeline = timeline;
} }
public Long id() { public Long id() {
@@ -170,6 +179,10 @@ final public class ClientData implements Comparable<ClientData> {
return this.imagePresent; return this.imagePresent;
} }
public ClientTimelineData getTimeline() {
return this.timeline;
}
@Override @Override
public int compareTo(final ClientData obj) { public int compareTo(final ClientData obj) {
if (obj == null) { return -1; } 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.Transient;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption; 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.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.api.ClientApiConstants;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity @Entity
@@ -121,7 +123,27 @@ public final class Client extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
private Date closureDate; 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 accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName); 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 lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName); 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; ClientStatus status = ClientStatus.PENDING;
boolean active = false; boolean active = false;
if (command.hasParameter("active")) { if (command.hasParameter("active")) {
active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName); active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
} }
LocalDate activationDate = null; LocalDate activationDate = null;
LocalDate officeJoiningDate = null; LocalDate officeJoiningDate = null;
if (active) { if (active) {
@@ -146,23 +179,48 @@ public final class Client extends AbstractPersistable<Long> {
officeJoiningDate = activationDate; 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() { 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 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)) { if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate(); this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true; this.accountNumberRequiresAutoGeneration = true;
} else { } else {
this.accountNumber = accountNo; 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.status = status.getValue();
this.office = office; this.office = office;
if (StringUtils.isNotBlank(externalId)) { if (StringUtils.isNotBlank(externalId)) {
@@ -246,7 +304,7 @@ public final class Client extends AbstractPersistable<Long> {
this.accountNumberRequiresAutoGeneration = false; 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()) { if (isActive()) {
final String defaultUserMessage = "Cannot activate client. Client is already active."; final String defaultUserMessage = "Cannot activate client. Client is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.already.active", defaultUserMessage, 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.activationDate = activationLocalDate.toDate();
this.activatedBy = currentUser;
this.officeJoiningDate = this.activationDate; this.officeJoiningDate = this.activationDate;
this.status = ClientStatus.ACTIVE.getValue(); this.status = ClientStatus.ACTIVE.getValue();
@@ -449,6 +508,10 @@ public final class Client extends AbstractPersistable<Long> {
this.displayName = nameBuilder.toString(); this.displayName = nameBuilder.toString();
} }
public LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
public LocalDate getActivationLocalDate() { public LocalDate getActivationLocalDate() {
LocalDate activationLocalDate = null; LocalDate activationLocalDate = null;
if (this.activationDate != null) { if (this.activationDate != null) {
@@ -545,9 +608,10 @@ public final class Client extends AbstractPersistable<Long> {
return this.groups; 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.closureReason = closureReason;
this.closureDate = closureDate; this.closureDate = closureDate;
this.closeddBy = currentUser;
this.status = ClientStatus.CLOSED.getValue(); 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.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData; 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.ClientEnumerations;
import org.mifosplatform.portfolio.client.domain.ClientStatus; import org.mifosplatform.portfolio.client.domain.ClientStatus;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; 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.fullname as fullname, c.display_name as displayName, ");
sqlBuilder.append("c.mobile_no as mobileNo, "); sqlBuilder.append("c.mobile_no as mobileNo, ");
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, "); 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("from m_client c ");
sqlBuilder.append("join m_office o on o.id = c.office_id "); 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("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_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_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(); this.schema = sqlBuilder.toString();
} }
@@ -305,8 +325,28 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long staffId = JdbcSupport.getLong(rs, "staffId"); final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName"); 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, 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.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.fullname as fullname, c.display_name as displayName, ");
builder.append("c.mobile_no as mobileNo, "); 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.activation_date as activationDate, c.image_id as imageId, ");
builder.append("c.staff_id as staffId, s.display_name as staffName "); builder.append("c.staff_id as staffId, s.display_name as staffName ");
builder.append("from m_client c "); builder.append("from m_client c ");
builder.append("join m_office o on o.id = c.office_id "); 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_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_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(); this.schema = builder.toString();
} }
@@ -378,8 +440,27 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long staffId = JdbcSupport.getLong(rs, "staffId"); final Long staffId = JdbcSupport.getLong(rs, "staffId");
final String staffName = rs.getString("staffName"); 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, 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.note.domain.NoteRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -136,6 +137,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
public CommandProcessingResult createClient(final JsonCommand command) { public CommandProcessingResult createClient(final JsonCommand command) {
try { try {
final AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json()); this.fromApiJsonDeserializer.validateForCreate(command.json());
final Long officeId = command.longValueOfParameterNamed(ClientApiConstants.officeIdParamName); final Long officeId = command.longValueOfParameterNamed(ClientApiConstants.officeIdParamName);
@@ -157,7 +161,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientOffice.getHierarchy()); 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); this.clientRepository.save(newClient);
@@ -235,7 +239,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate"); 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); this.clientRepository.saveAndFlush(client);
@@ -328,6 +333,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Override @Override
public CommandProcessingResult closeClient(final Long clientId, final JsonCommand command) { public CommandProcessingResult closeClient(final Long clientId, final JsonCommand command) {
try { try {
AppUser currentUser = this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateClose(command); this.fromApiJsonDeserializer.validateClose(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); 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); this.clientRepository.saveAndFlush(client);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -42,6 +42,8 @@ public class GroupingTypesApiConstants {
public static final String activationDateParamName = "activationDate"; public static final String activationDateParamName = "activationDate";
public static final String groupMembersParamName = "groupMembers"; public static final String groupMembersParamName = "groupMembers";
public static final String submittedOnDateParamName = "submittedOnDate";
// group parameters // group parameters
public static final String centerIdParamName = "centerId"; public static final String centerIdParamName = "centerId";
public static final String clientMembersParamName = "clientMembers"; public static final String clientMembersParamName = "clientMembers";
@@ -55,6 +57,7 @@ public class GroupingTypesApiConstants {
public static final String staffOptionsParamName = "staffOptions"; public static final String staffOptionsParamName = "staffOptions";
public static final String clientOptionsParamName = "clientOptions"; public static final String clientOptionsParamName = "clientOptions";
public static final String collectionMeetingCalendar = "collectionMeetingCalendar"; public static final String collectionMeetingCalendar = "collectionMeetingCalendar";
public static final String timeLine ="timeline";
// group close parameters // group close parameters
public static final String closureDateParamName = "closureDate"; 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, public static final Set<String> GROUP_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName, dateFormatParamName,
idParamName, nameParamName, externalIdParamName, centerIdParamName, officeIdParamName, staffIdParamName, activeParamName, 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, public static final Set<String> GROUP_ROLES_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(roleParamName,
clientIdParamName)); clientIdParamName));
@@ -86,7 +89,7 @@ public class GroupingTypesApiConstants {
public static final Set<String> GROUP_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName, nameParamName, public static final Set<String> GROUP_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName, nameParamName,
externalIdParamName, officeIdParamName, officeNameParamName, "parentId", "parentName", staffIdParamName, staffNameParamName, 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, public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName)); dateFormatParamName, activationDateParamName));
@@ -52,13 +52,14 @@ public class GroupGeneralData {
private final Collection<CodeValueData> availableRoles; private final Collection<CodeValueData> availableRoles;
private final GroupRoleData selectedRole; private final GroupRoleData selectedRole;
private final Collection<CodeValueData> closureReasons; private final Collection<CodeValueData> closureReasons;
private final GroupTimelineData timeline;
public static GroupGeneralData lookup(final Long groupId, final String groupName) { public static GroupGeneralData lookup(final Long groupId, final String groupName) {
final Collection<ClientData> clientMembers = null; final Collection<ClientData> clientMembers = null;
final Collection<GroupRoleData> groupRoles = null; final Collection<GroupRoleData> groupRoles = null;
final Collection<CodeValueData> closureReasons = null; final Collection<CodeValueData> closureReasons = null;
return new GroupGeneralData(groupId, groupName, null, null, null, null, null, null, null, null, null, null, clientMembers, 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, 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, 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, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null, null, null,
closureReasons); closureReasons,null);
} }
public static GroupGeneralData withTemplate(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping) { 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.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions, grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions,
templatedGrouping.staffOptions, templatedGrouping.clientOptions, grouping.groupRoles, templatedGrouping.availableRoles, 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, 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.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole, calendarsData, 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, 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 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<ClientData> clientMembers = null;
final Collection<CenterData> centerOptions = 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, return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, 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, 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<StaffData> staffOptions, final Collection<ClientData> clientOptions,
final Collection<GroupRoleData> groupRoles, final Collection<CodeValueData> availableRoles, final GroupRoleData role, final Collection<GroupRoleData> groupRoles, final Collection<CodeValueData> availableRoles, final GroupRoleData role,
final Collection<CalendarData> calendarsData, final CalendarData collectionMeetingCalendar, final Collection<CalendarData> calendarsData, final CalendarData collectionMeetingCalendar,
final Collection<CodeValueData> closureReasons) { final Collection<CodeValueData> closureReasons,final GroupTimelineData timeline) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.externalId = externalId; this.externalId = externalId;
@@ -159,6 +160,7 @@ public class GroupGeneralData {
this.calendarsData = calendarsData; this.calendarsData = calendarsData;
this.collectionMeetingCalendar = collectionMeetingCalendar; this.collectionMeetingCalendar = collectionMeetingCalendar;
this.closureReasons = closureReasons; this.closureReasons = closureReasons;
this.timeline = timeline;
} }
public Long getId() { public Long getId() {
@@ -190,7 +192,7 @@ public class GroupGeneralData {
grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName,
grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions,
grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole, grouping.calendarsData, 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) { 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, return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId,
staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, 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.ClientExistInGroupException;
import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException;
import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException; import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -64,6 +65,10 @@ public final class Group extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
private Date activationDate; private Date activationDate;
@ManyToOne(optional = true)
@JoinColumn(name = "activatedon_userid", nullable = true)
private AppUser activatedBy;
@ManyToOne @ManyToOne
@JoinColumn(name = "office_id", nullable = false) @JoinColumn(name = "office_id", nullable = false)
private Office office; private Office office;
@@ -106,6 +111,18 @@ public final class Group extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
private Date closureDate; 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() { public Group() {
this.name = null; this.name = null;
this.externalId = 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, 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 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; GroupingTypeStatus status = GroupingTypeStatus.PENDING;
LocalDate groupActivationDate = null; LocalDate groupActivationDate = null;
@@ -124,12 +141,14 @@ public final class Group extends AbstractPersistable<Long> {
// group is made active // 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, 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 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.office = office;
this.staff = staff; this.staff = staff;
this.groupLevel = groupLevel; this.groupLevel = groupLevel;
@@ -160,9 +179,25 @@ public final class Group extends AbstractPersistable<Long> {
if (groupMembers != null) { if (groupMembers != null) {
this.groupMembers.addAll(groupMembers); 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()) { if (isActive()) {
final String defaultUserMessage = "Cannot activate group. Group is already active."; final String defaultUserMessage = "Cannot activate group. Group is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.already.active", defaultUserMessage, 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.activationDate = activationLocalDate.toDate();
this.status = GroupingTypeStatus.ACTIVE.getValue(); this.status = GroupingTypeStatus.ACTIVE.getValue();
this.activatedBy = currentUser;
} }
public boolean isActivatedAfter(final LocalDate submittedOn) { public boolean isActivatedAfter(final LocalDate submittedOn) {
@@ -424,7 +460,7 @@ public final class Group extends AbstractPersistable<Long> {
return GroupingTypeStatus.fromInt(this.status).isClosed(); 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()) { if (isClosed()) {
final String errorMessage = "Group with identifier " + getId() + " is alread closed."; 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.closureReason = closureReason;
this.closureDate = closureDate.toDate(); this.closureDate = closureDate.toDate();
this.status = GroupingTypeStatus.CLOSED.getValue(); this.status = GroupingTypeStatus.CLOSED.getValue();
this.closedBy = currentUser;
} }
public boolean hasActiveClients() { public boolean hasActiveClients() {
@@ -13,6 +13,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.portfolio.client.domain.ClientEnumerations; import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.data.GroupTimelineData;
import org.springframework.jdbc.core.RowMapper; 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.parent_id as centerId, pg.display_name as centerName, ");
sqlBuilder.append("g.staff_id as staffId, s.display_name as staffName, "); 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.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("g.hierarchy as hierarchy ");
sqlBuilder.append("from m_group g "); sqlBuilder.append("from m_group g ");
sqlBuilder.append("join m_office o on o.id = g.office_id "); 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_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_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(); this.schemaSql = sqlBuilder.toString();
} }
@@ -61,7 +81,26 @@ public final class AllGroupTypesDataMapper implements RowMapper<GroupGeneralData
final String staffName = rs.getString("staffName"); final String staffName = rs.getString("staffName");
final String hierarchy = rs.getString("hierarchy"); 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, 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"); final String hierarchy = rs.getString("hierarchy");
return GroupGeneralData.instance(id, name, externalId, status, activationDate, officeId, officeName, null, null, staffId, 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.note.domain.NoteRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; 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 name = command.stringValueOfParameterNamed(GroupingTypesApiConstants.nameParamName);
final String externalId = command.stringValueOfParameterNamed(GroupingTypesApiConstants.externalIdParamName); final String externalId = command.stringValueOfParameterNamed(GroupingTypesApiConstants.externalIdParamName);
final AppUser currentUser = this.context.authenticatedUser();
Long officeId = null; Long officeId = null;
Group parentGroup = null; Group parentGroup = null;
@@ -130,9 +132,16 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final Set<Group> groupMembers = assembleSetOfChildGroups(officeId, command); 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 boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName);
final Group newGroup = Group.newGroup(groupOffice, staff, parentGroup, groupLevel, name, externalId, active, activationDate, 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 // pre-save to generate id for use in group hierarchy
this.groupRepository.save(newGroup); this.groupRepository.save(newGroup);
@@ -184,6 +193,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
try { try {
this.fromApiJsonDeserializer.validateForActivation(command, GroupingTypesApiConstants.GROUP_RESOURCE_NAME); this.fromApiJsonDeserializer.validateForActivation(command, GroupingTypesApiConstants.GROUP_RESOURCE_NAME);
AppUser currentUser = this.context.authenticatedUser();
final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId); final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId);
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
@@ -191,7 +202,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate"); final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(group.getOffice(), group.getGroupLevel(), activationDate); validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(group.getOffice(), group.getGroupLevel(), activationDate);
group.activate(fmt, activationDate); group.activate(currentUser,fmt, activationDate);
this.groupRepository.saveAndFlush(group); this.groupRepository.saveAndFlush(group);
@@ -416,6 +427,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final LocalDate closureDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.closureDateParamName); final LocalDate closureDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.closureDateParamName);
final Long closureReasonId = command.longValueOfParameterNamed(GroupingTypesApiConstants.closureReasonIdParamName); final Long closureReasonId = command.longValueOfParameterNamed(GroupingTypesApiConstants.closureReasonIdParamName);
final AppUser currentUser = this.context.authenticatedUser();
final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection( final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId); GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId);
@@ -428,7 +441,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
validateLoansAndSavingsForGroupOrCenterClose(group, closureDate); validateLoansAndSavingsForGroupOrCenterClose(group, closureDate);
group.close(closureReason, closureDate); group.close(currentUser,closureReason, closureDate);
this.groupRepository.saveAndFlush(group); this.groupRepository.saveAndFlush(group);
@@ -489,6 +502,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection( final CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId); GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId);
final AppUser currentUser = this.context.authenticatedUser();
if (center.hasActiveGroups()) { if (center.hasActiveGroups()) {
final String errorMessage = center.getGroupLevel().getLevelName() final String errorMessage = center.getGroupLevel().getLevelName()
+ " cannot be closed because of active groups associated with it."; + " cannot be closed because of active groups associated with it.";
@@ -498,7 +513,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
validateLoansAndSavingsForGroupOrCenterClose(center, closureDate); validateLoansAndSavingsForGroupOrCenterClose(center, closureDate);
center.close(closureReason, closureDate); center.close(currentUser,closureReason, closureDate);
this.groupRepository.saveAndFlush(center); this.groupRepository.saveAndFlush(center);
@@ -672,7 +672,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum); final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum);
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId, 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"); final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter");