/*
* Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ru.mystamps.web.service;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.access.prepost.PreAuthorize;
import lombok.RequiredArgsConstructor;
import ru.mystamps.web.dao.SeriesDao;
import ru.mystamps.web.dao.dto.AddSeriesDbDto;
import ru.mystamps.web.dao.dto.Currency;
import ru.mystamps.web.dao.dto.ImageInfoDto;
import ru.mystamps.web.dao.dto.PurchaseAndSaleDto;
import ru.mystamps.web.dao.dto.SeriesFullInfoDto;
import ru.mystamps.web.dao.dto.SeriesInfoDto;
import ru.mystamps.web.dao.dto.SitemapInfoDto;
import ru.mystamps.web.service.dto.AddImageDto;
import ru.mystamps.web.service.dto.AddSeriesDto;
import ru.mystamps.web.service.dto.SeriesDto;
import ru.mystamps.web.support.spring.security.HasAuthority;
import ru.mystamps.web.util.CatalogUtils;
// TODO: move stamps related methods to separate interface (#88)
// The String literal "Series id must be non null" appears N times in this file
// and we think that it's OK.
@SuppressWarnings({ "PMD.TooManyMethods", "PMD.AvoidDuplicateLiterals" })
@RequiredArgsConstructor
public class SeriesServiceImpl implements SeriesService {
private static final Logger LOG = LoggerFactory.getLogger(SeriesServiceImpl.class);
private final SeriesDao seriesDao;
private final ImageService imageService;
private final StampsCatalogService michelCatalogService;
private final StampsCatalogService scottCatalogService;
private final StampsCatalogService yvertCatalogService;
private final StampsCatalogService gibbonsCatalogService;
@Override
@Transactional
@PreAuthorize(HasAuthority.CREATE_SERIES)
@SuppressWarnings({ "PMD.NPathComplexity", "PMD.ModifiedCyclomaticComplexity" })
public Integer add(AddSeriesDto dto, Integer userId, boolean userCanAddComments) {
Validate.isTrue(dto != null, "DTO must be non null");
Validate.isTrue(dto.getQuantity() != null, "Stamps quantity must be non null");
Validate.isTrue(
dto.getPerforated() != null,
"Stamps perforated property must be non null"
);
Validate.isTrue(dto.getCategory() != null, "Category must be non null");
Validate.isTrue(dto.getCategory().getId() != null, "Category id must be non null");
Validate.isTrue(userId != null, "Current user id must be non null");
AddSeriesDbDto series = new AddSeriesDbDto();
if (dto.getCountry() != null) {
series.setCountryId(dto.getCountry().getId());
}
setDateOfReleaseIfProvided(dto, series);
series.setCategoryId(dto.getCategory().getId());
series.setQuantity(dto.getQuantity());
series.setPerforated(dto.getPerforated());
if (dto.getMichelPrice() != null) {
series.setMichelPrice(dto.getMichelPrice());
series.setMichelCurrency(Currency.EUR.toString());
}
if (dto.getScottPrice() != null) {
series.setScottPrice(dto.getScottPrice());
series.setScottCurrency(Currency.USD.toString());
}
if (dto.getYvertPrice() != null) {
series.setYvertPrice(dto.getYvertPrice());
series.setYvertCurrency(Currency.EUR.toString());
}
if (dto.getGibbonsPrice() != null) {
series.setGibbonsPrice(dto.getGibbonsPrice());
series.setGibbonsCurrency(Currency.GBP.toString());
}
if (userCanAddComments && dto.getComment() != null) {
Validate.isTrue(
!dto.getComment().trim().isEmpty(),
"Comment must be non empty"
);
series.setComment(dto.getComment());
}
Date now = new Date();
series.setCreatedAt(now);
series.setUpdatedAt(now);
series.setCreatedBy(userId);
series.setUpdatedBy(userId);
Integer id = seriesDao.add(series);
Set<String> michelNumbers = CatalogUtils.parseCatalogNumbers(dto.getMichelNumbers());
if (!michelNumbers.isEmpty()) {
michelCatalogService.add(michelNumbers);
michelCatalogService.addToSeries(id, michelNumbers);
}
Set<String> scottNumbers = CatalogUtils.parseCatalogNumbers(dto.getScottNumbers());
if (!scottNumbers.isEmpty()) {
scottCatalogService.add(scottNumbers);
scottCatalogService.addToSeries(id, scottNumbers);
}
Set<String> yvertNumbers = CatalogUtils.parseCatalogNumbers(dto.getYvertNumbers());
if (!yvertNumbers.isEmpty()) {
yvertCatalogService.add(yvertNumbers);
yvertCatalogService.addToSeries(id, yvertNumbers);
}
Set<String> gibbonsNumbers = CatalogUtils.parseCatalogNumbers(dto.getGibbonsNumbers());
if (!gibbonsNumbers.isEmpty()) {
gibbonsCatalogService.add(gibbonsNumbers);
gibbonsCatalogService.addToSeries(id, gibbonsNumbers);
}
ImageInfoDto imageInfo = imageService.save(dto.getImage());
imageService.addToSeries(id, imageInfo.getId());
LOG.info("Series #{} has been created ({})", id, series);
return id;
}
@Override
@Transactional
@PreAuthorize("isAuthenticated()")
public void addImageToSeries(AddImageDto dto, Integer seriesId, Integer userId) {
Validate.isTrue(dto != null, "DTO must be non null");
Validate.isTrue(seriesId != null, "Series id must be non null");
Validate.isTrue(userId != null, "User id must be non null");
seriesDao.markAsModified(seriesId, new Date(), userId);
ImageInfoDto imageInfo = imageService.save(dto.getImage());
Integer imageId = imageInfo.getId();
imageService.addToSeries(seriesId, imageId);
LOG.info(
"Image #{} has been added to series #{} by user #{}",
imageId,
seriesId,
userId
);
}
@Override
@Transactional(readOnly = true)
public long countAll() {
return seriesDao.countAll();
}
@Override
@Transactional(readOnly = true)
public long countAllStamps() {
return seriesDao.countAllStamps();
}
@Override
@Transactional(readOnly = true)
public long countSeriesOf(Integer collectionId) {
Validate.isTrue(collectionId != null, "Collection id must be non null");
return seriesDao.countSeriesOfCollection(collectionId);
}
@Override
@Transactional(readOnly = true)
public long countStampsOf(Integer collectionId) {
Validate.isTrue(collectionId != null, "Collection id must be non null");
return seriesDao.countStampsOfCollection(collectionId);
}
@Override
@Transactional(readOnly = true)
public long countAddedSince(Date date) {
Validate.isTrue(date != null, "Date must be non null");
return seriesDao.countAddedSince(date);
}
@Override
@Transactional(readOnly = true)
public long countUpdatedSince(Date date) {
Validate.isTrue(date != null, "Date must be non null");
return seriesDao.countUpdatedSince(date);
}
@Override
@Transactional(readOnly = true)
public boolean isSeriesExist(Integer seriesId) {
Validate.isTrue(seriesId != null, "Series id must be non null");
return seriesDao.countSeriesById(seriesId) > 0;
}
@Override
@Transactional(readOnly = true)
public SeriesDto findFullInfoById(Integer seriesId, String lang) {
Validate.isTrue(seriesId != null, "Series id must be non null");
SeriesFullInfoDto seriesBaseInfo = seriesDao.findByIdAsSeriesFullInfo(seriesId, lang);
if (seriesBaseInfo == null) {
return null;
}
List<String> michelNumbers = michelCatalogService.findBySeriesId(seriesId);
List<String> scottNumbers = scottCatalogService.findBySeriesId(seriesId);
List<String> yvertNumbers = yvertCatalogService.findBySeriesId(seriesId);
List<String> gibbonsNumbers = gibbonsCatalogService.findBySeriesId(seriesId);
List<Integer> imageIds = imageService.findBySeriesId(seriesId);
return new SeriesDto(
seriesBaseInfo,
michelNumbers,
scottNumbers,
yvertNumbers,
gibbonsNumbers,
imageIds
);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByMichelNumber(String michelNumberCode, String lang) {
Validate.isTrue(michelNumberCode != null, "Michel number code must be non null");
Validate.isTrue(!michelNumberCode.trim().isEmpty(), "Michel number code must be non empty");
List<Integer> seriesIds = seriesDao.findSeriesIdsByMichelNumberCode(michelNumberCode);
if (seriesIds.isEmpty()) {
return Collections.emptyList();
}
return seriesDao.findByIdsAsSeriesInfo(seriesIds, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByScottNumber(String scottNumberCode, String lang) {
Validate.isTrue(scottNumberCode != null, "Scott number code must be non null");
Validate.isTrue(!scottNumberCode.trim().isEmpty(), "Scott number code must be non empty");
List<Integer> seriesIds = seriesDao.findSeriesIdsByScottNumberCode(scottNumberCode);
if (seriesIds.isEmpty()) {
return Collections.emptyList();
}
return seriesDao.findByIdsAsSeriesInfo(seriesIds, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByYvertNumber(String yvertNumberCode, String lang) {
Validate.isTrue(yvertNumberCode != null, "Yvert number code must be non null");
Validate.isTrue(!yvertNumberCode.trim().isEmpty(), "Yvert number code must be non empty");
List<Integer> seriesIds = seriesDao.findSeriesIdsByYvertNumberCode(yvertNumberCode);
if (seriesIds.isEmpty()) {
return Collections.emptyList();
}
return seriesDao.findByIdsAsSeriesInfo(seriesIds, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByGibbonsNumber(String gibbonsNumberCode, String lang) {
Validate.isTrue(gibbonsNumberCode != null, "Gibbons number code must be non null");
Validate.isTrue(
!gibbonsNumberCode.trim().isEmpty(),
"Gibbons number code must be non empty"
);
List<Integer> seriesIds = seriesDao.findSeriesIdsByGibbonsNumberCode(gibbonsNumberCode);
if (seriesIds.isEmpty()) {
return Collections.emptyList();
}
return seriesDao.findByIdsAsSeriesInfo(seriesIds, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByCategorySlug(String slug, String lang) {
Validate.isTrue(slug != null, "Category slug must be non null");
return seriesDao.findByCategorySlugAsSeriesInfo(slug, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByCountrySlug(String slug, String lang) {
Validate.isTrue(slug != null, "Country slug must be non null");
return seriesDao.findByCountrySlugAsSeriesInfo(slug, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findByCollectionId(Integer collectionId, String lang) {
Validate.isTrue(collectionId != null, "Collection id must be non null");
return seriesDao.findByCollectionIdAsSeriesInfo(collectionId, lang);
}
@Override
@Transactional(readOnly = true)
public List<SeriesInfoDto> findRecentlyAdded(int quantity, String lang) {
Validate.isTrue(quantity > 0, "Quantity of recently added series must be greater than 0");
return seriesDao.findLastAdded(quantity, lang);
}
@Override
@Transactional(readOnly = true)
public List<SitemapInfoDto> findAllForSitemap() {
return seriesDao.findAllForSitemap();
}
/**
* @author Sergey Chechenev
*/
@Override
@Transactional(readOnly = true)
@PreAuthorize(HasAuthority.VIEW_SERIES_SALES)
public List<PurchaseAndSaleDto> findPurchasesAndSales(Integer seriesId) {
Validate.isTrue(seriesId != null, "Series id must be non null");
return seriesDao.findPurchasesAndSales(seriesId);
}
private static void setDateOfReleaseIfProvided(AddSeriesDto dto, AddSeriesDbDto series) {
if (dto.getYear() == null) {
return;
}
series.setReleaseYear(dto.getYear());
if (dto.getMonth() == null) {
return;
}
series.setReleaseMonth(dto.getMonth());
// even if day is null it won't change anything
series.setReleaseDay(dto.getDay());
}
}