blob: 4c305df8441c650cfa78aaad1bb2d127fd2bd339 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.closure.service;
import java.util.Date;
import java.util.Map;
import org.apache.fineract.accounting.closure.api.GLClosureJsonInputParams;
import org.apache.fineract.accounting.closure.command.GLClosureCommand;
import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
import org.apache.fineract.accounting.closure.exception.GLClosureDuplicateException;
import org.apache.fineract.accounting.closure.exception.GLClosureInvalidDeleteException;
import org.apache.fineract.accounting.closure.exception.GLClosureInvalidException;
import org.apache.fineract.accounting.closure.exception.GLClosureNotFoundException;
import org.apache.fineract.accounting.closure.exception.GLClosureInvalidException.GL_CLOSURE_INVALID_REASON;
import org.apache.fineract.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.organisation.office.exception.OfficeNotFoundException;
import org.joda.time.LocalDate;
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 GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosureWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GLClosureWritePlatformServiceJpaRepositoryImpl.class);
private final GLClosureRepository glClosureRepository;
private final OfficeRepository officeRepository;
private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Autowired
public GLClosureWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final OfficeRepository officeRepository, final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@Transactional
@Override
public CommandProcessingResult createGLClosure(final JsonCommand command) {
try {
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForCreate();
// check office is valid
final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
final Office office = this.officeRepository.findOne(officeId);
if (office == null) { throw new OfficeNotFoundException(officeId); }
// TODO: Get Tenant specific date
// ensure closure date is not in the future
final Date todaysDate = new Date();
final Date closureDate = command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(GL_CLOSURE_INVALID_REASON.FUTURE_DATE, closureDate); }
// shouldn't be before an existing accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(officeId);
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
}
final GLClosure glClosure = GLClosure.fromJson(office, command);
this.glClosureRepository.saveAndFlush(glClosure);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withEntityId(glClosure.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleGLClosureIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult updateGLClosure(final Long glClosureId, final JsonCommand command) {
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForUpdate();
// is the glClosure valid
final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
final Map<String, Object> changesOnly = glClosure.update(command);
if (!changesOnly.isEmpty()) {
this.glClosureRepository.saveAndFlush(glClosure);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(glClosure.getOffice().getId())
.withEntityId(glClosure.getId()).with(changesOnly).build();
}
@Transactional
@Override
public CommandProcessingResult deleteGLClosure(final Long glClosureId) {
final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
/**
* check if any closures are present for this branch at a later date
* than this closure date
**/
final Date closureDate = glClosure.getClosingDate();
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(glClosure.getOffice().getId());
if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidDeleteException(latestGLClosure.getOffice()
.getId(), latestGLClosure.getOffice().getName(), latestGLClosure.getClosingDate()); }
this.glClosureRepository.delete(glClosure);
return new CommandProcessingResultBuilder().withOfficeId(glClosure.getOffice().getId()).withEntityId(glClosure.getId()).build();
}
/**
* @param command
* @param dve
*/
private void handleGLClosureIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("office_id_closing_date")) { throw new GLClosureDuplicateException(
command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue()), new LocalDate(
command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue()))); }
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
"Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
}
}