| /** |
| * 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()); |
| } |
| } |