From 1a29bbe97ef5a80ad5aeb2dfd493d52da27ea698 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 12 Jul 2012 11:58:05 +0100 Subject: [PATCH] change office transactions capability to support intra office transactions and external transactions (money in or out of office from external entity). --- .../patch-0006-office-transactions.sql | 22 +++++ .../platform/api/OfficeApiResource.java | 42 ---------- .../api/OfficeTransactionsApiResource.java | 82 +++++++++++++++++++ .../commands/BranchMoneyTransferCommand.java | 10 +++ ...ryTransfer.java => OfficeTransaction.java} | 15 ++-- ....java => OfficeTransactionRepository.java} | 6 +- .../BranchMoneyTransferCommandValidator.java | 27 +++++- .../service/OfficeWritePlatformService.java | 5 +- ...WritePlatformServiceJpaRepositoryImpl.java | 66 ++++++++++----- 9 files changed, 202 insertions(+), 73 deletions(-) create mode 100644 mifosng-db/patches-beta-3-heavens-family/patch-0006-office-transactions.sql create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeTransactionsApiResource.java rename mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/{OfficeMonetaryTransfer.java => OfficeTransaction.java} (72%) rename mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/{OfficeMonetaryTransferRepository.java => OfficeTransactionRepository.java} (57%) diff --git a/mifosng-db/patches-beta-3-heavens-family/patch-0006-office-transactions.sql b/mifosng-db/patches-beta-3-heavens-family/patch-0006-office-transactions.sql new file mode 100644 index 000000000..12bacb3d6 --- /dev/null +++ b/mifosng-db/patches-beta-3-heavens-family/patch-0006-office-transactions.sql @@ -0,0 +1,22 @@ +-- create office transaction table +DROP TABLE IF EXISTS `mifosngprovider`.`portfolio_office_monetary_transfers`; +DROP TABLE IF EXISTS `mifosngprovider`.`portfolio_office_transaction`; +CREATE TABLE `portfolio_office_transaction` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `from_office_id` bigint(20) DEFAULT NULL, + `to_office_id` bigint(20) DEFAULT NULL, + `currency_code` varchar(3) NOT NULL, + `currency_digits` int(11) NOT NULL, + `transaction_amount` decimal(19,6) NOT NULL, + `transaction_date` date NOT NULL, + `description` varchar(100) DEFAULT NULL, + `created_date` datetime DEFAULT NULL, + `createdby_id` bigint(20) DEFAULT NULL, + `lastmodified_date` datetime DEFAULT NULL, + `lastmodifiedby_id` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FK1E37728B93C6C1B6` (`to_office_id`), + KEY `FK1E37728B783C5C25` (`from_office_id`), + CONSTRAINT `FK1E37728B783C5C25` FOREIGN KEY (`from_office_id`) REFERENCES `org_office` (`id`), + CONSTRAINT `FK1E37728B93C6C1B6` FOREIGN KEY (`to_office_id`) REFERENCES `org_office` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java index 7372c1e98..40a15d9c1 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java @@ -1,8 +1,6 @@ package org.mifosng.platform.api; -import java.math.BigDecimal; import java.util.Collection; -import java.util.Locale; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -17,15 +15,12 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; -import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; import org.mifosng.platform.api.commands.OfficeCommand; import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.OfficeData; import org.mifosng.platform.api.infrastructure.ApiDataConversionService; import org.mifosng.platform.api.infrastructure.ApiJSONFormattingService; -import org.mifosng.platform.exceptions.UnrecognizedQueryParamException; import org.mifosng.platform.organisation.service.OfficeReadPlatformService; import org.mifosng.platform.organisation.service.OfficeWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; @@ -130,41 +125,4 @@ public class OfficeApiResource { return Response.ok().entity(new EntityIdentifier(entityId)).build(); } - - @POST - @Path("{officeId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response transferMoneyFrom(@PathParam("officeId") final Long officeId, - @QueryParam("command") final String commandParam, - final BranchMoneyTransferCommand command) { - - command.setFromOfficeId(officeId); - - LocalDate transactionLocalDate = apiDataConversionService.convertFrom(command.getTransactionDate(), "transactionDate", command.getDateFormat()); - command.setTransactionLocalDate(transactionLocalDate); - - Locale clientLocale = this.apiDataConversionService.localeFromString(command.getLocale()); - - BigDecimal transactionAmountValue = apiDataConversionService.convertFrom(command.getTransactionAmount(), "transactionAmount", clientLocale); - command.setTransactionAmountValue(transactionAmountValue); - - Response response = null; - - if (is(commandParam, "transfer")) { - Long id = this.writePlatformService.transferMoney(command); - response = Response.ok().entity(new EntityIdentifier(id)).build(); - } - - if (response == null) { - throw new UnrecognizedQueryParamException("command", commandParam); - } - - return response; - } - - private boolean is(final String commandParam, final String commandValue) { - return StringUtils.isNotBlank(commandParam) - && commandParam.trim().equalsIgnoreCase(commandValue); - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeTransactionsApiResource.java new file mode 100644 index 000000000..15382c0dc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeTransactionsApiResource.java @@ -0,0 +1,82 @@ +package org.mifosng.platform.api; + +import java.math.BigDecimal; +import java.util.Locale; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; +import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.infrastructure.ApiDataConversionService; +import org.mifosng.platform.api.infrastructure.ApiJSONFormattingService; +import org.mifosng.platform.exceptions.UnrecognizedQueryParamException; +import org.mifosng.platform.organisation.service.OfficeReadPlatformService; +import org.mifosng.platform.organisation.service.OfficeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/officetransactions") +@Component +@Scope("singleton") +public class OfficeTransactionsApiResource { + + private String defaultFieldList = "openingDate"; + private String allowedFieldList = "allowedParents"; + private String filterName = "myFilter"; + + @Autowired + private OfficeReadPlatformService readPlatformService; + + @Autowired + private OfficeWritePlatformService writePlatformService; + + @Autowired + private ApiDataConversionService apiDataConversionService; + + @Autowired + private ApiJSONFormattingService jsonFormattingService; + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response transferMoneyFrom(@QueryParam("command") final String commandParam, final BranchMoneyTransferCommand command) { + + LocalDate transactionLocalDate = apiDataConversionService.convertFrom(command.getTransactionDate(), "transactionDate", command.getDateFormat()); + command.setTransactionLocalDate(transactionLocalDate); + + Locale clientLocale = this.apiDataConversionService.localeFromString(command.getLocale()); + + BigDecimal transactionAmountValue = apiDataConversionService.convertFrom(command.getTransactionAmount(), "transactionAmount", clientLocale); + command.setTransactionAmountValue(transactionAmountValue); + + Response response = null; + + if (is(commandParam, "intra-transfer")) { + Long id = this.writePlatformService.interBranchMoneyTransfer(command); + response = Response.ok().entity(new EntityIdentifier(id)).build(); + } else if (is(commandParam, "external-transfer")) { + Long id = this.writePlatformService.externalBranchMoneyTransfer(command); + response = Response.ok().entity(new EntityIdentifier(id)).build(); + } + + if (response == null) { + throw new UnrecognizedQueryParamException("command", commandParam); + } + + return response; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) + && commandParam.trim().equalsIgnoreCase(commandValue); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/BranchMoneyTransferCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/BranchMoneyTransferCommand.java index e09161615..9087f6621 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/BranchMoneyTransferCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/BranchMoneyTransferCommand.java @@ -25,6 +25,8 @@ public class BranchMoneyTransferCommand { private String transactionAmount; private BigDecimal transactionAmountValue; + private String description; + protected BranchMoneyTransferCommand() { // } @@ -108,4 +110,12 @@ public class BranchMoneyTransferCommand { public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeMonetaryTransfer.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeTransaction.java similarity index 72% rename from mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeMonetaryTransfer.java rename to mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeTransaction.java index 61ddc3d82..7907bbc13 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeMonetaryTransfer.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/OfficeTransaction.java @@ -20,8 +20,8 @@ import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.user.domain.AppUser; @Entity -@Table(name = "portfolio_office_monetary_transfers") -public class OfficeMonetaryTransfer extends AbstractAuditableCustom { +@Table(name = "portfolio_office_transaction") +public class OfficeTransaction extends AbstractAuditableCustom { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "from_office_id") @@ -40,22 +40,25 @@ public class OfficeMonetaryTransfer extends AbstractAuditableCustom, - JpaSpecificationExecutor { +public interface OfficeTransactionRepository extends + JpaRepository, + JpaSpecificationExecutor { // no added behaviour } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/BranchMoneyTransferCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/BranchMoneyTransferCommandValidator.java index 0c390c7a0..e8878608b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/BranchMoneyTransferCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/BranchMoneyTransferCommandValidator.java @@ -16,7 +16,7 @@ public class BranchMoneyTransferCommandValidator { this.command = command; } - public void validate() { + public void validateInterBranchTransfer() { List dataValidationErrors = new ArrayList(); @@ -27,6 +27,31 @@ public class BranchMoneyTransferCommandValidator { baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionLocalDate()).notNull(); baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank(); baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmountValue()).notNull(); + baseDataValidator.reset().parameter("description").value(command.getDescription()).notExceedingLengthOf(100); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } + } + + public void validateExternalBranchTransfer() { + + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("office.money.transfer"); + + if (command.getFromOfficeId() == null) { + baseDataValidator.reset().parameter("toOfficeId").value(command.getToOfficeId()).notNull(); + } + + if (command.getToOfficeId() == null) { + baseDataValidator.reset().parameter("fromOfficeId").value(command.getFromOfficeId()).notNull(); + } + + baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionLocalDate()).notNull(); + baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank(); + baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmountValue()).notNull(); + baseDataValidator.reset().parameter("description").value(command.getDescription()).notExceedingLengthOf(100); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java index 46d9ba07e..c57c00859 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java @@ -13,5 +13,8 @@ public interface OfficeWritePlatformService { Long updateOffice(final OfficeCommand command); @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") - Long transferMoney(BranchMoneyTransferCommand command); + Long interBranchMoneyTransfer(BranchMoneyTransferCommand command); + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long externalBranchMoneyTransfer(BranchMoneyTransferCommand command); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformServiceJpaRepositoryImpl.java index 471b73950..db9da1b25 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformServiceJpaRepositoryImpl.java @@ -13,8 +13,8 @@ import org.mifosng.platform.exceptions.NoAuthorizationException; import org.mifosng.platform.exceptions.OfficeNotFoundException; import org.mifosng.platform.exceptions.PlatformDataIntegrityException; import org.mifosng.platform.organisation.domain.Office; -import org.mifosng.platform.organisation.domain.OfficeMonetaryTransfer; -import org.mifosng.platform.organisation.domain.OfficeMonetaryTransferRepository; +import org.mifosng.platform.organisation.domain.OfficeTransaction; +import org.mifosng.platform.organisation.domain.OfficeTransactionRepository; import org.mifosng.platform.organisation.domain.OfficeRepository; import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.user.domain.AppUser; @@ -32,13 +32,13 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP private final PlatformSecurityContext context; private final OfficeRepository officeRepository; - private final OfficeMonetaryTransferRepository officeMonetaryTransferRepository; + private final OfficeTransactionRepository officeMonetaryTransferRepository; private final ApplicationCurrencyRepository applicationCurrencyRepository; @Autowired public OfficeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final OfficeRepository officeRepository, - final OfficeMonetaryTransferRepository officeMonetaryTransferRepository, + final OfficeTransactionRepository officeMonetaryTransferRepository, final ApplicationCurrencyRepository applicationCurrencyRepository) { this.context = context; this.officeRepository = officeRepository; @@ -104,26 +104,21 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP @Transactional @Override - public Long transferMoney(final BranchMoneyTransferCommand command) { + public Long interBranchMoneyTransfer(final BranchMoneyTransferCommand command) { AppUser currentUser = context.authenticatedUser(); BranchMoneyTransferCommandValidator validator = new BranchMoneyTransferCommandValidator(command); - validator.validate(); + validator.validateInterBranchTransfer(); - Office fromOffice = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getFromOfficeId()); - Office toOffice = null; - if (fromOffice.isHeadOffice()) { - toOffice = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getToOfficeId()); - } else { - toOffice = this.officeRepository.findOne(command.getToOfficeId()); - if (toOffice == null) { - throw new OfficeNotFoundException(command.getToOfficeId()); - } - - if (!fromOffice.hasParentOf(toOffice)) { - throw new NoAuthorizationException("User does not have sufficient priviledges to act on the provided office."); - } + Office fromOffice = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), command.getFromOfficeId())); + if (fromOffice == null) { + throw new OfficeNotFoundException(command.getFromOfficeId()); + } + + Office toOffice = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), command.getToOfficeId())); + if (toOffice == null) { + throw new OfficeNotFoundException(command.getToOfficeId()); } final String currencyCode = command.getCurrencyCode(); @@ -135,7 +130,38 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces()); Money amount = Money.of(currency, command.getTransactionAmountValue()); - OfficeMonetaryTransfer entity = OfficeMonetaryTransfer.create(fromOffice, toOffice, command.getTransactionLocalDate(), amount); + OfficeTransaction entity = OfficeTransaction.create(fromOffice, toOffice, command.getTransactionLocalDate(), amount); + + this.officeMonetaryTransferRepository.save(entity); + + return entity.getId(); + } + + @Transactional + @Override + public Long externalBranchMoneyTransfer(BranchMoneyTransferCommand command) { + + AppUser currentUser = context.authenticatedUser(); + + BranchMoneyTransferCommandValidator validator = new BranchMoneyTransferCommandValidator(command); + validator.validateExternalBranchTransfer(); + + Office fromOffice = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), command.getFromOfficeId())); + Office toOffice = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), command.getToOfficeId())); + if (fromOffice == null && toOffice == null) { + throw new OfficeNotFoundException(command.getToOfficeId()); + } + + final String currencyCode = command.getCurrencyCode(); + ApplicationCurrency appCurrency = this.applicationCurrencyRepository.findOneByCode(currencyCode); + if (appCurrency == null) { + throw new CurrencyNotFoundException(currencyCode); + } + + MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces()); + Money amount = Money.of(currency, command.getTransactionAmountValue()); + + OfficeTransaction entity = OfficeTransaction.create(fromOffice, toOffice, command.getTransactionLocalDate(), amount); this.officeMonetaryTransferRepository.save(entity);