Java tutorial
/** * 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 com.gst.accounting.rule.service; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import com.gst.accounting.closure.api.GLClosureJsonInputParams; import com.gst.accounting.glaccount.domain.GLAccount; import com.gst.accounting.glaccount.domain.GLAccountRepositoryWrapper; import com.gst.accounting.journalentry.domain.JournalEntryType; import com.gst.accounting.rule.api.AccountingRuleJsonInputParams; import com.gst.accounting.rule.domain.AccountingRule; import com.gst.accounting.rule.domain.AccountingRuleRepository; import com.gst.accounting.rule.domain.AccountingRuleRepositoryWrapper; import com.gst.accounting.rule.domain.AccountingTagRule; import com.gst.accounting.rule.exception.AccountingRuleDataException; import com.gst.accounting.rule.exception.AccountingRuleDuplicateException; import com.gst.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer; import com.gst.infrastructure.codes.domain.CodeValue; import com.gst.infrastructure.codes.domain.CodeValueRepository; import com.gst.infrastructure.codes.exception.CodeValueNotFoundException; import com.gst.infrastructure.core.api.JsonCommand; import com.gst.infrastructure.core.data.CommandProcessingResult; import com.gst.infrastructure.core.data.CommandProcessingResultBuilder; import com.gst.infrastructure.core.exception.PlatformDataIntegrityException; import com.gst.organisation.office.domain.Office; import com.gst.organisation.office.domain.OfficeRepositoryWrapper; 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 AccountingRuleWritePlatformServiceJpaRepositoryImpl implements AccountingRuleWritePlatformService { private final static Logger logger = LoggerFactory .getLogger(AccountingRuleWritePlatformServiceJpaRepositoryImpl.class); private final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper; private final AccountingRuleRepository accountingRuleRepository; private final GLAccountRepositoryWrapper accountRepositoryWrapper; private final OfficeRepositoryWrapper officeRepositoryWrapper; private final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final CodeValueRepository codeValueRepository; @Autowired public AccountingRuleWritePlatformServiceJpaRepositoryImpl( final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper, final OfficeRepositoryWrapper officeRepositoryWrapper, final AccountingRuleRepository ruleRepository, final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CodeValueRepository codeValueRepository) { this.accountRepositoryWrapper = accountRepositoryWrapper; this.officeRepositoryWrapper = officeRepositoryWrapper; this.accountingRuleRepository = ruleRepository; this.accountingRuleRepositoryWrapper = accountingRuleRepositoryWrapper; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.codeValueRepository = codeValueRepository; } /** * @param command * @param dve */ private void handleAccountingRuleIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { final Throwable realCause = dve.getMostSpecificCause(); if (realCause.getMessage().contains("accounting_rule_name_unique")) { throw new AccountingRuleDuplicateException( command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue())); } else if (realCause.getMessage().contains("UNIQUE_ACCOUNT_RULE_TAGS")) { throw new AccountingRuleDuplicateException(); } logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.accounting.rule.unknown.data.integrity.issue", "Unknown data integrity issue with resource Accounting Rule: " + realCause.getMessage()); } @Transactional @Override public CommandProcessingResult createAccountingRule(final JsonCommand command) { try { this.fromApiJsonDeserializer.validateForCreate(command.json()); // check office is valid final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue()); Office office = null; if (officeId != null) { office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId); } final AccountingRule accountingRule = assembleAccountingRuleAndTags(office, command); this.accountingRuleRepository.saveAndFlush(accountingRule); return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId) .withEntityId(accountingRule.getId()).build(); } catch (final DataIntegrityViolationException dve) { handleAccountingRuleIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } } private AccountingRule assembleAccountingRuleAndTags(final Office office, final JsonCommand command) { // get the GL Accounts or tags to Debit and Credit final String[] debitTags = command .arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()); final String[] creditTags = command .arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()); final Set<String> incomingDebitTags = debitTags == null ? new HashSet<String>() : new HashSet<>(Arrays.asList(debitTags)); final Set<String> incomingCreditTags = creditTags == null ? new HashSet<String>() : new HashSet<>(Arrays.asList(creditTags)); final Long accountToDebitId = command .longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()); final Long accountToCreditId = command .longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()); boolean allowMultipleCreditEntries = false; boolean allowMultipleDebitEntries = false; GLAccount debitAccount = null; GLAccount creditAccount = null; List<AccountingTagRule> accountingTagRules = new ArrayList<>(); if ((accountToDebitId != null && debitTags != null) || (accountToDebitId == null && debitTags == null)) { throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()); } else if (accountToDebitId != null) { debitAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId); } else if (debitTags != null) { accountingTagRules = saveDebitOrCreditTags(incomingDebitTags, JournalEntryType.DEBIT, accountingTagRules); allowMultipleDebitEntries = command.booleanPrimitiveValueOfParameterNamed( AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue()); } if ((accountToCreditId != null && creditTags != null) || (accountToCreditId == null && creditTags == null)) { throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(), AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()); } else if (accountToCreditId != null) { creditAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId); } else if (creditTags != null) { accountingTagRules = saveDebitOrCreditTags(incomingCreditTags, JournalEntryType.CREDIT, accountingTagRules); allowMultipleCreditEntries = command.booleanPrimitiveValueOfParameterNamed( AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue()); } final AccountingRule accountingRule = AccountingRule.fromJson(office, debitAccount, creditAccount, command, allowMultipleCreditEntries, allowMultipleDebitEntries); accountingRule.updateAccountingRuleForTags(accountingTagRules); return accountingRule; } @Transactional @Override public CommandProcessingResult updateAccountingRule(final Long accountingRuleId, final JsonCommand command) { try { this.fromApiJsonDeserializer.validateForUpdate(command.json()); Long officeId = null; if (command.parameterExists(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) { officeId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue()); } Long accountToDebitId = null; if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) { accountToDebitId = command .longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()); } Long accountToCreditId = null; if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) { accountToCreditId = command .longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()); } String[] debitTags = null; if (command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) { debitTags = command .arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()); } String[] creditTags = null; if (command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) { creditTags = command .arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()); } if (accountToDebitId != null && debitTags != null) { throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()); } if (accountToCreditId != null && creditTags != null) { throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(), AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()); } boolean allowMultipleCreditEntries = false; if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())) { allowMultipleCreditEntries = command.booleanPrimitiveValueOfParameterNamed( AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue()); } boolean allowMultipleDebitEntries = false; if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())) { allowMultipleDebitEntries = command.booleanPrimitiveValueOfParameterNamed( AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue()); } final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper .findOneWithNotFoundDetection(accountingRuleId); final Map<String, Object> changesOnly = accountingRule.update(command); if (accountToDebitId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) { final GLAccount accountToDebit = this.accountRepositoryWrapper .findOneWithNotFoundDetection(accountToDebitId); accountingRule.updateDebitAccount(accountToDebit); accountingRule.updateTags(JournalEntryType.CREDIT); } if (accountToCreditId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) { final GLAccount accountToCredit = this.accountRepositoryWrapper .findOneWithNotFoundDetection(accountToCreditId); accountingRule.updateCreditAccount(accountToCredit); accountingRule.updateTags(JournalEntryType.DEBIT); } if (creditTags != null && creditTags.length > 0 && command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) { final Set<String> creditTagsToAdd = determineCreditTagToAddAndRemoveOldTags(creditTags, JournalEntryType.CREDIT, accountingRule); if (!creditTagsToAdd.isEmpty()) { List<AccountingTagRule> accountingTagRules = new ArrayList<>(); accountingTagRules = saveDebitOrCreditTags(creditTagsToAdd, JournalEntryType.CREDIT, accountingTagRules); accountingRule.updateAccountingRuleForTags(accountingTagRules); accountingRule.updateCreditAccount(null); if (allowMultipleCreditEntries) { accountingRule.updateAllowMultipleCreditEntries(allowMultipleCreditEntries); } changesOnly.put(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(), creditTagsToAdd); } } if (debitTags != null && debitTags.length > 0 && command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) { final Set<String> debitTagsToAdd = determineCreditTagToAddAndRemoveOldTags(debitTags, JournalEntryType.DEBIT, accountingRule); if (!debitTagsToAdd.isEmpty()) { List<AccountingTagRule> accountingTagRules = new ArrayList<>(); accountingTagRules = saveDebitOrCreditTags(debitTagsToAdd, JournalEntryType.DEBIT, accountingTagRules); accountingRule.updateAccountingRuleForTags(accountingTagRules); accountingRule.updateDebitAccount(null); if (allowMultipleDebitEntries) { accountingRule.updateAllowMultipleDebitEntries(allowMultipleDebitEntries); } changesOnly.put(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(), debitTagsToAdd); } } if (officeId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) { final Office userOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId); accountingRule.setOffice(userOffice); } if (!changesOnly.isEmpty()) { this.accountingRuleRepository.saveAndFlush(accountingRule); } return new CommandProcessingResultBuilder().withCommandId(command.commandId()) .withEntityId(accountingRule.getId()).with(changesOnly).build(); } catch (final DataIntegrityViolationException dve) { handleAccountingRuleIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } } private Set<String> determineCreditTagToAddAndRemoveOldTags(final String[] creditOrDebitTags, final JournalEntryType type, final AccountingRule accountingRule) { final Set<String> incomingTags = new HashSet<>(Arrays.asList(creditOrDebitTags)); final Set<AccountingTagRule> existingTags = accountingRule.getAccountingTagRulesByType(type); final Set<String> existingTagIds = retrieveExistingTagIds(existingTags); final Set<String> tagsToAdd = new HashSet<>(); final Set<String> tagsToRemove = existingTagIds; final Map<Long, AccountingTagRule> accountsToRemove = new HashMap<>(); for (final String tagId : incomingTags) { if (existingTagIds.contains(tagId)) { tagsToRemove.remove(tagId); } else { tagsToAdd.add(tagId); } } if (!tagsToRemove.isEmpty()) { for (final String tagId : tagsToRemove) { for (final AccountingTagRule accountingTagRule : existingTags) { if (tagId.equals(accountingTagRule.getTagId().toString())) { accountsToRemove.put(accountingTagRule.getId(), accountingTagRule); } } } accountingRule.removeOldTags(new ArrayList<>(accountsToRemove.values())); } return tagsToAdd; } private Set<String> retrieveExistingTagIds(final Set<AccountingTagRule> existingCreditTags) { final Set<String> existingCreditTagIds = new HashSet<>(); for (final AccountingTagRule accountingTagRule : existingCreditTags) { existingCreditTagIds.add(accountingTagRule.getTagId().toString()); } return existingCreditTagIds; } @Transactional @Override public CommandProcessingResult deleteAccountingRule(final Long accountingRuleId) { final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper .findOneWithNotFoundDetection(accountingRuleId); this.accountingRuleRepository.delete(accountingRule); return new CommandProcessingResultBuilder().withEntityId(accountingRule.getId()).build(); } private List<AccountingTagRule> saveDebitOrCreditTags(final Set<String> creditOrDebitTagArray, final JournalEntryType transactionType, final List<AccountingTagRule> accountingTagRules) { for (final String creditOrDebitTag : creditOrDebitTagArray) { if (creditOrDebitTag != null && StringUtils.isNotBlank(creditOrDebitTag)) { final Long creditOrDebitTagIdLongValue = Long.valueOf(creditOrDebitTag); final CodeValue creditOrDebitAccount = this.codeValueRepository .findOne(creditOrDebitTagIdLongValue); if (creditOrDebitAccount == null) { throw new CodeValueNotFoundException(creditOrDebitTagIdLongValue); } final AccountingTagRule accountingTagRule = AccountingTagRule.create(creditOrDebitAccount, transactionType.getValue()); accountingTagRules.add(accountingTagRule); } } return accountingTagRules; } }