From 9ad9070a863386face617338e8bbf11b910ec04b Mon Sep 17 00:00:00 2001 From: Michal Dudzinski Date: Fri, 17 Aug 2012 12:57:53 +0200 Subject: [PATCH] MIFOSX-33 Add Infrastructure for fees, charges and penalties. --- .../platform/api/ChargesApiResource.java | 100 +++++++++++++++++ .../platform/api/commands/ChargeCommand.java | 44 ++++++++ .../mifosng/platform/api/data/ChargeData.java | 40 +++++++ .../ApiDataConversionService.java | 6 + .../ApiDataConversionServiceImpl.java | 63 ++++++++++- .../platform/charge/domain/Charge.java | 73 ++++++++++++ .../charge/domain/ChargeRepository.java | 8 ++ .../service/ChargeCommandValidator.java | 45 ++++++++ .../service/ChargeReadPlatformService.java | 12 ++ .../ChargeReadPlatformServiceImpl.java | 73 ++++++++++++ .../service/ChargeWritePlatformService.java | 16 +++ ...WritePlatformServiceJpaRepositoryImpl.java | 106 ++++++++++++++++++ .../exceptions/ChargeNotFoundException.java | 8 ++ 13 files changed, 592 insertions(+), 2 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/ChargesApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ChargeCommand.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/ChargeData.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/Charge.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/ChargeRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeCommandValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ChargeNotFoundException.java diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/ChargesApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/ChargesApiResource.java new file mode 100644 index 000000000..6b05fd4d8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/ChargesApiResource.java @@ -0,0 +1,100 @@ +package org.mifosng.platform.api; + +import org.mifosng.platform.api.commands.ChargeCommand; +import org.mifosng.platform.api.data.ChargeData; +import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.infrastructure.ApiDataConversionService; +import org.mifosng.platform.api.infrastructure.ApiParameterHelper; +import org.mifosng.platform.charge.service.ChargeReadPlatformService; +import org.mifosng.platform.charge.service.ChargeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.core.Response; +import java.util.Collection; +import java.util.Set; + +@Path("/charges") +@Component +@Scope("singleton") +public class ChargesApiResource { + + @Autowired + private ChargeReadPlatformService chargeReadPlatformService; + + @Autowired + private ChargeWritePlatformService chargeWritePlatformService; + + @Autowired + private ApiDataConversionService apiDataConversionService; + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveAllCharges(@Context final UriInfo uriInfo){ + + Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + Collection charges = this.chargeReadPlatformService.retrieveAllCharges(); + + return this.apiDataConversionService.convertChargeDataToJson(prettyPrint, + responseParameters, charges.toArray(new ChargeData[charges.size()])); + } + + @GET + @Path("{chargeId}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveCharge(@PathParam("chargeId") final Long chargeId, @Context final UriInfo uriInfo){ + + Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + ChargeData charge = this.chargeReadPlatformService.retrieveCharge(chargeId); + + return this.apiDataConversionService.convertChargeDataToJson(prettyPrint, + responseParameters, charge); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response createCharge(final String jsonRequestBody){ + + ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(null, jsonRequestBody); + + Long chargeId = this.chargeWritePlatformService.createCharge(command); + + return Response.ok().entity(new EntityIdentifier(chargeId)).build(); + } + + @PUT + @Path("{chargeId}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateCharge(@PathParam("chargeId") final Long chargeId, final String jsonRequestBody){ + + ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(chargeId, jsonRequestBody); + + Long entityId = this.chargeWritePlatformService.updateCharge(command); + + return Response.ok().entity(new EntityIdentifier(entityId)).build(); + } + + @DELETE + @Path("{chargeId}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response deleteCharge(@PathParam("chargeId") final Long chargeId){ + + Long entityId = this.chargeWritePlatformService.deleteCharge(chargeId); + + return Response.ok().entity(new EntityIdentifier(entityId)).build(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ChargeCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ChargeCommand.java new file mode 100644 index 000000000..ba97d8305 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ChargeCommand.java @@ -0,0 +1,44 @@ +package org.mifosng.platform.api.commands; + +import java.math.BigDecimal; +import java.util.Set; + +public class ChargeCommand { + + private final Long id; + private final String name; + private final BigDecimal amount; + + private final Set modifiedParameters; + + public ChargeCommand(Set modifiedParameters, Long id, String name, BigDecimal amount) { + this.id = id; + this.name = name; + this.amount = amount; + this.modifiedParameters = modifiedParameters; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public BigDecimal getAmount() { + return amount; + } + + public Set getModifiedParameters() { + return modifiedParameters; + } + + public boolean isNameChanged(){ + return this.modifiedParameters.contains("name"); + } + + public boolean isAmountChanged(){ + return this.modifiedParameters.contains("amount"); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ChargeData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ChargeData.java new file mode 100644 index 000000000..096609744 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ChargeData.java @@ -0,0 +1,40 @@ +package org.mifosng.platform.api.data; + +import java.math.BigDecimal; + +public class ChargeData { + + private Long id; + private String name; + private BigDecimal amount; + + public ChargeData(Long id, String name, BigDecimal amount) { + this.id = id; + this.name = name; + this.amount = amount; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java index a68c997bc..3b85e6a8b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java @@ -4,6 +4,7 @@ import java.util.Set; import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand; import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; +import org.mifosng.platform.api.commands.ChargeCommand; import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.DepositProductCommand; import org.mifosng.platform.api.commands.DepositAccountCommand; @@ -22,6 +23,7 @@ import org.mifosng.platform.api.commands.UserCommand; import org.mifosng.platform.api.data.AdditionalFieldsSetData; import org.mifosng.platform.api.data.AppUserData; import org.mifosng.platform.api.data.AuthenticatedUserData; +import org.mifosng.platform.api.data.ChargeData; import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.ClientLoanAccountSummaryCollectionData; import org.mifosng.platform.api.data.ConfigurationData; @@ -84,6 +86,10 @@ public interface ApiDataConversionService { String convertAuthenticatedUserDataToJson(boolean prettyPrint, AuthenticatedUserData authenticatedUserData); // + ChargeCommand convertJsonToChargeCommand(Long resourceIdentifier, String json); + + String convertChargeDataToJson(boolean prettyPrint, Set responseParameters, ChargeData... charges); + FundCommand convertJsonToFundCommand(Long resourceIdentifier, String json); String convertFundDataToJson(boolean prettyPrint, Set responseParameters, FundData... funds); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 5b2cd7ee7..7ffbe24ba 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -20,6 +20,7 @@ import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand; import org.mifosng.platform.api.commands.BranchMoneyTransferCommand; +import org.mifosng.platform.api.commands.ChargeCommand; import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.DepositProductCommand; import org.mifosng.platform.api.commands.DepositAccountCommand; @@ -39,6 +40,7 @@ import org.mifosng.platform.api.data.AdditionalFieldsSetData; import org.mifosng.platform.api.data.ApiParameterError; import org.mifosng.platform.api.data.AppUserData; import org.mifosng.platform.api.data.AuthenticatedUserData; +import org.mifosng.platform.api.data.ChargeData; import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.ClientLoanAccountSummaryCollectionData; import org.mifosng.platform.api.data.ConfigurationData; @@ -653,8 +655,65 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { return json; } - - @Override + + @Override + public ChargeCommand convertJsonToChargeCommand(Long resourceIdentifier, String json) { + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + + Set supportedParams = new HashSet( + Arrays.asList("name", "amount", "locale") + ); + + checkForUnsupportedParameters(requestMap, supportedParams); + + Set modifiedParameters = new HashSet(); + + String name = extractStringParameter("name", requestMap, modifiedParameters); + BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters); + + return new ChargeCommand(modifiedParameters, resourceIdentifier, name, amount); + } + + @Override + public String convertChargeDataToJson(boolean prettyPrint, Set responseParameters, ChargeData... charges) { + Set supportedParameters = new HashSet(Arrays.asList("id", "name", "amount")); + + final Set parameterNamesToSkip = new HashSet(); + + if (!responseParameters.isEmpty()) { + if (!supportedParameters.containsAll(responseParameters)) { + throw new UnsupportedParameterException(new ArrayList(responseParameters)); + } + + parameterNamesToSkip.addAll(supportedParameters); + parameterNamesToSkip.removeAll(responseParameters); + } + + ExclusionStrategy strategy = new ParameterListExclusionStrategy(parameterNamesToSkip); + + GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy); + if (prettyPrint) { + builder.setPrettyPrinting(); + } + Gson gsonDeserializer = builder.create(); + + String json = ""; + if (charges != null && charges.length == 1) { + json = gsonDeserializer.toJson(charges[0]); + } else { + json = gsonDeserializer.toJson(charges); + } + + return json; + } + + @Override public FundCommand convertJsonToFundCommand(final Long resourceIdentifier, final String json) { if (StringUtils.isBlank(json)) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/Charge.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/Charge.java new file mode 100644 index 000000000..664efa980 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/Charge.java @@ -0,0 +1,73 @@ +package org.mifosng.platform.charge.domain; + +import org.mifosng.platform.api.commands.ChargeCommand; +import org.mifosng.platform.infrastructure.AbstractAuditableCustom; +import org.mifosng.platform.user.domain.AppUser; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.math.BigDecimal; + +@Entity +@Table(name = "o_charge", uniqueConstraints = { + @UniqueConstraint(columnNames = {"name"}, name = "name") +}) +public class Charge extends AbstractAuditableCustom { + + @Column(name = "name", length = 100) + private String name; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + @Column(name = "is_deleted", nullable=false) + private boolean deleted = false; + + public static Charge createNew(String name, BigDecimal amount){ + return new Charge(name, amount); + } + + public Charge() { + this.name = null; + this.amount = null; + } + + public Charge(String name, BigDecimal amount) { + this.name = name; + this.amount = amount; + } + + public String getName() { + return name; + } + + public BigDecimal getAmount() { + return amount; + } + + public boolean isDeleted() { + return deleted; + } + + public void update(final ChargeCommand command){ + if (command.isNameChanged()) { + this.name = command.getName(); + } + + if (command.isAmountChanged()) { + this.amount = command.getAmount(); + } + } + + /** + * Delete is a soft delete. Updates flag on charge 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.name = this.getId() + "_" + this.name; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/ChargeRepository.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/ChargeRepository.java new file mode 100644 index 000000000..0162c4709 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/domain/ChargeRepository.java @@ -0,0 +1,8 @@ +package org.mifosng.platform.charge.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ChargeRepository extends JpaRepository, JpaSpecificationExecutor { + // no added behaviour +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeCommandValidator.java new file mode 100644 index 000000000..a57cd10a7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeCommandValidator.java @@ -0,0 +1,45 @@ +package org.mifosng.platform.charge.service; + +import org.mifosng.platform.DataValidatorBuilder; +import org.mifosng.platform.api.commands.ChargeCommand; +import org.mifosng.platform.api.data.ApiParameterError; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; + +import java.util.ArrayList; +import java.util.List; + +public class ChargeCommandValidator { + + private final ChargeCommand command; + + public ChargeCommandValidator(ChargeCommand command) { + this.command = command; + } + + public void validateForCreate(){ + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge"); + + baseDataValidator.reset().parameter("name").value(command.getName()).notBlank().notExceedingLengthOf(100); + baseDataValidator.reset().parameter("amount").value(command.getAmount()).notNull().positiveAmount(); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } + } + + public void validateForUpdate(){ + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge"); + + baseDataValidator.reset().parameter("id").value(command.getId()).notNull(); + baseDataValidator.reset().parameter("name").value(command.getName()).notBlank().notExceedingLengthOf(100); + baseDataValidator.reset().parameter("amount").value(command.getAmount()).notNull().positiveAmount(); + + 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/charge/service/ChargeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformService.java new file mode 100644 index 000000000..e8ae74ed6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformService.java @@ -0,0 +1,12 @@ +package org.mifosng.platform.charge.service; + +import org.mifosng.platform.api.data.ChargeData; + +import java.util.Collection; + +public interface ChargeReadPlatformService { + + Collection retrieveAllCharges(); + + ChargeData retrieveCharge(Long chargeId); +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java new file mode 100644 index 000000000..5bccde81e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java @@ -0,0 +1,73 @@ +package org.mifosng.platform.charge.service; + +import org.mifosng.platform.api.data.ChargeData; +import org.mifosng.platform.exceptions.ChargeNotFoundException; +import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +@Service +public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + + @Autowired + public ChargeReadPlatformServiceImpl(PlatformSecurityContext context, + final TenantAwareRoutingDataSource dataSource) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Collection retrieveAllCharges() { + this.context.authenticatedUser(); + + ChargeMapper rm = new ChargeMapper(); + + String sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 order by c.name "; + + return this.jdbcTemplate.query(sql, rm, new Object[] {}); + } + + @Override + public ChargeData retrieveCharge(Long chargeId) { + try { + this.context.authenticatedUser(); + + ChargeMapper rm = new ChargeMapper(); + + String sql = "select " + rm.chargeSchema() + " where c.id = ? and c.is_deleted=0 "; + + ChargeData chargeData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] {chargeId}); + return chargeData; + } catch (EmptyResultDataAccessException e){ + throw new ChargeNotFoundException(chargeId); + } + } + + private static final class ChargeMapper implements RowMapper { + + public String chargeSchema(){ + return "c.id as id, c.name as name, c.amount as amount from o_charge c "; + } + + @Override + public ChargeData mapRow(ResultSet rs, int rowNum) throws SQLException { + Long id = rs.getLong("id"); + String name = rs.getString("name"); + BigDecimal amount = rs.getBigDecimal("amount"); + + return new ChargeData(id, name, amount); + } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformService.java new file mode 100644 index 000000000..a6e3157ea --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformService.java @@ -0,0 +1,16 @@ +package org.mifosng.platform.charge.service; + +import org.mifosng.platform.api.commands.ChargeCommand; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface ChargeWritePlatformService { + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long createCharge(final ChargeCommand command); + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long updateCharge(final ChargeCommand command); + + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + Long deleteCharge(final Long chargeId); +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..6b089e137 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,106 @@ +package org.mifosng.platform.charge.service; + +import org.mifosng.platform.api.commands.ChargeCommand; +import org.mifosng.platform.charge.domain.Charge; +import org.mifosng.platform.charge.domain.ChargeRepository; +import org.mifosng.platform.exceptions.ChargeNotFoundException; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWritePlatformService { + private final static Logger logger = LoggerFactory.getLogger(ChargeWritePlatformServiceJpaRepositoryImpl.class); + + private final PlatformSecurityContext context; + private final ChargeRepository chargeRepository; + + @Autowired + public ChargeWritePlatformServiceJpaRepositoryImpl(PlatformSecurityContext context, ChargeRepository chargeRepository) { + this.context = context; + this.chargeRepository = chargeRepository; + } + + @Transactional + @Override + public Long createCharge(final ChargeCommand command) { + try { + this.context.authenticatedUser(); + + ChargeCommandValidator validator = new ChargeCommandValidator(command); + validator.validateForCreate(); + + Charge charge = Charge.createNew(command.getName(), command.getAmount()); + + this.chargeRepository.saveAndFlush(charge); + + return charge.getId(); + } catch (DataIntegrityViolationException dve) { + handleFundDataIntegrityIssues(command, dve); + return Long.valueOf(-1); + } + } + + @Transactional + @Override + public Long updateCharge(final ChargeCommand command) { + + try { + + this.context.authenticatedUser(); + ChargeCommandValidator validator = new ChargeCommandValidator(command); + validator.validateForUpdate(); + + final Long chargeId = command.getId(); + Charge chargeForUpdate = this.chargeRepository.findOne(chargeId); + + if (chargeForUpdate == null){ + throw new ChargeNotFoundException(chargeId); + } + chargeForUpdate.update(command); + + this.chargeRepository.saveAndFlush(chargeForUpdate); + + return chargeForUpdate.getId(); + } catch (DataIntegrityViolationException dve) { + handleFundDataIntegrityIssues(command, dve); + return Long.valueOf(-1); + } + } + + @Transactional + @Override + public Long deleteCharge(Long chargeId) { + + this.context.authenticatedUser(); + + Charge chargeForDelete = this.chargeRepository.findOne(chargeId); + if (chargeForDelete == null || chargeForDelete.isDeleted()){ + throw new ChargeNotFoundException(chargeId); + } + chargeForDelete.delete(); + + chargeRepository.saveAndFlush(chargeForDelete); + + return chargeForDelete.getId(); + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleFundDataIntegrityIssues(final ChargeCommand command, DataIntegrityViolationException dve) { + + Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("name")) { + throw new PlatformDataIntegrityException("error.msg.charge.duplicate.name", "Charge with name {0} already exists", "name", command.getName()); + } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue", "Unknown data integrity issue with resource: " + realCause.getMessage()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ChargeNotFoundException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ChargeNotFoundException.java new file mode 100644 index 000000000..8b821991a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ChargeNotFoundException.java @@ -0,0 +1,8 @@ +package org.mifosng.platform.exceptions; + +public class ChargeNotFoundException extends AbstractPlatformResourceNotFoundException { + + public ChargeNotFoundException(final Long id) { + super("error.msg.charge.id.invalid", "Charge with identifier " + id + " does not exist", id); + } +}