/* * Copyright (C) 2016 Arthur Gregorio, AG.Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package br.com.webbudget.domain.model.service; import br.com.webbudget.application.component.table.Page; import br.com.webbudget.application.component.table.PageRequest; import br.com.webbudget.domain.misc.ApportionmentBuilder; import br.com.webbudget.domain.misc.MovementBuilder; import br.com.webbudget.domain.misc.events.CreateMovement; import br.com.webbudget.domain.misc.events.DeleteMovement; import br.com.webbudget.domain.misc.events.MovementDeleted; import br.com.webbudget.domain.misc.ex.InternalServiceError; import br.com.webbudget.domain.model.entity.entries.MovementClass; import br.com.webbudget.domain.model.entity.entries.MovementClassType; import br.com.webbudget.domain.model.entity.logbook.Entry; import br.com.webbudget.domain.model.entity.logbook.Refueling; import br.com.webbudget.domain.model.entity.logbook.Vehicle; import br.com.webbudget.domain.model.repository.entries.IMovementClassRepository; import br.com.webbudget.domain.model.repository.logbook.IEntryRepository; import br.com.webbudget.domain.model.repository.entries.IVehicleRepository; import br.com.webbudget.domain.model.repository.logbook.IFuelRepository; import br.com.webbudget.domain.model.repository.logbook.IRefuelingRepository; import java.math.BigDecimal; import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.transaction.Transactional; import org.apache.commons.lang3.StringUtils; /** * Servico para executar as operacoes pertinentes ao diario de bordo * * @author Arthur Gregorio * * @version 1.0.0 * @since 2.3.0, 05/06/2016 */ @ApplicationScoped public class LogbookService { @Inject private IFuelRepository fuelRepository; @Inject private IEntryRepository entryRepository; @Inject private IVehicleRepository vehicleRepository; @Inject private IRefuelingRepository refuelingRepository; @Inject private IMovementClassRepository movementClassRepository; @Inject @DeleteMovement private Event<String> deleteMovementEvent; @Inject @CreateMovement private Event<MovementBuilder> createMovementEvent; /** * * @param vehicle */ @Transactional public void saveVehicle(Vehicle vehicle) { this.vehicleRepository.save(vehicle); } /** * * @param vehicle * @return */ @Transactional public Vehicle updateVehicle(Vehicle vehicle) { return this.vehicleRepository.save(vehicle); } /** * * @param vehicle */ @Transactional public void deleteVehicle(Vehicle vehicle) { this.vehicleRepository.delete(vehicle); } /** * * @param entry */ @Transactional public void saveEntry(Entry entry) { // caso seja registro financeiro, checa a integridade if (entry.isFinancial() && !entry.isFinancialValid()) { throw new InternalServiceError("error.entry.financial-invalid"); } // se temos um registro financeiro, incluimos o movimento if (entry.isFinancial()) { // cria o movimento final MovementBuilder builder = new MovementBuilder() .withValue(entry.getCost()) .onDueDate(entry.getEventDate()) .describedBy(entry.getTitle()) .inThePeriodOf(entry.getFinancialPeriod()) .dividedAmong(new ApportionmentBuilder() .onCostCenter(entry.getCostCenter()) .withMovementClass(entry.getMovementClass()) .withValue(entry.getCost())); entry.setMovementCode(builder.getMovementCode()); this.createMovementEvent.fire(builder); } final int lastVehicleOdometer = this.vehicleRepository .findLastOdometer(entry.getVehicle()); if (entry.getOdometer() > lastVehicleOdometer) { // atualizamos a distancia percorrida entry.setDistance(entry.getOdometer() - lastVehicleOdometer); // atualizamos o odometro entry.updateVehicleOdometer(); // salvamos o carro this.vehicleRepository.save(entry.getVehicle()); } // salva o registro this.entryRepository.save(entry); } /** * * @param entry * @return */ @Transactional public Entry updateEntry(Entry entry) { return this.entryRepository.save(entry); } /** * * @param entry */ @Transactional public void deleteEntry(Entry entry) { // deleta o registro this.entryRepository.delete(entry); // se tem movimento, deleta ele tambem if (entry.isFinancial()) { this.deleteMovementEvent.fire(entry.getMovementCode()); } } /** * * @param refueling */ @Transactional public void saveRefueling(Refueling refueling) { if (!refueling.isFuelsValid()) { throw new InternalServiceError("error.refueling.invalid-fuels"); } // pega o ultimo odometro para calcular a distancia percorrida int lastOdometer = this.refuelingRepository .findLastOdometerForVehicle(refueling.getVehicle()); // calcula a distancia refueling.setFirstRefueling(lastOdometer == 0); refueling.calculateDistance(lastOdometer); // pegamos a lista dos abastecimentos nao contabilizados e reservamos final List<Refueling> unaccounteds = this.refuelingRepository .findUnaccountedsForVehicle(refueling.getVehicle()); // se nao e um tanque cheio, marcamos que sera contabilizado no proximo // tanque cheio, se for, contabiliza a media do tanque if (refueling.isFullTank()) { // montamos o valor do ultimo odometro com base nas parciais ou // com base no ultimo odometro registrado com tanque cheio if (!unaccounteds.isEmpty()) { final int totalDistance = unaccounteds. stream() .mapToInt(Refueling::getDistance) .sum() + refueling.getDistance(); // pega o total de litros utilizados final BigDecimal liters = unaccounteds.stream() .map(Refueling::getLiters) .reduce(BigDecimal.ZERO, BigDecimal::add) .add(refueling.getLiters()); // adiciona os litros atuais e manda calcular a media refueling.calculateAverageComsumption(totalDistance, liters); } else { refueling.calculateAverageComsumption(); } // seta os que nao estavam contabilizados como contabilizados unaccounteds.stream().forEach(unaccounted -> { unaccounted.setAccounted(true); unaccounted.setAccountedBy(refueling.getCode()); this.refuelingRepository.save(unaccounted); }); // marca o abastecimento atual como contabilizado refueling.setAccounted(true); } else { refueling.setAccounted(false); } // salvamos o abastecimento final Refueling saved = this.refuelingRepository.save(refueling); // salvamos os combustiveis refueling.getFuels().stream().forEach(fuel -> { fuel.setRefueling(saved); this.fuelRepository.save(fuel); }); // gerar o movimento financeiro final MovementBuilder builder = new MovementBuilder() .withValue(saved.getCost()) .onDueDate(saved.getEventDate()) .describedBy(saved.createMovementDescription()) .inThePeriodOf(saved.getFinancialPeriod()) .dividedAmong(new ApportionmentBuilder() .onCostCenter(saved.getCostCenter()) .withMovementClass(saved.getMovementClass()) .withValue(saved.getCost())); saved.setMovementCode(builder.getMovementCode()); // invoca inclusao do movimento this.createMovementEvent.fire(builder); // atualiza o codigo do movimento no abastecimento this.refuelingRepository.save(saved); final int lastVehicleOdometer = this.vehicleRepository .findLastOdometer(refueling.getVehicle()); // atualiza ou nao o odometro if (refueling.getOdometer() > lastVehicleOdometer) { refueling.updateVehicleOdometer(); this.vehicleRepository.save(refueling.getVehicle()); } } /** * * @param refueling */ @Transactional public void deleteRefueling(Refueling refueling) { // verifica se estamos deletando o ultimo abastecimento if (!this.refuelingRepository.isLast(refueling)) { throw new InternalServiceError("error.refueling.not-last"); } // lista os contabilizados por esta abastecimento que queremos deletar final List<Refueling> accounteds = this.refuelingRepository .listAccountedsBy(refueling.getCode()); // volta os contabilizados para nao contabilizados accounteds.stream().forEach(accounted -> { accounted.setAccounted(false); accounted.setAccountedBy(null); this.refuelingRepository.save(refueling); }); // deleta o abastecimento this.refuelingRepository.delete(refueling); // dispara o evento para deletar o movimento caso ainda haja movimento // vinculado a este abastecimento if (StringUtils.isNotBlank(refueling.getMovementCode())) { this.deleteMovementEvent.fire(refueling.getMovementCode()); } } /** * Quando um movimento for deletado este evento escurtara por uma possivel * delecao de um evento vinculado com um registro do logbook * * @param code o codigo do movimento */ public void whenEntryMovementDeleted(@Observes @MovementDeleted String code) { // procura pelo registro final Entry entry = this.entryRepository.findByMovementCode(code); // se encontrar, limpa as flags de movimentacao financeira if (entry != null) { entry.setFinancial(false); entry.setMovementClass(null); entry.setFinancialPeriod(null); entry.setMovementCode(null); this.entryRepository.save(entry); } } /** * Escuta o evento de delecao de um movimento para verificar se o mesmo * pertence a um abastecimento, se sim, limpa a flag do codigo do movimento * vinculado a ele * * @param code o codigo do movimento deletado */ public void whenRefuelingMovementDeleted(@Observes @MovementDeleted String code) { final Refueling refueling = this.refuelingRepository.findByMovementCode(code); // se achar, limpa a flag e salva if (refueling != null) { refueling.setMovementCode(null); this.refuelingRepository.save(refueling); } } /** * * @param entryId * @return */ public Entry findEntryById(long entryId) { return this.entryRepository.findById(entryId, false); } /** * * @param vehicleId * @return */ public Vehicle findVehicleById(long vehicleId) { return this.vehicleRepository.findById(vehicleId, false); } /** * * @param refuelingId * @return */ public Refueling findRefuelingById(long refuelingId) { return this.refuelingRepository.findById(refuelingId, false); } /** * * @param isBlocked * @return */ public List<Vehicle> listVehicles(boolean isBlocked) { return this.vehicleRepository.listByStatus(isBlocked); } /** * * @param isBlocked * @param pageRequest * @return */ public Page<Vehicle> listVehiclesLazily(Boolean isBlocked, PageRequest pageRequest) { return this.vehicleRepository.listLazilyByStatus(isBlocked, pageRequest); } /** * * @param vehicle * @return */ public List<Entry> listEntriesByVehicle(Vehicle vehicle) { return this.entryRepository.listByVehicle(vehicle); } /** * * @param vehicle * @return */ public List<MovementClass> listClassesForVehicle(Vehicle vehicle) { return this.movementClassRepository.listByCostCenterAndType( vehicle.getCostCenter(), MovementClassType.OUT); } /** * * @param vehicle * @param filter * @return */ public List<Entry> listEntriesByVehicleAndFilter(Vehicle vehicle, String filter) { return this.entryRepository.listByVehicleAndFilter(vehicle, filter); } /** * * @param filter * @param pageRequest * @return */ public Page<Refueling> listRefuelingsLazily(String filter, PageRequest pageRequest) { return this.refuelingRepository.listLazily(filter, pageRequest); } }