From 0de4adf6e5018c1d5b24ff8d063d0160a54d3ebb Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 9 Sep 2013 09:54:42 +0100 Subject: [PATCH 1/6] MIFOSX-648: allow user to fetch their own account data no matter what permissions they hold. --- .../savings/domain/SavingsProduct.java | 19 +- .../api/UsersApiResource.java | 30 +- .../useradministration/domain/AppUser.java | 935 +++++++++--------- 3 files changed, 472 insertions(+), 512 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index 2f9da549c..577ce2edb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -73,7 +73,7 @@ public class SavingsProduct extends AbstractPersistable { /** * The interest period is the span of time at the end of which savings in a * client's account earn interest. - * + * * A value from the {@link SavingsCompoundingInterestPeriodType} * enumeration. */ @@ -119,9 +119,8 @@ public class SavingsProduct extends AbstractPersistable { @Column(name = "withdrawal_fee_type_enum", nullable = true) private Integer withdrawalFeeType; - + @Column(name = "withdrawal_fee_for_transfer") - @SuppressWarnings("unused") private boolean withdrawalFeeApplicableForTransfer; @Column(name = "annual_fee_amount", scale = 6, precision = 19, nullable = true) @@ -138,12 +137,12 @@ public class SavingsProduct extends AbstractPersistable { final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, - final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, boolean withdrawalFeeApplicableForTransfer, + final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType) { return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeApplicableForTransfer, annualFeeAmount, + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeApplicableForTransfer, annualFeeAmount, annualFeeOnMonthDay, accountingRuleType); } @@ -376,7 +375,7 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(withdrawalFeeTypeParamName, newValue); this.withdrawalFeeType = newValue != null ? SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue; } - + if(command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)){ final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); actualChanges.put(withdrawalFeeForTransfersParamName, newValue); @@ -484,16 +483,16 @@ public class SavingsProduct extends AbstractPersistable { .resource(SAVINGS_PRODUCT_RESOURCE_NAME); if (this.lockinPeriodFrequency == null) { - baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).ignoreIfNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).ignoreIfNull() .inMinMaxRange(0, 3); if (this.lockinPeriodFrequencyType != null) { - baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequency).notNull() .integerZeroOrGreater(); } } else { - baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequencyType).integerZeroOrGreater(); - baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequencyType).integerZeroOrGreater(); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).notNull() .inMinMaxRange(0, 3); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/UsersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/UsersApiResource.java index f23fa8c3a..432201399 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/UsersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/UsersApiResource.java @@ -75,25 +75,25 @@ public class UsersApiResource { @GET @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveUsers(@Context final UriInfo uriInfo) { + public String retrieveAll(@Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); final Collection users = this.readPlatformService.retrieveAllUsers(); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, users, RESPONSE_DATA_PARAMETERS); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, users, this.RESPONSE_DATA_PARAMETERS); } @GET @Path("{userId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveUser(@PathParam("userId") final Long userId, @Context final UriInfo uriInfo) { + public String retrieveOne(@PathParam("userId") final Long userId, @Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions, userId); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); AppUserData user = this.readPlatformService.retrieveUser(userId); if (settings.isTemplate()) { @@ -101,27 +101,27 @@ public class UsersApiResource { user = AppUserData.template(user, offices); } - return this.toApiJsonSerializer.serialize(settings, user, RESPONSE_DATA_PARAMETERS); + return this.toApiJsonSerializer.serialize(settings, user, this.RESPONSE_DATA_PARAMETERS); } @GET @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String newUserDetails(@Context final UriInfo uriInfo) { + public String template(@Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); final AppUserData user = this.readPlatformService.retrieveNewUserDetails(); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, user, RESPONSE_DATA_PARAMETERS); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, user, this.RESPONSE_DATA_PARAMETERS); } @POST @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String createUser(final String apiRequestBodyAsJson) { + public String create(final String apiRequestBodyAsJson) { final CommandWrapper commandRequest = new CommandWrapperBuilder() // .createUser() // @@ -137,7 +137,7 @@ public class UsersApiResource { @Path("{userId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String updateUser(@PathParam("userId") final Long userId, final String apiRequestBodyAsJson) { + public String update(@PathParam("userId") final Long userId, final String apiRequestBodyAsJson) { final CommandWrapper commandRequest = new CommandWrapperBuilder() // .updateUser(userId) // @@ -153,7 +153,7 @@ public class UsersApiResource { @Path("{userId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String deleteUser(@PathParam("userId") final Long userId) { + public String delete(@PathParam("userId") final Long userId) { final CommandWrapper commandRequest = new CommandWrapperBuilder() // .deleteUser(userId) // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java index 26feeae57..4e1e6f55b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java @@ -42,491 +42,452 @@ import org.springframework.security.core.userdetails.User; @Table(name = "m_appuser", uniqueConstraints = @UniqueConstraint(columnNames = { "username" }, name = "username_org")) public class AppUser extends AbstractPersistable implements PlatformUser { - private final static Logger logger = LoggerFactory.getLogger(AppUser.class); - - @Column(name = "email", nullable = false, length = 100) - private String email; - - @Column(name = "username", nullable = false, length = 100) - private String username; - - @Column(name = "firstname", nullable = false, length = 100) - private String firstname; - - @Column(name = "lastname", nullable = false, length = 100) - private String lastname; - - @Column(name = "password", nullable = false) - private String password; - - @Column(name = "nonexpired", nullable = false) - private boolean accountNonExpired; - - @Column(name = "nonlocked", nullable = false) - private final boolean accountNonLocked; - - @Column(name = "nonexpired_credentials", nullable = false) - private final boolean credentialsNonExpired; - - @Column(name = "enabled", nullable = false) - private boolean enabled; - - @Column(name = "firsttime_login_remaining", nullable = false) - private boolean firstTimeLoginRemaining; - - @Column(name = "is_deleted", nullable = false) - private boolean deleted; - - @ManyToOne - @JoinColumn(name = "office_id", nullable = false) - private Office office; - - @ManyToOne - @JoinColumn(name = "staff_id", nullable = true) - private Staff staff; - - @ManyToMany(fetch = FetchType.EAGER) - @JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) - private Set roles; - - public static AppUser fromJson(final Office userOffice, - final Set allRoles, final JsonCommand command) { - - final String username = command.stringValueOfParameterNamed("username"); - String password = command.stringValueOfParameterNamed("password"); - final Boolean sendPasswordToEmail = command - .booleanObjectValueOfParameterNamed("sendPasswordToEmail"); - - if (sendPasswordToEmail.booleanValue()) { - password = new RandomPasswordGenerator(13).generate(); - } - - final boolean userEnabled = true; - final boolean userAccountNonExpired = true; - final boolean userCredentialsNonExpired = true; - final boolean userAccountNonLocked = true; - - final Collection authorities = new ArrayList(); - authorities.add(new SimpleGrantedAuthority( - "DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION")); - - final User user = new User(username, password, userEnabled, - userAccountNonExpired, userCredentialsNonExpired, - userAccountNonLocked, authorities); - - final String email = command.stringValueOfParameterNamed("email"); - final String firstname = command - .stringValueOfParameterNamed("firstname"); - final String lastname = command.stringValueOfParameterNamed("lastname"); - - return new AppUser(userOffice, user, allRoles, email, firstname, - lastname); - } - - protected AppUser() { - this.accountNonLocked = false; - this.credentialsNonExpired = false; - this.roles = new HashSet(); - } - - public AppUser(final Office office, final User user, final Set roles, - final String email, final String firstname, final String lastname) { - this.office = office; - this.email = email.trim(); - this.username = user.getUsername().trim(); - this.firstname = firstname.trim(); - this.lastname = lastname.trim(); - this.password = user.getPassword().trim(); - this.accountNonExpired = user.isAccountNonExpired(); - this.accountNonLocked = user.isAccountNonLocked(); - this.credentialsNonExpired = user.isCredentialsNonExpired(); - this.enabled = user.isEnabled(); - this.roles = roles; - this.firstTimeLoginRemaining = true; - } - - public EnumOptionData organisationalRoleData() { - EnumOptionData organisationalRole = null; - if (this.staff != null) { - organisationalRole = this.staff.organisationalRoleData(); - } - return organisationalRole; - } - - public void updatePassword(final String encodePassword) { - this.password = encodePassword; - this.firstTimeLoginRemaining = false; - } - - public void changeOffice(final Office differentOffice) { - this.office = differentOffice; - } - - public void updateRoles(final Set allRoles) { - if (!allRoles.isEmpty()) { - this.roles.clear(); - this.roles = allRoles; - } - } - - public Map update(final JsonCommand command, - final PlatformPasswordEncoder platformPasswordEncoder) { - - final Map actualChanges = new LinkedHashMap( - 7); - - // unencoded password provided - final String passwordParamName = "password"; - final String passwordEncodedParamName = "passwordEncoded"; - if (command.hasParameter(passwordParamName)) { - if (command.isChangeInPasswordParameterNamed(passwordParamName, - this.password, platformPasswordEncoder, getId())) { - final String passwordEncodedValue = command - .passwordValueOfParameterNamed(passwordParamName, - platformPasswordEncoder, getId()); - actualChanges.put(passwordEncodedParamName, - passwordEncodedValue); - updatePassword(passwordEncodedValue); - } - } - - if (command.hasParameter(passwordEncodedParamName)) { - if (command.isChangeInStringParameterNamed( - passwordEncodedParamName, this.password)) { - final String newValue = command - .stringValueOfParameterNamed(passwordEncodedParamName); - actualChanges.put(passwordEncodedParamName, newValue); - updatePassword(newValue); - } - } - - final String officeIdParamName = "officeId"; - if (command.isChangeInLongParameterNamed(officeIdParamName, - this.office.getId())) { - final Long newValue = command - .longValueOfParameterNamed(officeIdParamName); - actualChanges.put(officeIdParamName, newValue); - } - - final String rolesParamName = "roles"; - if (command.isChangeInArrayParameterNamed(rolesParamName, - getRolesAsIdStringArray())) { - final String[] newValue = command - .arrayValueOfParameterNamed(rolesParamName); - actualChanges.put(rolesParamName, newValue); - } - - final String usernameParamName = "username"; - if (command.isChangeInStringParameterNamed(usernameParamName, - this.username)) { - final String newValue = command - .stringValueOfParameterNamed(usernameParamName); - actualChanges.put(usernameParamName, newValue); - this.username = newValue; - } - - final String firstnameParamName = "firstname"; - if (command.isChangeInStringParameterNamed(firstnameParamName, - this.firstname)) { - final String newValue = command - .stringValueOfParameterNamed(firstnameParamName); - actualChanges.put(firstnameParamName, newValue); - this.firstname = newValue; - } - - final String lastnameParamName = "lastname"; - if (command.isChangeInStringParameterNamed(lastnameParamName, - this.lastname)) { - final String newValue = command - .stringValueOfParameterNamed(lastnameParamName); - actualChanges.put(lastnameParamName, newValue); - this.lastname = newValue; - } - - final String emailParamName = "email"; - if (command.isChangeInStringParameterNamed(emailParamName, this.email)) { - final String newValue = command - .stringValueOfParameterNamed(emailParamName); - actualChanges.put(emailParamName, newValue); - this.email = newValue; - } - - return actualChanges; - } - - private String[] getRolesAsIdStringArray() { - final List roleIds = new ArrayList(); - - for (final Role role : this.roles) { - roleIds.add(role.getId().toString()); - } - - return roleIds.toArray(new String[roleIds.size()]); - } - - /** - * Delete is a soft delete. Updates flag so it wont appear in - * query/report results. - * - * Any fields with unique constraints and prepended with id of record. - */ - public void delete() { - this.deleted = true; - this.enabled = false; - this.accountNonExpired = false; - this.firstTimeLoginRemaining = true; - this.username = getId() + "_DELETED_" + this.username; - } - - public boolean isDeleted() { - return this.deleted; - } - - @Override - public Collection getAuthorities() { - return populateGrantedAuthorities(); - } - - private List populateGrantedAuthorities() { - final List grantedAuthorities = new ArrayList(); - for (final Role role : this.roles) { - final Collection permissions = role.getPermissions(); - for (final Permission permission : permissions) { - grantedAuthorities.add(new SimpleGrantedAuthority(permission - .getCode())); - } - } - return grantedAuthorities; - } - - @Override - public String getPassword() { - return this.password; - } - - @Override - public String getUsername() { - return this.username; - } - - @Override - public boolean isAccountNonExpired() { - return this.accountNonExpired; - } - - @Override - public boolean isAccountNonLocked() { - return this.accountNonLocked; - } - - @Override - public boolean isCredentialsNonExpired() { - return this.credentialsNonExpired; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - public String getFirstname() { - return this.firstname; - } - - public String getLastname() { - return this.lastname; - } - - public String getEmail() { - return this.email; - } - - public Set getRoles() { - return this.roles; - } - - public Office getOffice() { - return this.office; - } - - public boolean canNotApproveLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN"); - } - - public boolean canNotRejectLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN"); - } - - public boolean canNotWithdrawByClientLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN"); - } - - public boolean canNotDisburseLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN"); - } - - public boolean canNotMakeRepaymentOnLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN"); - } - - public boolean hasNotPermissionForReport(final String reportName) { - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", - "REPORTING_SUPER_USER", "READ_" + reportName)) { - return true; - } - - return false; - } - - public boolean hasNotPermissionForDatatable(final String datatable, - final String accessType) { - - final String matchPermission = accessType + "_" + datatable; - - if (accessType.equalsIgnoreCase("READ")) { - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", - matchPermission)) { - return true; - } - - return false; - } - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) { - return true; - } - - return false; - } - - public boolean hasNotPermissionForAnyOf(final String... permissionCodes) { - boolean hasNotPermission = true; - for (final String permissionCode : permissionCodes) { - final boolean checkPermission = hasPermissionTo(permissionCode); - if (checkPermission) { - hasNotPermission = false; - break; - } - } - return hasNotPermission; - } - - public void validateHasReadPermission(final String resourceType) { - - final String authorizationMessage = "User has no authority to view " - + resourceType.toLowerCase() + "s"; - final String matchPermission = "READ_" + resourceType.toUpperCase(); - - if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", - matchPermission)) { - return; - } - - throw new NoAuthorizationException(authorizationMessage); - } - - private boolean hasNotPermissionTo(final String permissionCode) { - return !hasPermissionTo(permissionCode); - } - - private boolean hasPermissionTo(final String permissionCode) { - boolean hasPermission = hasAllFunctionsPermission(); - if (!hasPermission) { - for (final Role role : this.roles) { - if (role.hasPermissionTo(permissionCode)) { - hasPermission = true; - break; - } - } - } - return hasPermission; - } - - private boolean hasAllFunctionsPermission() { - boolean match = false; - for (final Role role : this.roles) { - if (role.hasPermissionTo("ALL_FUNCTIONS")) { - match = true; - break; - } - } - return match; - } - - public boolean hasIdOf(final Long userId) { - return getId().equals(userId); - } - - private boolean hasNotAnyPermission(final List permissions) { - return !hasAnyPermission(permissions); - } - - private boolean hasAnyPermission(final List permissions) { - boolean hasAtLeastOneOf = false; - - for (final String permissionCode : permissions) { - if (hasPermissionTo(permissionCode)) { - hasAtLeastOneOf = true; - break; - } - } - - return hasAtLeastOneOf; - } - - public void validateHasPermissionTo(final String function, - final List allowedPermissions) { - if (hasNotAnyPermission(allowedPermissions)) { - final String authorizationMessage = "User has no authority to: " - + function; - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasPermissionTo(final String function) { - if (hasNotPermissionTo(function)) { - final String authorizationMessage = "User has no authority to: " - + function; - logger.info("Unauthorized access: userId: " + getId() + " action: " - + function + " allowed: " + getAuthorities()); - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasCheckerPermissionTo(final String function) { - - final String checkerPermissionName = function.toUpperCase() - + "_CHECKER"; - if (hasNotPermissionTo("CHECKER_SUPER_USER") - && hasNotPermissionTo(checkerPermissionName)) { - final String authorizationMessage = "User has no authority to be a checker for: " - + function; - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasDatatableReadPermission(final String datatable) { - if (hasNotPermissionForDatatable(datatable, "READ")) { - throw new NoAuthorizationException( - "Not authorised to read datatable: " + datatable); - } - } - - public Long getStaffId() { - Long staffId = null; - if (this.staff != null) { - staffId = this.staff.getId(); - } - return staffId; - } - - public String getStaffDisplayName() { - String staffDisplayName = null; - if (this.staff != null) { - staffDisplayName = this.staff.displayName(); - } - return staffDisplayName; - } + private final static Logger logger = LoggerFactory.getLogger(AppUser.class); + + @Column(name = "email", nullable = false, length = 100) + private String email; + + @Column(name = "username", nullable = false, length = 100) + private String username; + + @Column(name = "firstname", nullable = false, length = 100) + private String firstname; + + @Column(name = "lastname", nullable = false, length = 100) + private String lastname; + + @Column(name = "password", nullable = false) + private String password; + + @Column(name = "nonexpired", nullable = false) + private boolean accountNonExpired; + + @Column(name = "nonlocked", nullable = false) + private final boolean accountNonLocked; + + @Column(name = "nonexpired_credentials", nullable = false) + private final boolean credentialsNonExpired; + + @Column(name = "enabled", nullable = false) + private boolean enabled; + + @Column(name = "firsttime_login_remaining", nullable = false) + private boolean firstTimeLoginRemaining; + + @Column(name = "is_deleted", nullable = false) + private boolean deleted; + + @ManyToOne + @JoinColumn(name = "office_id", nullable = false) + private Office office; + + @ManyToOne + @JoinColumn(name = "staff_id", nullable = true) + private Staff staff; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) + private Set roles; + + public static AppUser fromJson(final Office userOffice, final Set allRoles, final JsonCommand command) { + + final String username = command.stringValueOfParameterNamed("username"); + String password = command.stringValueOfParameterNamed("password"); + final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail"); + + if (sendPasswordToEmail.booleanValue()) { + password = new RandomPasswordGenerator(13).generate(); + } + + final boolean userEnabled = true; + final boolean userAccountNonExpired = true; + final boolean userCredentialsNonExpired = true; + final boolean userAccountNonLocked = true; + + final Collection authorities = new ArrayList(); + authorities.add(new SimpleGrantedAuthority("DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION")); + + final User user = new User(username, password, userEnabled, userAccountNonExpired, userCredentialsNonExpired, userAccountNonLocked, + authorities); + + final String email = command.stringValueOfParameterNamed("email"); + final String firstname = command.stringValueOfParameterNamed("firstname"); + final String lastname = command.stringValueOfParameterNamed("lastname"); + + return new AppUser(userOffice, user, allRoles, email, firstname, lastname); + } + + protected AppUser() { + this.accountNonLocked = false; + this.credentialsNonExpired = false; + this.roles = new HashSet(); + } + + public AppUser(final Office office, final User user, final Set roles, final String email, final String firstname, + final String lastname) { + this.office = office; + this.email = email.trim(); + this.username = user.getUsername().trim(); + this.firstname = firstname.trim(); + this.lastname = lastname.trim(); + this.password = user.getPassword().trim(); + this.accountNonExpired = user.isAccountNonExpired(); + this.accountNonLocked = user.isAccountNonLocked(); + this.credentialsNonExpired = user.isCredentialsNonExpired(); + this.enabled = user.isEnabled(); + this.roles = roles; + this.firstTimeLoginRemaining = true; + } + + public EnumOptionData organisationalRoleData() { + EnumOptionData organisationalRole = null; + if (this.staff != null) { + organisationalRole = this.staff.organisationalRoleData(); + } + return organisationalRole; + } + + public void updatePassword(final String encodePassword) { + this.password = encodePassword; + this.firstTimeLoginRemaining = false; + } + + public void changeOffice(final Office differentOffice) { + this.office = differentOffice; + } + + public void updateRoles(final Set allRoles) { + if (!allRoles.isEmpty()) { + this.roles.clear(); + this.roles = allRoles; + } + } + + public Map update(final JsonCommand command, final PlatformPasswordEncoder platformPasswordEncoder) { + + final Map actualChanges = new LinkedHashMap(7); + + // unencoded password provided + final String passwordParamName = "password"; + final String passwordEncodedParamName = "passwordEncoded"; + if (command.hasParameter(passwordParamName)) { + if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, getId())) { + final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder, + getId()); + actualChanges.put(passwordEncodedParamName, passwordEncodedValue); + updatePassword(passwordEncodedValue); + } + } + + if (command.hasParameter(passwordEncodedParamName)) { + if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) { + final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName); + actualChanges.put(passwordEncodedParamName, newValue); + updatePassword(newValue); + } + } + + final String officeIdParamName = "officeId"; + if (command.isChangeInLongParameterNamed(officeIdParamName, this.office.getId())) { + final Long newValue = command.longValueOfParameterNamed(officeIdParamName); + actualChanges.put(officeIdParamName, newValue); + } + + final String rolesParamName = "roles"; + if (command.isChangeInArrayParameterNamed(rolesParamName, getRolesAsIdStringArray())) { + final String[] newValue = command.arrayValueOfParameterNamed(rolesParamName); + actualChanges.put(rolesParamName, newValue); + } + + final String usernameParamName = "username"; + if (command.isChangeInStringParameterNamed(usernameParamName, this.username)) { + final String newValue = command.stringValueOfParameterNamed(usernameParamName); + actualChanges.put(usernameParamName, newValue); + this.username = newValue; + } + + final String firstnameParamName = "firstname"; + if (command.isChangeInStringParameterNamed(firstnameParamName, this.firstname)) { + final String newValue = command.stringValueOfParameterNamed(firstnameParamName); + actualChanges.put(firstnameParamName, newValue); + this.firstname = newValue; + } + + final String lastnameParamName = "lastname"; + if (command.isChangeInStringParameterNamed(lastnameParamName, this.lastname)) { + final String newValue = command.stringValueOfParameterNamed(lastnameParamName); + actualChanges.put(lastnameParamName, newValue); + this.lastname = newValue; + } + + final String emailParamName = "email"; + if (command.isChangeInStringParameterNamed(emailParamName, this.email)) { + final String newValue = command.stringValueOfParameterNamed(emailParamName); + actualChanges.put(emailParamName, newValue); + this.email = newValue; + } + + return actualChanges; + } + + private String[] getRolesAsIdStringArray() { + final List roleIds = new ArrayList(); + + for (final Role role : this.roles) { + roleIds.add(role.getId().toString()); + } + + return roleIds.toArray(new String[roleIds.size()]); + } + + /** + * Delete is a soft delete. Updates flag so it wont appear in + * query/report results. + * + * Any fields with unique constraints and prepended with id of record. + */ + public void delete() { + this.deleted = true; + this.enabled = false; + this.accountNonExpired = false; + this.firstTimeLoginRemaining = true; + this.username = getId() + "_DELETED_" + this.username; + } + + public boolean isDeleted() { + return this.deleted; + } + + @Override + public Collection getAuthorities() { + return populateGrantedAuthorities(); + } + + private List populateGrantedAuthorities() { + final List grantedAuthorities = new ArrayList(); + for (final Role role : this.roles) { + final Collection permissions = role.getPermissions(); + for (final Permission permission : permissions) { + grantedAuthorities.add(new SimpleGrantedAuthority(permission.getCode())); + } + } + return grantedAuthorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return this.accountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + return this.accountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + return this.credentialsNonExpired; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public String getFirstname() { + return this.firstname; + } + + public String getLastname() { + return this.lastname; + } + + public String getEmail() { + return this.email; + } + + public Set getRoles() { + return this.roles; + } + + public Office getOffice() { + return this.office; + } + + public boolean canNotApproveLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN"); + } + + public boolean canNotRejectLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN"); + } + + public boolean canNotWithdrawByClientLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN"); + } + + public boolean canNotDisburseLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN"); + } + + public boolean canNotMakeRepaymentOnLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN"); + } + + public boolean hasNotPermissionForReport(final String reportName) { + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "REPORTING_SUPER_USER", "READ_" + reportName)) { return true; } + + return false; + } + + public boolean hasNotPermissionForDatatable(final String datatable, final String accessType) { + + final String matchPermission = accessType + "_" + datatable; + + if (accessType.equalsIgnoreCase("READ")) { + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) { return true; } + + return false; + } + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) { return true; } + + return false; + } + + public boolean hasNotPermissionForAnyOf(final String... permissionCodes) { + boolean hasNotPermission = true; + for (final String permissionCode : permissionCodes) { + final boolean checkPermission = hasPermissionTo(permissionCode); + if (checkPermission) { + hasNotPermission = false; + break; + } + } + return hasNotPermission; + } + + public void validateHasReadPermission(final String resourceType) { + + final String authorizationMessage = "User has no authority to view " + resourceType.toLowerCase() + "s"; + final String matchPermission = "READ_" + resourceType.toUpperCase(); + + if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) { return; } + + throw new NoAuthorizationException(authorizationMessage); + } + + private boolean hasNotPermissionTo(final String permissionCode) { + return !hasPermissionTo(permissionCode); + } + + private boolean hasPermissionTo(final String permissionCode) { + boolean hasPermission = hasAllFunctionsPermission(); + if (!hasPermission) { + for (final Role role : this.roles) { + if (role.hasPermissionTo(permissionCode)) { + hasPermission = true; + break; + } + } + } + return hasPermission; + } + + private boolean hasAllFunctionsPermission() { + boolean match = false; + for (final Role role : this.roles) { + if (role.hasPermissionTo("ALL_FUNCTIONS")) { + match = true; + break; + } + } + return match; + } + + public boolean hasIdOf(final Long userId) { + return getId().equals(userId); + } + + private boolean hasNotAnyPermission(final List permissions) { + return !hasAnyPermission(permissions); + } + + private boolean hasAnyPermission(final List permissions) { + boolean hasAtLeastOneOf = false; + + for (final String permissionCode : permissions) { + if (hasPermissionTo(permissionCode)) { + hasAtLeastOneOf = true; + break; + } + } + + return hasAtLeastOneOf; + } + + public void validateHasPermissionTo(final String function, final List allowedPermissions) { + if (hasNotAnyPermission(allowedPermissions)) { + final String authorizationMessage = "User has no authority to: " + function; + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasPermissionTo(final String function) { + if (hasNotPermissionTo(function)) { + final String authorizationMessage = "User has no authority to: " + function; + logger.info("Unauthorized access: userId: " + getId() + " action: " + function + " allowed: " + getAuthorities()); + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasReadPermission(final String function, final Long userId) { + if ("USER".equalsIgnoreCase(function) && userId.equals(getId())) { + // abstain from validation as user allowed fetch their own data no matter what permissions they have. + } else { + validateHasPermissionTo(function); + } + } + + public void validateHasCheckerPermissionTo(final String function) { + + final String checkerPermissionName = function.toUpperCase() + "_CHECKER"; + if (hasNotPermissionTo("CHECKER_SUPER_USER") && hasNotPermissionTo(checkerPermissionName)) { + final String authorizationMessage = "User has no authority to be a checker for: " + function; + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasDatatableReadPermission(final String datatable) { + if (hasNotPermissionForDatatable(datatable, "READ")) { throw new NoAuthorizationException("Not authorised to read datatable: " + + datatable); } + } + + public Long getStaffId() { + Long staffId = null; + if (this.staff != null) { + staffId = this.staff.getId(); + } + return staffId; + } + + public String getStaffDisplayName() { + String staffDisplayName = null; + if (this.staff != null) { + staffDisplayName = this.staff.displayName(); + } + return staffDisplayName; + } } \ No newline at end of file From 6d533b7d5cf7f15b7ceb234caa51fb64c75a93f6 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 9 Sep 2013 09:56:21 +0100 Subject: [PATCH 2/6] update release version details. --- api-docs/apiLive.htm | 2 +- mifosng-provider/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index ac6cd712d..e900d2935 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -90,7 +90,7 @@
- +

Overview

diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 9f46c774a..43e264960 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1 +1 @@ -releaseVersion=1.9.0.RELEASE +releaseVersion=1.9.1.RELEASE From bba521a8c38ed968b6bc94ca4d8b9ab1a180ca55 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 10 Sep 2013 19:43:52 +0530 Subject: [PATCH 3/6] minor issues with loan transfers --- ...ers_in_suspense_account_for_existing_loan_products.sql | 4 ++++ .../service/CashBasedAccountingProcessorForLoan.java | 6 ++++-- .../ProductToGLAccountMappingFromApiJsonDeserializer.java | 5 +++++ .../service/LoanProductToGLAccountMappingHelper.java | 4 ++++ .../mifosplatform/portfolio/loanaccount/domain/Loan.java | 2 +- .../portfolio/loanaccount/domain/LoanTransaction.java | 2 +- .../loanaccount/service/LoanWritePlatformService.java | 6 +++--- .../LoanWritePlatformServiceJpaRepositoryImpl.java | 8 ++++---- .../TransferWritePlatformServiceJpaRepositoryImpl.java | 8 ++++++-- 9 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql diff --git a/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql b/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql new file mode 100644 index 000000000..55e60416b --- /dev/null +++ b/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql @@ -0,0 +1,4 @@ +INSERT INTO `acc_product_mapping` (`gl_account_id`,`product_id`,`product_type`,`payment_type`,`charge_id`,`financial_account_type`) +select mapping.gl_account_id,mapping.product_id,mapping.product_type,mapping.payment_type,mapping.charge_id, 10 +from acc_product_mapping mapping +where mapping.financial_account_type = 1 and mapping.product_type=1; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java index e2f69f4df..05c71e2c4 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java @@ -64,7 +64,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, false); } else if (loanTransactionDTO.getTransactionType().isInitiateTransfer() - || loanTransactionDTO.getTransactionType().isApproveTransfer()) { + || loanTransactionDTO.getTransactionType().isApproveTransfer() + || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { createJournalEntriesForTransfers(loanDTO, loanTransactionDTO, office); } } @@ -184,7 +185,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, null, loanId, transactionId, transactionDate, principalAmount, isReversal); - } else if (loanTransactionDTO.getTransactionType().isApproveTransfer()) { + } else if (loanTransactionDTO.getTransactionType().isApproveTransfer() + || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE, loanProductId, null, loanId, transactionId, transactionDate, principalAmount, isReversal); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java index daf95146e..3772e6937 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java @@ -106,6 +106,11 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer { baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) .notNull().integerGreaterThanZero(); + final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); + baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue()) + .value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero(); + } if (isAccrualBasedAccounting(accountingRuleType)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java index e3b12c28f..74de8f839 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java @@ -112,6 +112,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element); final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element); + final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); final Long receivableInterestAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element); @@ -130,6 +132,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId); + changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); break; case ACCRUAL_BASED: changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId); @@ -141,6 +144,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), receivableFeeAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId); + changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); break; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index f937d5f81..cbbbc73db 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -2090,7 +2090,7 @@ public class Loan extends AbstractPersistable { return !isDisbursed(); } - private boolean isDisbursed() { + public boolean isDisbursed() { return hasDisbursementTransaction(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 29e7148d2..b93e54df9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -138,7 +138,7 @@ public final class LoanTransaction extends AbstractPersistable { } public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate) { - return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(), + return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() .getTotalPenaltyChargesOutstanding(), false, null); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java index f8d78d084..c4c7050c3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -59,12 +59,12 @@ public interface LoanWritePlatformService { void applyHolidaysToLoans(); - LoanTransaction initiateLoanTransfer(Long accountId, LocalDate TransferDate); + LoanTransaction initiateLoanTransfer(Long accountId, LocalDate transferDate); - LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate); + LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate transferDate); void rejectLoanTransfer(Long accountId); - LoanTransaction acceptLoanTransfer(Long accountId, LocalDate TransferDate, Office acceptedInOffice, Staff loanOfficer); + LoanTransaction acceptLoanTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff loanOfficer); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index d6ed5530e..e6229c04c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -899,7 +899,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override - public LoanTransaction initiateLoanTransfer(final Long accountId, final LocalDate TransferDate) { + public LoanTransaction initiateLoanTransfer(final Long accountId, final LocalDate transferDate) { final Loan loan = this.loanAssembler.assembleFrom(accountId); checkClientOrGroupActive(loan); @@ -907,7 +907,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(loan.findExistingTransactionIds()); final List existingReversedTransactionIds = new ArrayList(loan.findExistingReversedTransactionIds()); - final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, TransferDate); + final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate); loan.getLoanTransactions().add(newTransferTransaction); loan.setLoanStatus(LoanStatus.TRANSFER_IN_PROGRESS.getValue()); @@ -946,13 +946,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override - public LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate) { + public LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate transferDate) { final Loan loan = this.loanAssembler.assembleFrom(accountId); final List existingTransactionIds = new ArrayList(loan.findExistingTransactionIds()); final List existingReversedTransactionIds = new ArrayList(loan.findExistingReversedTransactionIds()); - final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, TransferDate); + final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate); loan.getLoanTransactions().add(newTransferAcceptanceTransaction); loan.setLoanStatus(LoanStatus.ACTIVE.getValue()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java index df7e1d12c..dafc87d59 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java @@ -325,7 +325,11 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr if (loanRepository.doNonClosedLoanAccountsExistForClient(client.getId())) { // get each individual loan for the client for (Loan loan : loanRepository.findLoanByClientId(client.getId())) { - if (!loan.isClosed()) { + /** + * We need to create transactions etc only for loans which are + * disbursed and not yet closed + **/ + if (loan.isDisbursed() && !loan.isClosed()) { switch (transferEventType) { case ACCEPTANCE: this.loanWritePlatformService.acceptLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant(), @@ -335,7 +339,7 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr this.loanWritePlatformService.initiateLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant()); break; case REJECTION: - // do nothing + this.loanWritePlatformService.rejectLoanTransfer(loan.getId()); break; case WITHDRAWAL: this.loanWritePlatformService.withdrawLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant()); From 18cc921d62d23e466ca8e5d7e766694fd5824708 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 10 Sep 2013 20:01:47 +0530 Subject: [PATCH 4/6] added validation for account trasfer transaction while updating --- .../AccountTransfersReadPlatformService.java | 3 + ...countTransfersReadPlatformServiceImpl.java | 31 ++++- .../loanaccount/data/LoanTransactionData.java | 12 +- .../loanaccount/domain/LoanTransaction.java | 5 +- .../service/LoanReadPlatformServiceImpl.java | 109 ++++++++++++++++-- ...WritePlatformServiceJpaRepositoryImpl.java | 11 +- ...WritePlatformServiceJpaRepositoryImpl.java | 18 ++- 7 files changed, 168 insertions(+), 21 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java index a039841b1..2967fa1a6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.account.service; import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.group.service.SearchParameters; @@ -17,4 +18,6 @@ public interface AccountTransfersReadPlatformService { Page retrieveAll(SearchParameters searchParameters); AccountTransferData retrieveOne(Long transferId); + + boolean isAccountTransfer(Long transactionId,PortfolioAccountType accountType); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index e5135302d..bc00341fd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -247,6 +247,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); @@ -260,6 +261,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("join m_client fromclient on fromclient.id = sat.from_client_id "); sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id "); sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id "); + sqlBuilder.append("left join m_savings_account fromloanacc on fromloanacc.id = sat.from_loan_account_id "); sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id "); sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = sat.to_loan_account_id "); sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id "); @@ -307,11 +309,20 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final String toClientName = rs.getString("toClientName"); final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); - final EnumOptionData fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); - final PortfolioAccountData fromSavingsAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); + final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); + PortfolioAccountData fromAccount = null; + EnumOptionData fromAccountType = null; + if(fromSavingsAccountId != null){ + fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + }else if(fromLoanAccountId != null){ + fromAccount = PortfolioAccountData.lookup(fromLoanAccountId, fromLoanAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + } PortfolioAccountData toAccount = null; EnumOptionData toAccountType = null; @@ -329,7 +340,21 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, - toOffice, fromClient, toClient, fromAccountType, fromSavingsAccount, toAccountType, toAccount); + toOffice, fromClient, toClient, fromAccountType, fromAccount, toAccountType, toAccount); } } + + @Override + public boolean isAccountTransfer(Long transactionId, PortfolioAccountType accountType) { + StringBuilder sql = new StringBuilder("select count(*) from m_savings_account_transfer at where "); + if (accountType.isLoanAccount()) { + sql.append("at.from_loan_transaction_id=").append(transactionId).append(" or at.to_loan_transaction_id=").append(transactionId); + } else { + sql.append("at.from_savings_transaction_id=").append(transactionId).append(" or at.to_savings_transaction_id=").append(transactionId); + } + + int count = this.jdbcTemplate.queryForInt(sql.toString()); + return count > 0; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java index 4b34efa97..8837c9eab 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java @@ -11,6 +11,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; /** @@ -35,6 +36,7 @@ public class LoanTransactionData { private final BigDecimal feeChargesPortion; private final BigDecimal penaltyChargesPortion; private final String externalId; + private final AccountTransferData transfer; // templates final Collection paymentTypeOptions; @@ -44,21 +46,22 @@ public class LoanTransactionData { loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, paymentTypeOptions, - loanTransactionData.externalId); + loanTransactionData.externalId,loanTransactionData.transfer); } public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final String externalId) { + final BigDecimal penaltyChargesPortion, final String externalId, AccountTransferData transfer) { this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, - feeChargesPortion, penaltyChargesPortion, null, externalId); + feeChargesPortion, penaltyChargesPortion, null, externalId,transfer); } public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final Collection paymentTypeOptions, final String externalId) { + final BigDecimal penaltyChargesPortion, final Collection paymentTypeOptions, final String externalId, + final AccountTransferData transfer) { this.id = id; this.officeId = officeId; this.officeName = officeName; @@ -73,6 +76,7 @@ public class LoanTransactionData { this.penaltyChargesPortion = penaltyChargesPortion; this.paymentTypeOptions = paymentTypeOptions; this.externalId = externalId; + this.transfer = transfer; } public LocalDate dateOf() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 29e7148d2..e034c0cf6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -32,6 +32,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; @@ -395,7 +396,7 @@ public final class LoanTransaction extends AbstractPersistable { return result; } - public LoanTransactionData toData(final CurrencyData currencyData) { + public LoanTransactionData toData(final CurrencyData currencyData,final AccountTransferData transfer) { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf); PaymentDetailData paymentDetailData = null; if (this.paymentDetail != null) { @@ -403,7 +404,7 @@ public final class LoanTransaction extends AbstractPersistable { } return new LoanTransactionData(this.getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion, - this.penaltyChargesPortion, this.externalId); + this.penaltyChargesPortion, this.externalId, transfer); } public Map toMapData(final CurrencyData currencyData) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 11dadbc75..2a93650f2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -31,6 +31,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations; import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; @@ -329,7 +330,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, - possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null); + possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null,null); } @Override @@ -353,7 +354,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount(); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, - null, null, null, null, null); + null, null, null, null, null, null); } @Override @@ -363,7 +364,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); return new LoanTransactionData(null, null, null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, - null, null, null); + null, null, null, null); } @Override @@ -374,7 +375,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, - null, null, null, paymentOptions, null); + null, null, null, paymentOptions, null,null); } @Override @@ -393,8 +394,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { if (transaction == null) { throw new LoanTransactionNotFoundException(transactionId); } if (transaction.isNotBelongingToLoanOf(loan)) { throw new LoanTransactionNotFoundException(transactionId, loanId); } - - return transaction.toData(currencyData); + + LoanTransactionsAccountTransferMapper trasfermapper = new LoanTransactionsAccountTransferMapper(); + String sql = "select " + + trasfermapper.accountTransferSchema() + + " where tr.loan_id = ? and tr.id = ?"; + AccountTransferData accountTransferData = this.jdbcTemplate.queryForObject(sql, trasfermapper, loanId, transactionId); + return transaction.toData(currencyData,accountTransferData); } private static final class LoanMapper implements RowMapper { @@ -861,12 +867,20 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " - + " cv.code_value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName " + + " cv.code_value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName, " + + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed," + + " fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount," + + " fromtran.description as fromTransferDescription," + + " totran.id as toTransferId, totran.is_reversed as toTransferReversed," + + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount," + + " totran.description as toTransferDescription " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + " join m_currency rc on rc.`code` = l.currency_code " + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id" + " left join m_code_value cv on pd.payment_type_cv_id = cv.id" - + " left join m_office office on office.id=tr.office_id"; + + " left join m_office office on office.id=tr.office_id" + + " left join m_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; } @Override @@ -910,12 +924,89 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); final String externalId = rs.getString("externalId"); + + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currencyData, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currencyData, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount, - principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, externalId); + principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, externalId, transfer); } } + private static final class LoanTransactionsAccountTransferMapper implements RowMapper { + + public String accountTransferSchema() { + + return " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed," + + " fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount," + + " fromtran.description as fromTransferDescription," + + " totran.id as toTransferId, totran.is_reversed as toTransferReversed," + + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount," + + " totran.description as toTransferDescription " + + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + + " join m_currency rc on rc.`code` = l.currency_code " + + " left join m_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; + } + + @Override + public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currencyData, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currencyData, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return transfer; + } + } + + @Override public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index d6ed5530e..7ad81b30c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomain import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -35,6 +36,7 @@ import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.calendar.domain.Calendar; @@ -115,6 +117,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final WorkingDaysRepositoryWrapper workingDaysRepository; private final LoanProductReadPlatformService loanProductReadPlatformService; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -129,7 +132,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanProductReadPlatformService loanProductReadPlatformService, - final AccountTransfersWritePlatformService accountTransfersWritePlatformService) { + final AccountTransfersWritePlatformService accountTransfersWritePlatformService, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -150,6 +154,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.workingDaysRepository = workingDaysRepository; this.loanProductReadPlatformService = loanProductReadPlatformService; this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -428,6 +433,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId); if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)){ + throw new PlatformServiceUnavailableException("error.msg.loan.transfer.transaction.update.not.allowed","Loan transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 4c3d9e59f..011382ac0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; @@ -34,6 +35,8 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.group.domain.Group; @@ -72,6 +75,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final SavingsAccountDomainService savingsAccountDomainService; private final NoteRepository noteRepository; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -82,7 +86,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, final JournalEntryWritePlatformService journalEntryWritePlatformService, - final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository) { + final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; @@ -93,6 +98,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.savingsAccountDomainService = savingsAccountDomainService; this.noteRepository = noteRepository; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; } @Transactional @@ -299,6 +305,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( transactionId, savingsId); if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){ + throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed","Savings account transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); @@ -337,6 +347,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( savingsId, transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){ + throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed","Savings account transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } this.savingsAccountTransactionDataValidator.validate(command); final LocalDate today = DateUtils.getLocalDateOfTenant(); @@ -361,7 +375,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi //for undo withdrawal fee SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( transactionId+1, savingsId); - if(nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()){ + if(nextSavingsAccountTransaction!=null && nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()){ account.undoTransaction(transactionId+1, existingReversedTransactionIds); } From d5e50a8913576e889802619fa8df572da33d15d4 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 10 Sep 2013 20:21:27 +0530 Subject: [PATCH 5/6] Update CHANGELOG.md for 1.9.1 Release --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af2d098e..b5b11c713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for Releases =============== +1. 10 Sep 2013 - 1.9.1.RELEASE 1. 8 Sep 2013 - 1.9.0.RELEASE 1. 21 Aug 2013 - 1.8.1.RELEASE 1. 20 Aug 2013 - 1.8.0.RELEASE @@ -34,6 +35,21 @@ Releases 1. 05 April 2013 - 0.11.1.beta 1. 05 April 2013 - 0.11.0.beta +1.8.1.RELEASE +============= +Bug Release + +Platform & API + +Bug + - [MIFOSX-647] - Transfered amount in Loan account transaction page should not be allowed to Edit/Delete + - [MIFOSX-648] - User with no read permissions should still be able to fetch their own user account data + - [MIFOSX-658] - Unable to update existing Loan Products in demo server + +Reference App + - [MIFOSX-655] - Client Closure reason added in code values is not displayed in dropdon list of Client Close page + + 1.9.0.RELEASE ============= Feature Release From 5af987ffb81ff663df8063eb6fb196802914b4f7 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 10 Sep 2013 20:21:55 +0530 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5b11c713..250a4d5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ Releases 1. 05 April 2013 - 0.11.1.beta 1. 05 April 2013 - 0.11.0.beta -1.8.1.RELEASE +1.9.1.RELEASE ============= Bug Release