First commit waiting for Budget Alert

This commit is contained in:
2025-09-04 13:37:35 +01:00
commit 2d681f27f5
4563 changed files with 1061534 additions and 0 deletions

View File

@ -0,0 +1,26 @@
apply plugin: "com.axelor.app-module"
apply from: "../version.gradle"
apply {
version = openSuiteVersion
}
axelor {
title "Axelor Base"
description "Axelor Base Module"
}
dependencies {
compile project(":modules:axelor-admin")
compile project(":modules:axelor-message")
compile "org.apache.commons:commons-lang3:3.2"
compile "org.apache.commons:commons-math3:3.6.1"
compile "commons-validator:commons-validator:1.4.0"
compile "org.mnode.ical4j:ical4j:2.2.0"
compile "org.mnode.ical4j:ical4j-extensions:1.0.3"
compile "org.mnode.ical4j:ical4j-connector:1.0.1"
compile "com.google.zxing:core:3.3.2"
compile 'org.iban4j:iban4j:3.2.1'
compile 'com.itextpdf:itextpdf:5.5.13.1'
}

View File

@ -0,0 +1,36 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps;
import com.axelor.apps.report.engine.EmbeddedReportSettings;
import com.axelor.apps.report.engine.ExternalReportSettings;
import com.axelor.apps.report.engine.ReportSettings;
public class ReportFactory {
public static ReportSettings createReport(String rptdesign, String outputName) {
if (ReportSettings.useIntegratedEngine()) {
return new EmbeddedReportSettings(rptdesign, outputName);
} else {
return new ExternalReportSettings(rptdesign, outputName);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.ABCAnalysis;
public class ABCAnalysisBaseRepository extends ABCAnalysisRepository {
@Override
public ABCAnalysis copy(ABCAnalysis entity, boolean deep) {
ABCAnalysis abcAnalysis = super.copy(entity, true);
abcAnalysis.setStatusSelect(STATUS_DRAFT);
abcAnalysis.setAbcAnalysisSeq(null);
return abcAnalysis;
}
}

View File

@ -0,0 +1,41 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.service.AddressService;
import com.google.inject.Inject;
import javax.persistence.PersistenceException;
public class AddressBaseRepository extends AddressRepository {
@Inject protected AddressService addressService;
@Override
public Address save(Address entity) {
entity.setFullName(addressService.computeFullName(entity));
try {
addressService.updateLatLong(entity);
} catch (Exception e) {
throw new PersistenceException(e);
}
return super.save(entity);
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.AlarmEngineBatch;
public class AlarmEngineBatchBaseRepository extends AlarmEngineBatchRepository {
@Override
public AlarmEngineBatch copy(AlarmEngineBatch entity, boolean deep) {
AlarmEngineBatch copy = super.copy(entity, deep);
copy.setBatchList(null);
return copy;
}
}

View File

@ -0,0 +1,33 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.BankAddress;
import com.axelor.apps.base.service.BankAddressService;
import com.google.inject.Inject;
public class BankAddressBaseRepository extends BankAddressRepository {
@Inject BankAddressService bankAddressService;
@Override
public BankAddress save(BankAddress bankAddress) {
bankAddress.setFullAddress(bankAddressService.computeFullAddress(bankAddress));
return super.save(bankAddress);
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.service.BankService;
import com.google.inject.Inject;
public class BankBaseRepository extends BankRepository {
@Inject BankService bankService;
@Override
public Bank save(Bank bank) {
bankService.splitBic(bank);
bankService.computeFullName(bank);
return super.save(bank);
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.BaseBatch;
public class BaseBatchBaseRepository extends BaseBatchRepository {
@Override
public BaseBatch copy(BaseBatch entity, boolean deep) {
BaseBatch copy = super.copy(entity, deep);
copy.setBatchList(null);
return copy;
}
}

View File

@ -0,0 +1,61 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Duration;
import com.axelor.i18n.I18n;
import javax.persistence.PersistenceException;
public class DurationBaseRepository extends DurationRepository {
@Override
public Duration save(Duration duration) {
try {
duration.setName(this.computeName(duration.getTypeSelect(), duration.getValue()));
return super.save(duration);
} catch (Exception e) {
throw new PersistenceException(e.getLocalizedMessage());
}
}
public String computeName(int typeSelect, int value) {
String name = "";
switch (typeSelect) {
case TYPE_MONTH:
name += "month";
break;
case TYPE_DAY:
name += "day";
break;
default:
break;
}
if (value > 1) {
name += "s";
}
return value + " " + I18n.get(name);
}
}

View File

@ -0,0 +1,87 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.ICalendarEvent;
import com.axelor.apps.base.db.ICalendarUser;
import com.axelor.apps.base.ical.ICalendarService;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
public class ICalendarEventManagementRepository extends ICalendarEventRepository {
@Inject private ICalendarService calendarService;
@Override
public ICalendarEvent save(ICalendarEvent entity) {
User creator = entity.getCreatedBy();
if (creator == null) {
creator = AuthUtils.getUser();
}
if (entity.getOrganizer() == null && creator != null) {
if (creator.getPartner() != null && creator.getPartner().getEmailAddress() != null) {
String email = creator.getPartner().getEmailAddress().getAddress();
if (email != null) {
ICalendarUser organizer =
Beans.get(ICalendarUserRepository.class)
.all()
.filter("self.email = ?1 AND self.user.id = ?2", email, creator.getId())
.fetchOne();
if (organizer == null) {
organizer = new ICalendarUser();
organizer.setEmail(email);
organizer.setName(creator.getFullName());
organizer.setUser(creator);
}
entity.setOrganizer(organizer);
}
}
}
entity.setSubjectTeam(entity.getSubject());
if (entity.getVisibilitySelect() == ICalendarEventRepository.VISIBILITY_PRIVATE) {
entity.setSubjectTeam(I18n.get("Available"));
if (entity.getDisponibilitySelect() == ICalendarEventRepository.DISPONIBILITY_BUSY) {
entity.setSubjectTeam(I18n.get("Busy"));
}
}
return super.save(entity);
}
@Override
public void remove(ICalendarEvent entity) {
remove(entity, true);
}
public void remove(ICalendarEvent entity, boolean removeRemote) {
try {
if (removeRemote) {
calendarService.removeEventFromIcal(entity);
}
} catch (Exception e) {
TraceBackService.trace(e);
}
super.remove(entity);
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.MailBatch;
public class MailBatchBaseRepository extends MailBatchRepository {
@Override
public MailBatch copy(MailBatch entity, boolean deep) {
MailBatch copy = super.copy(entity, deep);
copy.setBatchList(null);
return copy;
}
}

View File

@ -0,0 +1,42 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.MailingListMessage;
import com.axelor.auth.AuthUtils;
import com.axelor.inject.Beans;
import com.axelor.mail.db.MailFollower;
import com.axelor.mail.db.repo.MailFollowerRepository;
import java.util.Map;
public class MailingListMessageBaseRepository extends MailingListMessageRepository {
@Override
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
if (json != null && json.get("id") != null) {
final MailingListMessage entity = find((Long) json.get("id"));
final MailFollowerRepository followers = Beans.get(MailFollowerRepository.class);
final MailFollower follower = followers.findOne(entity, AuthUtils.getUser());
json.put("_following", follower != null && follower.getArchived() == Boolean.FALSE);
json.put("_image", entity.getImage() != null);
}
return json;
}
}

View File

@ -0,0 +1,97 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PartnerAddress;
import com.axelor.apps.base.service.PartnerService;
import com.axelor.auth.db.User;
import com.axelor.inject.Beans;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Map;
import javax.persistence.PersistenceException;
public class PartnerBaseRepository extends PartnerRepository {
@Override
public Partner save(Partner partner) {
try {
Beans.get(PartnerService.class).onSave(partner);
return super.save(partner);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
@Override
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
if (!context.containsKey("json-enhance")) {
return json;
}
try {
Long id = (Long) json.get("id");
Partner partner = find(id);
json.put("address", Beans.get(PartnerService.class).getDefaultAddress(partner));
} catch (Exception e) {
e.printStackTrace();
}
return json;
}
@Override
public Partner copy(Partner partner, boolean deep) {
Partner copy = super.copy(partner, deep);
copy.setPartnerSeq(null);
copy.setEmailAddress(null);
copy.setBarCodeSeq(null);
PartnerAddressRepository partnerAddressRepository = Beans.get(PartnerAddressRepository.class);
List<PartnerAddress> partnerAddressList = Lists.newArrayList();
if (deep && copy.getPartnerAddressList() != null) {
for (PartnerAddress partnerAddress : copy.getPartnerAddressList()) {
partnerAddressList.add(partnerAddressRepository.copy(partnerAddress, deep));
}
}
copy.setPartnerAddressList(partnerAddressList);
copy.setBlockingList(null);
copy.setBankDetailsList(null);
return copy;
}
@Override
public void remove(Partner partner) {
if (partner.getLinkedUser() != null) {
UserBaseRepository userRepo = Beans.get(UserBaseRepository.class);
User user = userRepo.find(partner.getLinkedUser().getId());
if (user != null) {
user.setPartner(null);
userRepo.save(user);
}
}
super.remove(partner);
}
}

View File

@ -0,0 +1,109 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.service.BarcodeGeneratorService;
import com.axelor.apps.base.service.ProductService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.tool.service.TranslationService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import javax.persistence.PersistenceException;
import javax.validation.ValidationException;
public class ProductBaseRepository extends ProductRepository {
@Inject private MetaFiles metaFiles;
@Inject protected AppBaseService appBaseService;
@Inject protected TranslationService translationService;
protected static final String FULL_NAME_FORMAT = "[%s] %s";
@Inject protected BarcodeGeneratorService barcodeGeneratorService;
@Override
public Product save(Product product) {
try {
if (appBaseService.getAppBase().getGenerateProductSequence()
&& Strings.isNullOrEmpty(product.getCode())) {
product.setCode(Beans.get(ProductService.class).getSequence());
}
} catch (Exception e) {
throw new PersistenceException(e.getLocalizedMessage());
}
product.setFullName(String.format(FULL_NAME_FORMAT, product.getCode(), product.getName()));
if (product.getId() != null) {
Product oldProduct = Beans.get(ProductRepository.class).find(product.getId());
translationService.updateFormatedValueTranslations(
oldProduct.getFullName(), FULL_NAME_FORMAT, product.getCode(), product.getName());
} else {
translationService.createFormatedValueTranslations(
FULL_NAME_FORMAT, product.getCode(), product.getName());
}
product = super.save(product);
if (product.getBarCode() == null
&& appBaseService.getAppBase().getActivateBarCodeGeneration()) {
try {
boolean addPadding = false;
InputStream inStream;
if (!appBaseService.getAppBase().getEditProductBarcodeType()) {
inStream =
barcodeGeneratorService.createBarCode(
product.getSerialNumber(),
appBaseService.getAppBase().getBarcodeTypeConfig(),
addPadding);
} else {
inStream =
barcodeGeneratorService.createBarCode(
product.getSerialNumber(), product.getBarcodeTypeConfig(), addPadding);
}
if (inStream != null) {
MetaFile barcodeFile =
metaFiles.upload(inStream, String.format("ProductBarCode%d.png", product.getId()));
product.setBarCode(barcodeFile);
}
} catch (IOException e) {
e.printStackTrace();
} catch (AxelorException e) {
throw new ValidationException(e.getMessage());
}
}
return super.save(product);
}
@Override
public Product copy(Product product, boolean deep) {
Product copy = super.copy(product, deep);
Beans.get(ProductService.class).copyProduct(product, copy);
return copy;
}
}

View File

@ -0,0 +1,33 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Sequence;
public class SequenceBaseRepository extends SequenceRepository {
@Override
public Sequence copy(Sequence sequence, boolean deep) {
Sequence copy = super.copy(sequence, deep);
copy.clearSequenceVersionList();
return copy;
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.account.db.Tax;
import com.axelor.apps.account.db.repo.TaxRepository;
public class TaxBaseRepository extends TaxRepository {
@Override
public Tax copy(Tax entity, boolean deep) {
Tax copy = super.copy(entity, deep);
copy.setActiveTaxLine(null);
return copy;
}
}

View File

@ -0,0 +1,75 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Frequency;
import com.axelor.apps.base.service.TeamTaskService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.team.db.TeamTask;
import com.axelor.team.db.repo.TeamTaskRepository;
import javax.persistence.PersistenceException;
public class TeamTaskBaseRepository extends TeamTaskRepository {
@Override
public TeamTask save(TeamTask teamTask) {
TeamTaskService teamTaskService = Beans.get(TeamTaskService.class);
if (teamTask.getDoApplyToAllNextTasks()
&& teamTask.getNextTeamTask() != null
&& teamTask.getHasDateOrFrequencyChanged()) {
// remove next tasks
teamTaskService.removeNextTasks(teamTask);
// regenerate new tasks
teamTask.setIsFirst(true);
}
Frequency frequency = teamTask.getFrequency();
if (frequency != null && teamTask.getIsFirst() && teamTask.getNextTeamTask() == null) {
if (teamTask.getTaskDate() != null) {
if (frequency.getEndDate().isBefore(teamTask.getTaskDate())) {
throw new PersistenceException(
I18n.get("Frequency end date cannot be before task date."));
}
} else {
throw new PersistenceException(I18n.get("Please fill in task date."));
}
teamTaskService.generateTasks(teamTask, frequency);
}
if (teamTask.getDoApplyToAllNextTasks()) {
teamTaskService.updateNextTask(teamTask);
}
teamTask.setDoApplyToAllNextTasks(false);
teamTask.setHasDateOrFrequencyChanged(false);
return super.save(teamTask);
}
@Override
public TeamTask copy(TeamTask entity, boolean deep) {
entity.setAssignedTo(null);
entity.setTaskDate(null);
entity.setPriority(null);
return super.copy(entity, deep);
}
}

View File

@ -0,0 +1,111 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.common.StringUtils;
import com.axelor.db.Query;
import com.axelor.inject.Beans;
import javax.persistence.PersistenceException;
public class UserBaseRepository extends UserRepository {
@Override
public User save(User user) {
try {
if (user.getPartner() != null
&& user.getPartner().getEmailAddress() != null
&& StringUtils.notBlank(user.getPartner().getEmailAddress().getAddress())
&& !user.getPartner().getEmailAddress().getAddress().equals(user.getEmail())) {
user.setEmail(user.getPartner().getEmailAddress().getAddress());
}
user = super.save(user);
if (StringUtils.notBlank(user.getTransientPassword())) {
Beans.get(UserService.class).processChangedPassword(user);
}
return user;
} catch (Exception e) {
e.printStackTrace();
throw new PersistenceException(e.getLocalizedMessage());
}
}
@Override
public User copy(User entity, boolean deep) {
User copy = new User();
copy.setGroup(entity.getGroup());
copy.setRoles(entity.getRoles());
copy.setPermissions(entity.getPermissions());
copy.setMetaPermissions(entity.getMetaPermissions());
copy.setActiveCompany(entity.getActiveCompany());
copy.setCompanySet(entity.getCompanySet());
copy.setLanguage(entity.getLanguage());
copy.setHomeAction(entity.getHomeAction());
copy.setSingleTab(entity.getSingleTab());
copy.setNoHelp(entity.getNoHelp());
return super.copy(copy, deep);
}
@Override
public void remove(User user) {
if (user.getPartner() != null) {
PartnerBaseRepository partnerRepo = Beans.get(PartnerBaseRepository.class);
Partner partner = partnerRepo.find(user.getPartner().getId());
if (partner != null) {
partner.setLinkedUser(null);
partnerRepo.save(partner);
}
}
super.remove(user);
}
@Override
public User findByEmail(String email) {
return Query.of(User.class)
.filter(
""
+ "LOWER(self.partner.emailAddress.address) = LOWER(:email) "
+ "OR LOWER(self.email) = LOWER(:email)")
.bind("email", email)
.cacheable()
.fetchOne();
}
@Override
public User findByCodeOrEmail(String codeOrEmail) {
return Query.of(User.class)
.filter(
""
+ "LOWER(self.code) = LOWER(:codeOrEmail) "
+ "OR LOWER(self.partner.emailAddress.address) = LOWER(:codeOrEmail) "
+ "OR LOWER(self.email) = LOWER(:codeOrEmail)")
.bind("codeOrEmail", codeOrEmail)
.cacheable()
.fetchOne();
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.Year;
public class YearBaseRepository extends YearRepository {
@Override
public Year copy(Year year, boolean deep) {
year.setPeriodList(null);
return super.copy(year, deep);
}
}

View File

@ -0,0 +1,380 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.exceptions;
/**
* Interface of Exceptions.
*
* @author dubaux
*/
public interface IExceptionMessage {
public static final String EXCEPTION = /*$$(*/ "Warning !" /*)*/;
static final String NOT_IMPLEMENTED_METHOD = /*$$(*/ "Not implemented yet!" /*)*/;
static final String BIRT_EXTERNAL_REPORT_NO_URL = /*$$(*/
"Impossible to generate report, url toward Birt viewer is not correctly configured (%s)" /*)*/;
static final String RECORD_UNSAVED = /*$$(*/ "Unsaved record" /*)*/;
static final String RECORD_NONE_SELECTED = /*$$(*/ "Please select at least one record." /*)*/;
/** Currency service and controller */
static final String CURRENCY_1 = /*$$(*/
"No currency conversion found from '%s' to '%s' for date %s" /*)*/;
static final String CURRENCY_2 = /*$$(*/
"The currency exchange rate from '%s' to '%s' for date %s must be different from zero." /*)*/;
static final String CURRENCY_3 = /*$$(*/
"WARNING : please close the current conversion period to create a new one." /*)*/;
static final String CURRENCY_4 = /*$$(*/
"The end date has to be greater than or equal to the start date." /*)*/;
static final String CURRENCY_5 = /*$$(*/
"Both currencies must be saved before currency rate apply" /*)*/;
static final String CURRENCY_6 = /*$$(*/ "Currency conversion webservice not working" /*)*/;
static final String CURRENCY_7 = /*$$(*/
"No currency conversion rate found for the date %s to %s. Please change the conversion date" /*)*/;
static final String COMPANY_CURRENCY = /*$$(*/
"%s : Please, configure a currency for the company %s" /*)*/;
/** Unit conversion service */
static final String UNIT_CONVERSION_1 = /*$$(*/
"Please configure unit conversion from '%s' to '%s'." /*)*/;
static final String UNIT_CONVERSION_2 = /*$$(*/ "Please configure units." /*)*/;
static final String CURRENCY_CONVERSION_1 = /*$$(*/
"WARNING : Please close the current conversion period before creating new one" /*)*/;
static final String CURRENCY_CONVERSION_2 = /*$$(*/
"WARNING : To Date must be after or equals to From Date" /*)*/;
/** Account management service */
public static final String ACCOUNT_MANAGEMENT_2 = /*$$(*/ "No tax found for product %s" /*)*/;
public static final String ACCOUNT_MANAGEMENT_3 = /*$$(*/
"Tax configuration is missing for Product: %s (company: %s)" /*)*/;
/** Period service */
public static final String PERIOD_1 = /*$$(*/
"No period found or it has been closed for the company %s for the date %s" /*)*/;
public static final String PERIOD_2 = /*$$(*/ "Period closed" /*)*/;
public static final String PERIOD_3 = /*$$(*/ "Too much iterations." /*)*/;
static final String PAY_PERIOD_CLOSED = /*$$(*/ "Warning : the pay period %s is closed." /*)*/;
/** Abstract batch */
String ABSTRACT_BATCH_1 = /*$$(*/ "This batch is not runnable!" /*)*/;
String ABSTRACT_BATCH_2 = /*$$(*/ "Nested batch execution!" /*)*/;
String ABSTRACT_BATCH_REPORT = /*$$(*/ "Batch report:" /*)*/;
String ABSTRACT_BATCH_DONE_SINGULAR = /*$$(*/ "%d record processed successfully," /*)*/;
String ABSTRACT_BATCH_DONE_PLURAL = /*$$(*/ "%d records processed successfully," /*)*/;
String ABSTRACT_BATCH_ANOMALY_SINGULAR = /*$$(*/ "%d anomaly." /*)*/;
String ABSTRACT_BATCH_ANOMALY_PLURAL = /*$$(*/ "%d anomalies." /*)*/;
/** Indicator generator grouping service */
public static final String INDICATOR_GENERATOR_GROUPING_1 = /*$$(*/
"Error : no export path has been set" /*)*/;
public static final String INDICATOR_GENERATOR_GROUPING_2 = /*$$(*/
"Error : no code has been set" /*)*/;
public static final String INDICATOR_GENERATOR_GROUPING_3 = /*$$(*/
"Error while creating the file" /*)*/;
public static final String INDICATOR_GENERATOR_GROUPING_4 = /*$$(*/ "Result exported" /*)*/;
/** Indicator generator service */
public static final String INDICATOR_GENERATOR_1 = /*$$(*/
"Error : a request has to be set for the indicatior generator %s" /*)*/;
public static final String INDICATOR_GENERATOR_2 = /*$$(*/
"Error : incorrect request for the indicatior generator %s" /*)*/;
public static final String INDICATOR_GENERATOR_3 = /*$$(*/ "Request performed" /*)*/;
/** Alarm engine batch service */
public static final String ALARM_ENGINE_BATCH_1 = /*$$(*/ "Alarm Engine %s" /*)*/;
public static final String ALARM_ENGINE_BATCH_2 = /*$$(*/ "Alarms report :" /*)*/;
public static final String ALARM_ENGINE_BATCH_3 = /*$$(*/ "* %s object(s) into alarm" /*)*/;
public static final String ALARM_ENGINE_BATCH_4 = /*$$(*/ "* %s anomaly(ies)" /*)*/;
public static final String ALARM_ENGINE_BATCH_5 = /*$$(*/ "Alarm batch" /*)*/;
/** Base batch service */
public static final String BASE_BATCH_1 = /*$$(*/ "Unknown action %s for the %s treatment" /*)*/;
public static final String BASE_BATCH_2 = /*$$(*/ "Batch %s unknown" /*)*/;
/** Product service */
public static final String PRODUCT_NO_SEQUENCE = /*$$(*/
"There is no configured sequence for product" /*)*/;
/** Importer */
public static final String IMPORTER_1 = /*$$(*/ "Error : Mapping file is unreachable." /*)*/;
public static final String IMPORTER_2 = /*$$(*/ "Error : Data file is unreachable." /*)*/;
public static final String IMPORTER_3 = /*$$(*/ "Error : Mapping file is not found." /*)*/;
/** Importer Listener */
public static final String IMPORTER_LISTERNER_1 = /*$$(*/ "Total :" /*)*/;
public static final String IMPORTER_LISTERNER_2 = /*$$(*/ "- Succeeded :" /*)*/;
public static final String IMPORTER_LISTERNER_3 = /*$$(*/ "Generated anomalies :" /*)*/;
public static final String IMPORTER_LISTERNER_4 = /*$$(*/
"The line cannot be imported (import : %s)" /*)*/;
public static final String IMPORTER_LISTERNER_5 = /*$$(*/ "- Not null :" /*)*/;
/** Template message service base impl */
public static final String TEMPLATE_MESSAGE_BASE_1 = /*$$(*/
"%s : Path to Birt template is incorrect" /*)*/;
public static final String TEMPLATE_MESSAGE_BASE_2 = /*$$(*/
"Unable to generate Birt report file" /*)*/;
/** Querie Service and controller */
public static final String QUERIE_1 = /*$$(*/
"Error : There is no query set for the querie %s" /*)*/;
public static final String QUERIE_2 = /*$$(*/ "Error : Incorrect query for the querie %s" /*)*/;
public static final String QUERIE_3 = /*$$(*/ "Valid query." /*)*/;
/** Tax service */
public static final String TAX_1 = /*$$(*/ "Please enter a tax version for the tax %s" /*)*/;
public static final String TAX_2 = /*$$(*/ "Tax is missing" /*)*/;
/** Template rule service */
public static final String TEMPLATE_RULE_1 = /*$$(*/ "Bean is not an instance of" /*)*/;
/** Sequence service */
public static final String SEQUENCE_NOT_SAVED_RECORD = /*$$(*/
"Can't generate draft sequence number on an unsaved record." /*)*/;
/** Address controller */
public static final String ADDRESS_1 = /*$$(*/ "OK" /*)*/;
public static final String ADDRESS_2 = /*$$(*/
"Service unavailable, please contact a administrator" /*)*/;
public static final String ADDRESS_3 = /*$$(*/
"There is no matching address in the QAS base" /*)*/;
public static final String ADDRESS_4 = /*$$(*/ "NA" /*)*/;
public static final String ADDRESS_5 = /*$$(*/ "<B>%s</B> not found" /*)*/;
public static final String ADDRESS_6 = /*$$(*/
"Feature currently not available with Open Street Maps." /*)*/;
public static final String ADDRESS_7 = /*$$(*/
"Current user's active company address is not set" /*)*/;
public static final String ADDRESS_8 = /*$$(*/
"You can select only one default invoicing address." /*)*/;
public static final String ADDRESS_9 = /*$$(*/
"You can select only one default delivery address." /*)*/;
public static final String ADDRESS_CANNOT_BE_NULL = "Address cannot be null.";
/** Bank details controller */
public static final String BANK_DETAILS_1 = /*$$(*/
"The entered IBAN code is not valid . <br> Either the code doesn't respect the norm, or the format you have entered is not correct. It has to be without any blank space, as the following : <br> FR0000000000000000000000000" /*)*/;
public static final String BANK_DETAILS_2 = /*$$(*/
"At least one iban code you have entered for this partner is not valid. Here is the list of invalid codes : %s" /*)*/;
/** General controller */
public static final String GENERAL_1 = /*$$(*/ "No duplicate records found" /*)*/;
public static final String GENERAL_2 = /*$$(*/ "Duplicate records" /*)*/;
public static final String GENERAL_3 = /*$$(*/
"Please select key fields to check duplicate" /*)*/;
public static final String GENERAL_4 = /*$$(*/
"Attachment directory OR Application source does not exist" /*)*/;
public static final String GENERAL_5 = /*$$(*/ "Export Object" /*)*/;
public static final String GENERAL_6 = /*$$(*/ "Connection successful" /*)*/;
public static final String GENERAL_7 = /*$$(*/ "Error in Connection" /*)*/;
public static final String GENERAL_8 = /*$$(*/
"Duplicate finder field '%s' is not found inside model '%s'" /*)*/;
public static final String GENERAL_9 = /*$$(*/
"Invalid duplicate finder field '%s'. Field type ManyToMany or OneToMany is not supported for duplicate check" /*)*/;
public static final String GENERAL_10 = /*$$(*/ "No duplicate finder field configured." /*)*/;
public static final String GENERAL_11 = /*$$(*/ "Please select original object." /*)*/;
/** Messsage controller */
public static final String MESSAGE_1 = /*$$(*/
"Error in print. Please check report configuration and print setting." /*)*/;
public static final String MESSAGE_2 = /*$$(*/ "Please select the Message(s) to print." /*)*/;
/** Partner controller */
public static final String PARTNER_1 = /*$$(*/ "There is no sequence set for the partners" /*)*/;
public static final String PARTNER_2 = /*$$(*/
"%s SIRET Number required. Please configure SIRET Number for partner %s" /*)*/;
public static final String PARTNER_3 = /*$$(*/
"Cant convert into an individual partner from scratch." /*)*/;
public static final String PARTNER_NOT_FOUND = /*$$(*/ "Partner not found" /*)*/;
public static final String PARTNER_EMAIL_EXIST = /*$$(*/
"Email address already linked with another partner" /*)*/;
/** Product controller */
public static final String PRODUCT_1 = /*$$(*/ "Variants generated" /*)*/;
public static final String PRODUCT_2 = /*$$(*/ "Prices updated" /*)*/;
static final String PRODUCT_NO_ACTIVE_COMPANY = /*$$(*/
"No active company for this user, please define an active company." /*)*/;
/** Calendar */
static final String CALENDAR_NOT_VALID = /*$$(*/ "Calendar configuration not valid" /*)*/;
static final String IMPORT_CALENDAR = /*$$(*/ "Import calendar" /*)*/;
static final String CALENDAR_NO_EVENTS_FOR_SYNC_ERROR = /*$$(*/
"Calendars are empty, there is no event to synchronize." /*)*/;
/** Price list */
String PRICE_LIST_DATE_WRONG_ORDER = /*$$(*/ "The end date is before the begin date." /*)*/;
String PARTNER_PRICE_LIST_DATE_INCONSISTENT = /*$$(*/
"The price list %s will still be active when the price list %s will become active." /*)*/;
/** Advanced export */
static final String ADVANCED_EXPORT_1 = /*$$(*/ "Please select fields for export." /*)*/;
static final String ADVANCED_EXPORT_2 = /*$$(*/ "There is no records to export." /*)*/;
static final String ADVANCED_EXPORT_3 = /*$$(*/
"Warning : Exported maximum export limit records." /*)*/;
static final String ADVANCED_EXPORT_4 = /*$$(*/
"Please select export object or export format." /*)*/;
/** Barcode Generator Service */
public static final String BARCODE_GENERATOR_1 = /*$$(*/
"Invalid serial number '%s' for '%s' barcode type.It must be of %d digits only." /*)*/;
public static final String BARCODE_GENERATOR_2 = /*$$(*/
"Invalid serial number '%s' for '%s' barcode type.It must be digits only with even number length." /*)*/;
public static final String BARCODE_GENERATOR_3 = /*$$(*/
"Invalid serial number '%s' for '%s' barcode type.It must be digits only" /*)*/;
public static final String BARCODE_GENERATOR_4 = /*$$(*/
"Invalid Serial Number '%s' for '%s' barcode type.Alphabets must be in uppercase only" /*)*/;
public static final String BARCODE_GENERATOR_5 = /*$$(*/
"Invalid Serial Number '%s' for '%s' barcode type.Its length limit must be greater than %d and less than %d" /*)*/;
public static final String BARCODE_GENERATOR_6 = /*$$(*/
"Invalid Serial Number '%s' for '%s' barcode type.It must be alphanumeric" /*)*/;
public static final String BARCODE_GENERATOR_7 = /*$$(*/
"Invalid Serial Number '%s' for '%s' barcode type.Its Length must be %d" /*)*/;
public static final String BARCODE_GENERATOR_8 = /*$$(*/
"Invalid Serial Number '%s' for '%s' barcode type.It must be only number or only alphabets" /*)*/;
public static final String BARCODE_GENERATOR_9 = /*$$(*/ "Barcode format not supported" /*)*/;
public static final String MAP_RESPONSE_ERROR = /*$$(*/ "Response error from map API: %s" /*)*/;;
public static final String MAP_GOOGLE_MAPS_API_KEY_MISSING = /*$$(*/
"Google Maps API key is missing in configuration." /*)*/;
/** Weekly planning service */
public static final String WEEKLY_PLANNING_1 = /*$$(*/ "Invalid times %s morning" /*)*/;
public static final String WEEKLY_PLANNING_2 = /*$$(*/
"Invalid times on %s between morning and afternoon" /*)*/;
public static final String WEEKLY_PLANNING_3 = /*$$(*/ "Invalid times %s afternoon" /*)*/;
public static final String WEEKLY_PLANNING_4 = /*$$(*/
"Some times are null and should not on %s" /*)*/;
/*
* User service
*/
String USER_CODE_ALREADY_EXISTS = /*$$(*/ "A user with this login already exists." /*)*/;
String USER_PATTERN_MISMATCH_ACCES_RESTRICTION = /*$$(*/
"Password must have at least 8 characters with at least three of these four types: lowercase, uppercase, digit, special." /*)*/;
String USER_PATTERN_MISMATCH_CUSTOM = /*$$(*/
"Password doesn't match with configured pattern." /*)*/;
/** Convert demo data file */
public static final String DUPLICATE_CSV_FILE_NAME_EXISTS = /*$$(*/
"Please remove duplicate csv file name from excel file." /*)*/;
public static final String CSV_FILE_NAME_NOT_EXISTS = /*$$(*/
"Please provide valid csv file name." /*)*/;
public static final String EXCEL_FILE_FORMAT_ERROR = /*$$(*/
"Improper format of excel file." /*)*/;
public static final String VALIDATE_FILE_TYPE = /*$$(*/ "Please import only excel file." /*)*/;
String TIMER_IS_NOT_STOPPED = /*$$(*/ "You can't start a timer that has already started" /*)*/;
String TIMER_IS_NOT_STARTED = /*$$(*/ "You can't stop a timer that hasn't been started" /*)*/;
/** write to CSV from excel sheet */
public static final String INVALID_HEADER = /*$$(*/ "Header is not valid." /*)*/;
/** Import demo data from excel */
public static final String MODULE = /*$$(*/ "Module" /*)*/;
public static final String MODULE_NOT_EXIST = /*$$(*/ "%s module does not exist." /*)*/;
public static final String DATA_FILE = /*$$(*/ "Data file" /*)*/;
public static final String CONFIGURATION_FILE = /*$$(*/ "Configuration file" /*)*/;
public static final String CONFIGURATION_FILE_NOT_EXIST = /*$$(*/
"%s configuration file is not exist." /*)*/;
public static final String ROW_NOT_EMPTY = /*$$(*/ "%s row must not be empty." /*)*/;
public static final String CELL_NOT_VALID = /*$$(*/ "%s cell is not valid." /*)*/;
public static final String IMPORT_COMPLETED_MESSAGE = /*$$(*/
"Import completed successfully. Please check the log for more details." /*)*/;
public static final String INVALID_DATA_FORMAT_ERROR = /*$$(*/
"Invalid data format. Please check log for more details." /*)*/;
/* ABC Analysis */
public static final String ABC_CLASSES_INVALID_STATE_FOR_REPORTING = /*$$(*/
"The analysis must be completed before the report can be printed." /*)*/;
public static final String ABC_CLASSES_INVALID_QTY_OR_WORTH = /*$$(*/
"The classes total quantity and total worth must equal 100%." /*)*/;
public static final String ABC_CLASSES_NEGATIVE_OR_NULL_QTY_OR_WORTH = /*$$(*/
"The worth and quantity value of each class must be greater than 0." /*)*/;
/** Advanced Import */
public static final String ADVANCED_IMPORT_NO_IMPORT_FILE = /*$$(*/
"Data file doesn't exist" /*)*/;
public static final String ADVANCED_IMPORT_FILE_FORMAT_INVALID = /*$$(*/
"Data file format is invalid" /*)*/;
public static final String ADVANCED_IMPORT_ATTACHMENT_FORMAT = /*$$(*/
"Attachments must be in zip format" /*)*/;
public static final String ADVANCED_IMPORT_1 = /*$$(*/
"Field(%s) doesn't exist for the object(%s)" /*)*/;
public static final String ADVANCED_IMPORT_2 = /*$$(*/
"Sub field(%s) doesn't exist of field(%s) for the object(%s)" /*)*/;
public static final String ADVANCED_IMPORT_3 = /*$$(*/
"Config exist in the file. Please check 'Config included in file'" /*)*/;
public static final String ADVANCED_IMPORT_4 = /*$$(*/
"Config doesn't exist in the file. Please uncheck 'Config included in file'" /*)*/;
public static final String ADVANCED_IMPORT_5 = /*$$(*/
"Sub field doesn't exist of field(%s) for the object(%s)" /*)*/;
public static final String ADVANCED_IMPORT_6 = /*$$(*/
"Search fields are missing for the object(%s)" /*)*/;
public static final String ADVANCED_IMPORT_TAB_ERR = /*$$(*/
"File tab is not matched inside file" /*)*/;
public static final String ADVANCED_IMPORT_NO_OBJECT = /*$$(*/
"Object is missing for tab configuration(%s)" /*)*/;
public static final String ADVANCED_IMPORT_NO_FIELDS = /*$$(*/
"There is no field for tab configuration(%s)" /*)*/;
public static final String ADVANCED_IMPORT_CHECK_LOG = /*$$(*/
"Check log file in tabs configuration" /*)*/;
public static final String ADVANCED_IMPORT_IMPORT_DATA = /*$$(*/
"Data imported successfully" /*)*/;
public static final String ADVANCED_IMPORT_LOG_1 = /*$$(*/ "Object is not matched" /*)*/;
public static final String ADVANCED_IMPORT_LOG_2 = /*$$(*/ "Missing import fields" /*)*/;
public static final String ADVANCED_IMPORT_LOG_3 = /*$$(*/ "Missing required fields" /*)*/;
public static final String ADVANCED_IMPORT_LOG_4 = /*$$(*/ "Missing sub fields for" /*)*/;
public static final String ADVANCED_IMPORT_LOG_5 = /*$$(*/ "Fields can't be ignore" /*)*/;
public static final String ADVANCED_IMPORT_LOG_6 = /*$$(*/
"Missing date format or expression" /*)*/;
public static final String ADVANCED_IMPORT_LOG_7 = /*$$(*/ "Invalid fields" /*)*/;
public static final String ADVANCED_IMPORT_LOG_8 = /*$$(*/
"Missing data for required fields" /*)*/;
public static final String ADVANCED_IMPORT_LOG_9 = /*$$(*/ "Invalid type of data" /*)*/;
public static final String ADVANCED_IMPORT_RESET = /*$$(*/
"Reset imported data successfully" /*)*/;
public static final String ADVANCED_IMPORT_NO_RESET = /*$$(*/ "No imported data to reset" /*)*/;
}

View File

@ -0,0 +1,140 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.ical;
import com.axelor.apps.base.db.CalendarConfiguration;
import com.axelor.apps.base.db.repo.CalendarConfigurationRepository;
import com.axelor.apps.tool.MetaActionTool;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.Role;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.GroupRepository;
import com.axelor.auth.db.repo.RoleRepository;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaAction;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.repo.MetaActionRepository;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.axelor.meta.schema.actions.ActionView;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class CalendarConfigurationService {
private static final String NAME = "ical-calendar-";
@Inject protected CalendarConfigurationRepository calendarConfigurationRepo;
@Transactional
public void createEntryMenu(CalendarConfiguration calendarConfiguration) {
String menuName =
NAME + calendarConfiguration.getName().toLowerCase() + "-" + calendarConfiguration.getId();
String subName = menuName.replaceAll("[-\\s]", ".");
String title = calendarConfiguration.getName();
User user = calendarConfiguration.getCalendarUser();
Group group = calendarConfiguration.getCalendarGroup();
MetaAction metaAction = this.createMetaAction("action." + subName, title);
MetaMenu metaMenu =
this.createMetaMenu(menuName, title, metaAction, calendarConfiguration.getParentMetaMenu());
Beans.get(MetaMenuRepository.class).save(metaMenu);
Role role = new Role();
role.setName("role." + subName);
role.addMenu(metaMenu);
Beans.get(RoleRepository.class).save(role);
user.addRole(role);
Beans.get(UserRepository.class).save(user);
if (group != null) {
group.addRole(role);
Beans.get(GroupRepository.class).save(group);
}
calendarConfiguration.setRole(role);
calendarConfiguration.setMetaAction(metaAction);
calendarConfigurationRepo.save(calendarConfiguration);
}
@Transactional
public void deleteEntryMenu(CalendarConfiguration calendarConfiguration) {
MetaAction metaAction = calendarConfiguration.getMetaAction();
Role role = calendarConfiguration.getRole();
Group group = calendarConfiguration.getCalendarGroup();
calendarConfiguration.setMetaAction(null);
calendarConfiguration.setRole(null);
calendarConfiguration.getCalendarUser().removeRole(role);
if (group != null) {
group.removeRole(role);
}
String menuName =
NAME + calendarConfiguration.getName().toLowerCase() + "-" + calendarConfiguration.getId();
MetaMenuRepository metaMenuRepository = Beans.get(MetaMenuRepository.class);
MetaMenu metaMenu = metaMenuRepository.findByName(menuName);
metaMenuRepository.remove(metaMenu);
MetaActionRepository metaActionRepository = Beans.get(MetaActionRepository.class);
metaActionRepository.remove(metaAction);
RoleRepository roleRepository = Beans.get(RoleRepository.class);
roleRepository.remove(role);
}
public MetaMenu createMetaMenu(
String name, String title, MetaAction metaAction, MetaMenu parentMenu) {
MetaMenu metaMenu = new MetaMenu();
metaMenu.setName(name);
metaMenu.setAction(metaAction);
metaMenu.setModule("axelor-base");
metaMenu.setTitle(title);
metaMenu.setParent(parentMenu);
return metaMenu;
}
public MetaAction createMetaAction(String name, String title) {
String module = "axelor-base";
String type = "action-view";
String expr =
String.format(
"eval: __repo__(CalendarConfiguration).all().filter('self.metaAction.name = :name').bind('name', '%s').fetchOne().calendarSet.collect{ it.id }",
name);
ActionView actionView =
ActionView.define(title)
.name(name)
.model("com.axelor.apps.base.db.ICalendarEvent")
.add("calendar", "calendar-event-all")
.add("grid", "calendar-event-grid")
.add("form", "calandar-event-form")
.domain("self.calendar.id in (:_calendarIdList)")
.context("_calendarIdList", expr)
.get();
return MetaActionTool.actionToMetaAction(actionView, name, type, module);
}
}

View File

@ -0,0 +1,44 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.ical;
import com.axelor.apps.base.db.ICalendar;
import com.axelor.apps.base.db.ICalendarEvent;
import com.axelor.apps.base.db.repo.ICalendarRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ICalendarEventFactory {
private static final Map<String, Supplier<ICalendarEvent>> map = new HashMap<>();
static {
map.put(ICalendarRepository.ICAL_ONLY, ICalendarEvent::new);
}
public static ICalendarEvent getNewIcalEvent(ICalendar calendar) {
Supplier<ICalendarEvent> supplier =
map.getOrDefault(calendar.getSynchronizationSelect(), ICalendarEvent::new);
return supplier.get();
}
public static void register(String selection, Supplier<ICalendarEvent> eventSupplier) {
map.put(selection, eventSupplier);
}
}

View File

@ -0,0 +1,36 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.ical;
@SuppressWarnings("serial")
public class ICalendarException extends Exception {
public ICalendarException() {}
public ICalendarException(String message, Throwable cause) {
super(message, cause);
}
public ICalendarException(String message) {
super(message);
}
public ICalendarException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,998 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.ical;
import com.axelor.apps.base.db.ICalendar;
import com.axelor.apps.base.db.ICalendarEvent;
import com.axelor.apps.base.db.ICalendarUser;
import com.axelor.apps.base.db.repo.ICalendarEventRepository;
import com.axelor.apps.base.db.repo.ICalendarRepository;
import com.axelor.apps.base.db.repo.ICalendarUserRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.message.db.EmailAddress;
import com.axelor.apps.message.db.repo.EmailAddressRepository;
import com.axelor.apps.message.service.MailAccountService;
import com.axelor.apps.tool.QueryBuilder;
import com.axelor.auth.db.User;
import com.axelor.common.StringUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.SecureRandom;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import net.fortuna.ical4j.connector.FailedOperationException;
import net.fortuna.ical4j.connector.ObjectStoreException;
import net.fortuna.ical4j.connector.dav.CalDavCalendarCollection;
import net.fortuna.ical4j.connector.dav.PathResolver;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ConstraintViolationException;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.parameter.Cn;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Clazz;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.Geo;
import net.fortuna.ical4j.model.property.LastModified;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.model.property.Summary;
import net.fortuna.ical4j.model.property.Transp;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Url;
import net.fortuna.ical4j.model.property.Version;
import net.fortuna.ical4j.model.property.XProperty;
import net.fortuna.ical4j.util.FixedUidGenerator;
import net.fortuna.ical4j.util.HostInfo;
import net.fortuna.ical4j.util.InetAddressHostInfo;
import net.fortuna.ical4j.util.SimpleHostInfo;
import net.fortuna.ical4j.util.UidGenerator;
import net.fortuna.ical4j.util.Uris;
import net.fortuna.ical4j.validate.ValidationException;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
/** Provides calendars utilities. */
public class ICalendarService {
static final String PRODUCT_ID = "-//Axelor//ADK Calendar 1.0//EN";
static final String X_WR_CALNAME = "X-WR-CALNAME";
protected static UidGenerator generator;
@Inject protected ICalendarUserRepository iCalendarUserRepository;
@Inject protected ICalendarEventRepository iEventRepo;
@Inject private MailAccountService mailAccountService;
public static class GenericPathResolver extends PathResolver {
private String principalPath;
private String userPath;
public String principalPath() {
return principalPath;
}
public void setPrincipalPath(String principalPath) {
this.principalPath = principalPath;
}
@Override
public String getPrincipalPath(String username) {
return principalPath + "/" + username + "/";
}
public String userPath() {
return userPath;
}
public void setUserPath(String userPath) {
this.userPath = userPath;
}
@Override
public String getUserPath(String username) {
return userPath + "/" + username;
}
}
/**
* Generate next {@link Uid} to be used with calendar event.
*
* @return an {@link Uid} instance
* @throws SocketException if unable to determine host name
*/
public static Uid nextUid() throws SocketException {
if (generator == null) {
HostInfo info = new SimpleHostInfo("localhost");
try {
info = new InetAddressHostInfo(InetAddress.getLocalHost());
} catch (Exception e) {
}
generator = new FixedUidGenerator(info, "" + new SecureRandom().nextInt(Integer.MAX_VALUE));
}
return generator.generateUid();
}
public static Calendar newCalendar() {
final Calendar cal = new Calendar();
cal.getProperties().add(new ProdId(PRODUCT_ID));
cal.getProperties().add(Version.VERSION_2_0);
cal.getProperties().add(CalScale.GREGORIAN);
return cal;
}
public void testConnect(ICalendar cal) throws MalformedURLException, ObjectStoreException {
PathResolver RESOLVER = getPathResolver(cal.getTypeSelect());
Protocol protocol = getProtocol(cal.getIsSslConnection());
URL url = new URL(protocol.getScheme(), cal.getUrl(), cal.getPort(), "");
ICalendarStore store = new ICalendarStore(url, RESOLVER);
try {
store.connect(cal.getLogin(), getCalendarDecryptPassword(cal.getPassword()));
} finally {
store.disconnect();
}
}
public Protocol getProtocol(boolean isSslConnection) {
if (isSslConnection) {
return Protocol.getProtocol("https");
} else {
return Protocol.getProtocol("http");
}
}
/**
* Load the calendar events from the given source.
*
* @param calendar the target {@link ICalendar}
* @param text the raw calendar text
* @throws ParserException
*/
@Transactional(rollbackOn = {Exception.class})
public void load(ICalendar calendar, String text) throws ParserException {
Preconditions.checkNotNull(calendar, "calendar can't be null");
Preconditions.checkNotNull(text, "calendar source can't be null");
final StringReader reader = new StringReader(text);
try {
load(calendar, reader);
} catch (IOException e) {
}
}
/**
* Load the calendar events from the given source file.
*
* @param calendar the target {@link ICalendar}
* @param file the input file
* @throws IOException
* @throws ParserException
*/
@Transactional(rollbackOn = {Exception.class})
public void load(ICalendar calendar, File file) throws IOException, ParserException {
Preconditions.checkNotNull(calendar, "calendar can't be null");
Preconditions.checkNotNull(file, "input file can't be null");
Preconditions.checkArgument(file.exists(), "no such file: " + file);
final Reader reader = new FileReader(file);
try {
load(calendar, reader);
} finally {
reader.close();
}
}
/**
* Load the calendar events from the given reader.
*
* @param calendar the target {@link ICalendar}
* @param reader the input source reader
* @throws IOException
* @throws ParserException
*/
@Transactional(rollbackOn = {Exception.class})
public void load(ICalendar calendar, Reader reader) throws IOException, ParserException {
Preconditions.checkNotNull(calendar, "calendar can't be null");
Preconditions.checkNotNull(reader, "reader can't be null");
final CalendarBuilder builder = new CalendarBuilder();
final Calendar cal = builder.build(reader);
if (calendar.getName() == null && cal.getProperty(X_WR_CALNAME) != null) {
calendar.setName(cal.getProperty(X_WR_CALNAME).getValue());
}
for (Object item : cal.getComponents(Component.VEVENT)) {
findOrCreateEvent((VEvent) item, calendar);
}
}
protected String getValue(Component component, String name) {
if (component.getProperty(name) != null) {
return component.getProperty(name).getValue();
}
return null;
}
@Transactional
protected ICalendarEvent findOrCreateEvent(VEvent vEvent, ICalendar calendar) {
String uid = vEvent.getUid().getValue();
DtStart dtStart = vEvent.getStartDate();
DtEnd dtEnd = vEvent.getEndDate();
ICalendarEvent event = iEventRepo.findByUid(uid);
if (event == null) {
event = ICalendarEventFactory.getNewIcalEvent(calendar);
event.setUid(uid);
event.setCalendar(calendar);
}
ZoneId zoneId = OffsetDateTime.now().getOffset();
if (dtStart.getDate() != null) {
if (dtStart.getTimeZone() != null) {
zoneId = dtStart.getTimeZone().toZoneId();
}
event.setStartDateTime(LocalDateTime.ofInstant(dtStart.getDate().toInstant(), zoneId));
}
if (dtEnd.getDate() != null) {
if (dtEnd.getTimeZone() != null) {
zoneId = dtEnd.getTimeZone().toZoneId();
}
event.setEndDateTime(LocalDateTime.ofInstant(dtEnd.getDate().toInstant(), zoneId));
}
event.setAllDay(!(dtStart.getDate() instanceof DateTime));
event.setSubject(getValue(vEvent, Property.SUMMARY));
event.setDescription(getValue(vEvent, Property.DESCRIPTION));
event.setLocation(getValue(vEvent, Property.LOCATION));
event.setGeo(getValue(vEvent, Property.GEO));
event.setUrl(getValue(vEvent, Property.URL));
event.setSubjectTeam(event.getSubject());
if (Clazz.PRIVATE.getValue().equals(getValue(vEvent, Property.CLASS))) {
event.setVisibilitySelect(ICalendarEventRepository.VISIBILITY_PRIVATE);
} else {
event.setVisibilitySelect(ICalendarEventRepository.VISIBILITY_PUBLIC);
}
if (Transp.TRANSPARENT.getValue().equals(getValue(vEvent, Property.TRANSP))) {
event.setDisponibilitySelect(ICalendarEventRepository.DISPONIBILITY_AVAILABLE);
} else {
event.setDisponibilitySelect(ICalendarEventRepository.DISPONIBILITY_BUSY);
}
if (event.getVisibilitySelect() == ICalendarEventRepository.VISIBILITY_PRIVATE) {
event.setSubjectTeam(I18n.get("Available"));
if (event.getDisponibilitySelect() == ICalendarEventRepository.DISPONIBILITY_BUSY) {
event.setSubjectTeam(I18n.get("Busy"));
}
}
ICalendarUser organizer = findOrCreateUser(vEvent.getOrganizer(), event);
if (organizer != null) {
event.setOrganizer(organizer);
iCalendarUserRepository.save(organizer);
}
for (Object item : vEvent.getProperties(Property.ATTENDEE)) {
ICalendarUser attendee = findOrCreateUser((Property) item, event);
if (attendee != null) {
event.addAttendee(attendee);
iCalendarUserRepository.save(attendee);
}
}
iEventRepo.save(event);
return event;
}
public ICalendarUser findOrCreateUser(User user) {
String email = null;
if (user.getPartner() != null
&& user.getPartner().getEmailAddress() != null
&& !Strings.isNullOrEmpty(user.getPartner().getEmailAddress().getAddress())) {
email = user.getPartner().getEmailAddress().getAddress();
} else if (!Strings.isNullOrEmpty(user.getEmail())) {
email = user.getEmail();
} else {
return null;
}
ICalendarUserRepository repo = Beans.get(ICalendarUserRepository.class);
ICalendarUser icalUser = null;
icalUser =
repo.all().filter("self.email = ?1 AND self.user.id = ?2", email, user.getId()).fetchOne();
if (icalUser == null) {
icalUser = repo.all().filter("self.user.id = ?1", user.getId()).fetchOne();
}
if (icalUser == null) {
icalUser = repo.all().filter("self.email = ?1", email).fetchOne();
}
if (icalUser == null) {
icalUser = new ICalendarUser();
icalUser.setEmail(email);
icalUser.setName(user.getFullName());
EmailAddress emailAddress = Beans.get(EmailAddressRepository.class).findByAddress(email);
if (emailAddress != null
&& emailAddress.getPartner() != null
&& emailAddress.getPartner().getUser() != null) {
icalUser.setUser(emailAddress.getPartner().getUser());
}
}
return icalUser;
}
protected ICalendarUser findOrCreateUser(Property source, ICalendarEvent event) {
URI addr = null;
if (source instanceof Organizer) {
addr = ((Organizer) source).getCalAddress();
}
if (source instanceof Attendee) {
addr = ((Attendee) source).getCalAddress();
}
if (addr == null) {
return null;
}
String email = mailto(addr.toString(), true);
ICalendarUserRepository repo = Beans.get(ICalendarUserRepository.class);
ICalendarUser user = null;
if (source instanceof Organizer) {
user = repo.all().filter("self.email = ?1", email).fetchOne();
} else {
user =
repo.all()
.filter("self.email = ?1 AND self.event.id = ?2", email, event.getId())
.fetchOne();
}
if (user == null) {
user = new ICalendarUser();
user.setEmail(email);
user.setName(email);
EmailAddress emailAddress = Beans.get(EmailAddressRepository.class).findByAddress(email);
if (emailAddress != null
&& emailAddress.getPartner() != null
&& emailAddress.getPartner().getUser() != null) {
user.setUser(emailAddress.getPartner().getUser());
}
}
if (source.getParameter(Parameter.CN) != null) {
user.setName(source.getParameter(Parameter.CN).getValue());
}
if (source.getParameter(Parameter.PARTSTAT) != null) {
String role = source.getParameter(Parameter.PARTSTAT).getValue();
if (role.equals("TENTATIVE")) {
user.setStatusSelect(ICalendarUserRepository.STATUS_MAYBE);
} else if (role.equals("ACCEPTED")) {
user.setStatusSelect(ICalendarUserRepository.STATUS_YES);
} else if (role.equals("DECLINED")) {
user.setStatusSelect(ICalendarUserRepository.STATUS_NO);
}
}
return user;
}
public <T extends Property> T updateUser(T target, ICalendarUser user) {
if (user == null || user.getEmail() == null) {
return null;
}
String email = mailto(user.getEmail(), false);
String name = user.getName();
if (target instanceof Organizer) {
((Organizer) target).setCalAddress(createUri(email));
}
if (target instanceof Attendee) {
((Attendee) target).setCalAddress(createUri(email));
}
if (name != null) {
target.getParameters().add(new Cn(name));
}
return target;
}
protected String mailto(final String mail, boolean strip) {
if (mail == null) {
return null;
}
String res = mail.trim();
if (strip) {
if (res.toLowerCase().startsWith("mailto:")) {
res = res.substring(7);
}
} else if (!res.toLowerCase().startsWith("mailto:")) {
res = "mailto:" + res;
}
return res;
}
protected Date toDate(LocalDateTime dt, boolean allDay) {
if (dt == null) return null;
if (allDay)
return new Date(java.util.Date.from(dt.toInstant(OffsetDateTime.now().getOffset())));
return new DateTime(java.util.Date.from(dt.toInstant(OffsetDateTime.now().getOffset())));
}
protected URI createUri(String uri) {
try {
return Uris.create(uri);
} catch (URISyntaxException e) {
throw Throwables.propagate(e);
}
}
protected VEvent createVEvent(ICalendarEvent event) throws SocketException, ParseException {
boolean allDay = event.getAllDay() == Boolean.TRUE;
Date start = toDate(event.getStartDateTime(), allDay);
Date end = toDate(event.getEndDateTime(), allDay);
VEvent vevent = new VEvent();
PropertyList<Property> items = vevent.getProperties();
items.add(new DtStart(start));
if (end != null) {
items.add(new DtEnd(end));
}
if (event.getSubject() != null) {
items.add(new Summary(event.getSubject()));
}
if (event.getDescription() != null) {
items.add(new Description(event.getDescription()));
}
if (event.getStatus() != null) {
items.add(new Status(event.getStatus()));
}
if (event.getLocation() != null) {
items.add(new Location(event.getLocation()));
}
if (event.getGeo() != null) {
items.add(new Geo(event.getGeo()));
}
if (event.getUid() == null) {
items.add(nextUid());
} else {
items.add(new Uid(event.getUid()));
}
if (event.getUrl() != null) {
items.add(new Url(createUri(event.getUrl())));
}
if (event.getUpdatedOn() != null) {
DateTime date =
new DateTime(
java.util.Date.from(
event.getUpdatedOn().toInstant(OffsetDateTime.now().getOffset())));
date.setUtc(true);
LastModified lastModified = new LastModified(date);
items.add(lastModified);
} else {
DateTime date =
new DateTime(
java.util.Date.from(
event.getCreatedOn().toInstant(OffsetDateTime.now().getOffset())));
date.setUtc(true);
LastModified lastModified = new LastModified(date);
items.add(lastModified);
}
Organizer organizer = updateUser(new Organizer(), event.getOrganizer());
if (organizer != null) {
items.add(organizer);
}
if (event.getAttendees() != null) {
for (ICalendarUser user : event.getAttendees()) {
Attendee attendee = updateUser(new Attendee(), user);
if (attendee != null) {
items.add(attendee);
}
}
}
return vevent;
}
/**
* Export the calendar to the given file.
*
* @param calendar the source {@link ICalendar}
* @param file the target file
* @throws IOException
* @throws ParseException
* @throws ValidationException
*/
public void export(ICalendar calendar, File file)
throws IOException, ParseException, ValidationException {
Preconditions.checkNotNull(calendar, "calendar can't be null");
Preconditions.checkNotNull(file, "input file can't be null");
final Writer writer = new FileWriter(file);
try {
export(calendar, writer);
} finally {
writer.close();
}
}
/**
* Export the calendar to the given output writer.
*
* @param calendar the source {@link ICalendar}
* @param writer the output writer
* @throws IOException
* @throws ParseException
* @throws ValidationException
*/
public void export(ICalendar calendar, Writer writer)
throws IOException, ParseException, ValidationException {
Preconditions.checkNotNull(calendar, "calendar can't be null");
Preconditions.checkNotNull(writer, "writer can't be null");
Preconditions.checkNotNull(getICalendarEvents(calendar), "can't export empty calendar");
Calendar cal = newCalendar();
cal.getProperties().add(new XProperty(X_WR_CALNAME, calendar.getName()));
for (ICalendarEvent item : getICalendarEvents(calendar)) {
VEvent event = createVEvent(item);
cal.getComponents().add(event);
}
CalendarOutputter outputter = new CalendarOutputter();
outputter.output(cal, writer);
}
public void sync(ICalendar calendar, boolean all, int weeks)
throws MalformedURLException, ICalendarException {
if (all || calendar.getLastSynchronizationDateT() == null) {
sync(calendar, null, null);
} else {
int nbOfWeeks = weeks <= 0 ? calendar.getSynchronizationDuration() : weeks;
LocalDateTime now = Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime();
sync(calendar, now.minusWeeks(nbOfWeeks), now.plusWeeks(nbOfWeeks));
}
}
@Transactional(rollbackOn = {Exception.class})
protected void sync(ICalendar calendar, LocalDateTime startDate, LocalDateTime endDate)
throws ICalendarException, MalformedURLException {
PathResolver RESOLVER = getPathResolver(calendar.getTypeSelect());
Protocol protocol = getProtocol(calendar.getIsSslConnection());
URL url = new URL(protocol.getScheme(), calendar.getUrl(), calendar.getPort(), "");
ICalendarStore store = new ICalendarStore(url, RESOLVER);
try {
String password = getCalendarDecryptPassword(calendar.getPassword());
if (calendar.getLogin() != null
&& calendar.getPassword() != null
&& store.connect(calendar.getLogin(), password)) {
List<CalDavCalendarCollection> colList = store.getCollections();
if (!colList.isEmpty()) {
calendar = doSync(calendar, colList.get(0), startDate, endDate);
calendar.setLastSynchronizationDateT(
Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime());
Beans.get(ICalendarRepository.class).save(calendar);
}
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CALENDAR_NOT_VALID));
}
} catch (Exception e) {
throw new ICalendarException(e);
} finally {
store.disconnect();
}
}
@Transactional(rollbackOn = {Exception.class})
protected ICalendar doSync(
ICalendar calendar,
CalDavCalendarCollection collection,
LocalDateTime startDate,
LocalDateTime endDate)
throws IOException, URISyntaxException, ParseException, ObjectStoreException,
ConstraintViolationException, DavException, ParserConfigurationException, ParserException,
AxelorException {
final boolean keepRemote = calendar.getKeepRemote() == Boolean.TRUE;
final Map<String, VEvent> modifiedRemoteEvents = new HashMap<>();
final List<ICalendarEvent> modifiedLocalEvents = getICalendarEvents(calendar);
final Set<String> allRemoteUids = new HashSet<>();
final Set<VEvent> updatedEvents = new HashSet<>();
List<VEvent> events = null;
Instant lastSynchro = null;
if (calendar.getLastSynchronizationDateT() != null) {
lastSynchro =
calendar.getLastSynchronizationDateT().toInstant(OffsetDateTime.now().getOffset());
} else {
lastSynchro =
Beans.get(AppBaseService.class)
.getTodayDateTime()
.toLocalDateTime()
.toInstant(OffsetDateTime.now().getOffset());
}
if (startDate == null || endDate == null) {
events = ICalendarStore.getModifiedEvents(collection, null, allRemoteUids);
} else {
events =
ICalendarStore.getModifiedEventsInRange(
collection, lastSynchro, allRemoteUids, startDate, endDate);
}
if (events == null || events.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CALENDAR_NO_EVENTS_FOR_SYNC_ERROR));
}
for (VEvent item : events) {
modifiedRemoteEvents.put(item.getUid().getValue(), item);
}
for (ICalendarEvent item : modifiedLocalEvents) {
VEvent source = createVEvent(item);
VEvent target = modifiedRemoteEvents.get(source.getUid().getValue());
// If uid is empty, the event is new
if (StringUtils.isBlank(item.getUid())) {
item.setUid(source.getUid().getValue());
Calendar cal = newCalendar();
cal.getComponents().add(source);
collection.addCalendar(cal);
allRemoteUids.add(item.getUid());
}
// else it has been modified
else {
// if target is null, then it hasn't been modified or it has been deleted
if (target == null) {
target = source;
} else {
updateEvent(source, target, keepRemote);
modifiedRemoteEvents.remove(target.getUid().getValue());
}
updatedEvents.add(target);
}
}
// Process remaining modified remote events, find and update or create a
// corresponding ICalendarEvent
for (Map.Entry<String, VEvent> entry : modifiedRemoteEvents.entrySet()) {
findOrCreateEvent(entry.getValue(), calendar);
}
// update remote events
for (VEvent item : updatedEvents) {
Calendar cal = newCalendar();
cal.getComponents().add(item);
collection.updateCalendar(cal);
}
// remove deleted remote events
removeDeletedEventsInRange(allRemoteUids, calendar, startDate, endDate);
return calendar;
}
@Transactional
protected void removeDeletedEventsInRange(
Set<String> allRemoteUids,
ICalendar calendar,
LocalDateTime startDate,
LocalDateTime endDate) {
QueryBuilder<ICalendarEvent> queryBuilder = QueryBuilder.of(ICalendarEvent.class);
queryBuilder.add("self.uid NOT in (:uids)").bind("uids", allRemoteUids);
queryBuilder.add("self.calendar = :calendar").bind("calendar", calendar);
queryBuilder.add("self.archived = :archived OR self.archived IS NULL").bind("archived", false);
if (startDate != null && endDate != null) {
queryBuilder
.add(
"self.startDateTime BETWEEN :start AND :end OR self.endDateTime BETWEEN :start AND :end")
.bind("start", startDate)
.bind("end", endDate);
}
ICalendarEventRepository repo = Beans.get(ICalendarEventRepository.class);
for (ICalendarEvent event : queryBuilder.build().fetch()) {
if (ICalendarRepository.ICAL_ONLY.equals(calendar.getSynchronizationSelect())) {
repo.remove(event);
} else {
event.setArchived(true);
}
}
}
private VEvent updateEvent(VEvent source, VEvent target, boolean keepRemote)
throws IOException, URISyntaxException, ParseException {
final String[] names = {
Property.UID,
Property.URL,
Property.SUMMARY,
Property.DESCRIPTION,
Property.DTSTART,
Property.DTEND,
Property.ORGANIZER,
Property.ATTENDEE
};
if (keepRemote) {
VEvent tmp = target;
target = source;
source = tmp;
} else {
if (source.getLastModified() != null && target.getLastModified() != null) {
ZoneId zoneId = OffsetDateTime.now().getOffset();
if (source.getLastModified().getTimeZone() != null) {
zoneId = source.getLastModified().getTimeZone().toZoneId();
}
LocalDateTime lastModifiedSource =
LocalDateTime.ofInstant(source.getLastModified().getDate().toInstant(), zoneId);
LocalDateTime lastModifiedTarget =
LocalDateTime.ofInstant(target.getLastModified().getDate().toInstant(), zoneId);
if (lastModifiedSource.isBefore(lastModifiedTarget)) {
VEvent tmp = target;
target = source;
source = tmp;
}
} else if (target.getLastModified() != null) {
VEvent tmp = target;
target = source;
source = tmp;
}
if (source != target) {
for (String name : names) {
Property s = source.getProperty(name);
Property t = target.getProperty(name);
if (s == null && t == null) {
continue;
}
if (t == null) {
t = s;
}
if (s == null) {
target.getProperties().remove(t);
} else {
t.setValue(s.getValue());
}
}
}
}
return target;
}
public PathResolver getPathResolver(int typeSelect) {
switch (typeSelect) {
case ICalendarRepository.ICAL_SERVER:
return PathResolver.ICAL_SERVER;
case ICalendarRepository.CALENDAR_SERVER:
return PathResolver.CALENDAR_SERVER;
case ICalendarRepository.GCAL:
return PathResolver.GCAL;
case ICalendarRepository.ZIMBRA:
return PathResolver.ZIMBRA;
case ICalendarRepository.KMS:
return PathResolver.KMS;
case ICalendarRepository.CGP:
return PathResolver.CGP;
case ICalendarRepository.CHANDLER:
return PathResolver.CHANDLER;
default:
return null;
}
}
public ICalendarEvent createEvent(
LocalDateTime fromDateTime,
LocalDateTime toDateTime,
User user,
String description,
int type,
String subject) {
ICalendarEvent event = new ICalendarEvent();
event.setSubject(subject);
event.setStartDateTime(fromDateTime);
event.setEndDateTime(toDateTime);
event.setTypeSelect(type);
event.setUser(user);
event.setCalendar(user.getiCalendar());
if (!Strings.isNullOrEmpty(description)) {
event.setDescription(description);
}
return event;
}
public net.fortuna.ical4j.model.Calendar removeCalendar(
CalDavCalendarCollection collection, String uid)
throws FailedOperationException, ObjectStoreException {
net.fortuna.ical4j.model.Calendar calendar = collection.getCalendar(uid);
DeleteMethod deleteMethod = new DeleteMethod(collection.getPath() + uid + ".ics");
try {
collection.getStore().getClient().execute(deleteMethod);
} catch (IOException e) {
throw new ObjectStoreException(e);
}
if (!deleteMethod.succeeded()) {
throw new FailedOperationException(deleteMethod.getStatusLine().toString());
}
return calendar;
}
public net.fortuna.ical4j.model.Calendar getCalendar(String uid, ICalendar calendar)
throws ICalendarException, MalformedURLException {
net.fortuna.ical4j.model.Calendar cal = null;
PathResolver RESOLVER = getPathResolver(calendar.getTypeSelect());
Protocol protocol = getProtocol(calendar.getIsSslConnection());
URL url = new URL(protocol.getScheme(), calendar.getUrl(), calendar.getPort(), "");
ICalendarStore store = new ICalendarStore(url, RESOLVER);
try {
if (store.connect(calendar.getLogin(), calendar.getPassword())) {
List<CalDavCalendarCollection> colList = store.getCollections();
if (!colList.isEmpty()) {
CalDavCalendarCollection collection = colList.get(0);
cal = collection.getCalendar(uid);
}
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CALENDAR_NOT_VALID));
}
} catch (Exception e) {
throw new ICalendarException(e);
} finally {
store.disconnect();
}
return cal;
}
public void removeEventFromIcal(ICalendarEvent event)
throws MalformedURLException, ICalendarException {
if (event.getCalendar() != null && !Strings.isNullOrEmpty(event.getUid())) {
ICalendar calendar = event.getCalendar();
PathResolver RESOLVER = getPathResolver(calendar.getTypeSelect());
Protocol protocol = getProtocol(calendar.getIsSslConnection());
URL url = new URL(protocol.getScheme(), calendar.getUrl(), calendar.getPort(), "");
ICalendarStore store = new ICalendarStore(url, RESOLVER);
try {
if (store.connect(
calendar.getLogin(), getCalendarDecryptPassword(calendar.getPassword()))) {
List<CalDavCalendarCollection> colList = store.getCollections();
if (!colList.isEmpty()) {
CalDavCalendarCollection collection = colList.get(0);
final Map<String, VEvent> remoteEvents = new HashMap<>();
for (VEvent item : ICalendarStore.getEvents(collection)) {
remoteEvents.put(item.getUid().getValue(), item);
}
VEvent target = remoteEvents.get(event.getUid());
if (target != null) removeCalendar(collection, target.getUid().getValue());
}
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CALENDAR_NOT_VALID));
}
} catch (Exception e) {
throw new ICalendarException(e);
} finally {
store.disconnect();
}
}
}
@Transactional
public void removeOldEvents(List<ICalendarEvent> oldEvents) {
for (ICalendarEvent event : oldEvents) {
iEventRepo.remove(event);
}
}
private List<ICalendarEvent> getICalendarEvents(ICalendar calendar) {
LocalDateTime lastSynchro = calendar.getLastSynchronizationDateT();
if (lastSynchro != null) {
return iEventRepo
.all()
.filter(
"COALESCE(self.archived, false) = false AND self.calendar = ?1 AND COALESCE(self.updatedOn, self.createdOn) > ?2",
calendar,
lastSynchro)
.fetch();
}
return iEventRepo
.all()
.filter("COALESCE(self.archived, false) = false AND self.calendar = ?1", calendar)
.fetch();
}
public String getCalendarEncryptPassword(String password) {
return mailAccountService.getEncryptPassword(password);
}
public String getCalendarDecryptPassword(String password) {
return mailAccountService.getDecryptPassword(password);
}
}

View File

@ -0,0 +1,162 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.ical;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import net.fortuna.ical4j.connector.ObjectNotFoundException;
import net.fortuna.ical4j.connector.ObjectStoreException;
import net.fortuna.ical4j.connector.dav.CalDavCalendarCollection;
import net.fortuna.ical4j.connector.dav.CalDavCalendarStore;
import net.fortuna.ical4j.connector.dav.PathResolver;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.component.CalendarComponent;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.util.CompatibilityHints;
import org.apache.jackrabbit.webdav.DavException;
/**
* This class delegates the {@link CalDavCalendarStore} and provides most common methods to deal
* with CalDAV store.
*/
public class ICalendarStore {
private CalDavCalendarStore deligateStore;
static {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_NOTES_COMPATIBILITY, true);
}
public ICalendarStore(URL url, PathResolver pathResolver) {
this.deligateStore = new CalDavCalendarStore(ICalendarService.PRODUCT_ID, url, pathResolver);
}
public boolean connect(String username, String password) throws ObjectStoreException {
if (deligateStore.isConnected()) {
return true;
}
return deligateStore.connect(username, password.toCharArray());
}
public boolean connect() {
if (deligateStore.isConnected()) {
return true;
}
try {
return deligateStore.connect();
} catch (ObjectStoreException e) {
}
return false;
}
public void disconnect() {
if (deligateStore.isConnected()) {
deligateStore.disconnect();
}
}
public CalDavCalendarCollection getCollection(String id)
throws ObjectStoreException, ObjectNotFoundException {
return deligateStore.getCollection(id);
}
public List<CalDavCalendarCollection> getCollections() throws ObjectStoreException {
try {
return deligateStore.getCollections();
} catch (ObjectNotFoundException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
public static List<VEvent> getEvents(CalDavCalendarCollection calendar) {
final List<VEvent> events = new ArrayList<>();
for (Calendar cal : calendar.getEvents()) {
for (Object item : cal.getComponents(Component.VEVENT)) {
VEvent event = (VEvent) item;
events.add(event);
}
}
return events;
}
public static List<VEvent> getModifiedEvents(
CalDavCalendarCollection calendar, Instant instant, Set<String> remoteUids) {
final List<VEvent> events = new ArrayList<>();
for (Calendar cal : calendar.getEvents()) {
cal.toString();
for (Object item : ((List<CalendarComponent>) cal.getComponents(Component.VEVENT))) {
VEvent event = (VEvent) item;
if (instant == null || event.getLastModified().getDate().toInstant().isAfter(instant)) {
events.add(event);
}
remoteUids.add(event.getUid().getValue());
}
}
return events;
}
public static List<VEvent> getModifiedEventsInRange(
CalDavCalendarCollection calendar,
Instant instant,
Set<String> remoteUids,
LocalDateTime startDate,
LocalDateTime endDate)
throws IOException, DavException, ParserConfigurationException, ParserException,
ParseException {
final List<VEvent> events = new ArrayList<>();
DateTime start = new DateTime(Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant()));
DateTime end = new DateTime(Date.from(endDate.atZone(ZoneId.systemDefault()).toInstant()));
start = new DateTime(start.toString() + "Z");
end = new DateTime(end.toString() + "Z");
for (Calendar cal : calendar.getEventsForTimePeriod(start, end)) {
cal.toString();
for (Object item : ((List<CalendarComponent>) cal.getComponents(Component.VEVENT))) {
VEvent event = (VEvent) item;
if (event.getLastModified().getDate().toInstant().isAfter(instant)) {
events.add(event);
}
remoteUids.add(event.getUid().getValue());
}
}
return events;
}
public CalDavCalendarStore getDelegateStore() {
return deligateStore;
}
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.job;
import com.axelor.apps.base.service.administration.AbstractBatchService;
import com.axelor.db.JPA;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaSchedule;
import com.axelor.meta.db.repo.MetaScheduleRepository;
import com.axelor.rpc.Context;
import com.axelor.script.GroovyScriptHelper;
import com.axelor.script.ScriptHelper;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
public class BatchJob extends ThreadedJob {
@Override
public void executeInThread(JobExecutionContext context) {
JobDetail jobDetail = context.getJobDetail();
MetaSchedule metaSchedule =
Beans.get(MetaScheduleRepository.class).findByName(jobDetail.getKey().getName());
String batchServiceClassName = metaSchedule.getBatchServiceSelect();
Class<? extends AbstractBatchService> batchServiceClass;
try {
batchServiceClass =
Class.forName(batchServiceClassName).asSubclass(AbstractBatchService.class);
} catch (ClassNotFoundException e) {
throw new UncheckedJobExecutionException(e);
}
AbstractBatchService batchService = Beans.get(batchServiceClass);
String batchCode = metaSchedule.getBatchCode();
Model batchModel = batchService.findModelByCode(batchCode);
if (batchModel == null) {
String msg =
String.format("Batch %s not found with service %s", batchCode, batchServiceClassName);
throw new UncheckedJobExecutionException(msg);
}
// Apply job's parameters to the batch.
Map<String, Object> originalProperties =
applyBeanPropertiesWithScriptHelper(batchModel, jobDetail.getJobDataMap());
try {
batchService.run(batchModel);
} catch (Exception e) {
throw new UncheckedJobExecutionException(e);
} finally {
if (!JPA.em().contains(batchModel)) {
batchModel = batchService.findModelByCode(batchCode);
}
// Restore original values on the batch.
applyBeanProperties(batchModel, originalProperties);
}
}
private Map<String, Object> applyBeanPropertiesWithScriptHelper(
Object bean, Map<String, Object> properties) {
Context scriptContext = new Context(Mapper.toMap(bean), bean.getClass());
ScriptHelper scriptHelper = new GroovyScriptHelper(scriptContext);
return applyBeanProperties(bean, properties, value -> scriptHelper.eval(value.toString()));
}
private Map<String, Object> applyBeanProperties(Object bean, Map<String, Object> properties) {
return applyBeanProperties(bean, properties, value -> value);
}
private Map<String, Object> applyBeanProperties(
Object bean, Map<String, Object> properties, Function<Object, Object> evalFunc) {
Map<String, Object> originalProperties = new HashMap<>();
JPA.runInTransaction(
() -> {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
String key = entry.getKey();
if (PropertyUtils.isWriteable(bean, key)) {
try {
originalProperties.put(key, BeanUtils.getProperty(bean, key));
Object value = evalFunc.apply(entry.getValue());
BeanUtils.setProperty(bean, key, value);
} catch (IllegalAccessException
| InvocationTargetException
| NoSuchMethodException e) {
throw new UncheckedJobExecutionException(e);
}
}
}
});
return originalProperties;
}
}

View File

@ -0,0 +1,39 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.job;
import com.axelor.apps.base.service.CurrencyConversionService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class CurrencyConversionJob implements Job {
@Inject private CurrencyConversionService currencyConversionService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
currencyConversionService.updateCurrencyConverion();
} catch (AxelorException e) {
throw new JobExecutionException(e);
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.job;
import com.axelor.apps.base.db.repo.MailBatchRepository;
import com.axelor.apps.base.service.batch.MailBatchService;
import com.axelor.inject.Beans;
import org.quartz.JobExecutionContext;
public class MailJob extends ThreadedJob {
@Override
public void executeInThread(JobExecutionContext context) {
try {
Beans.get(MailBatchService.class).run(MailBatchRepository.CODE_BATCH_EMAIL_ALL_TIME_SHEET);
} catch (Exception e) {
throw new UncheckedJobExecutionException(e);
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.module;
import com.axelor.app.AxelorModule;
import com.axelor.apps.account.db.repo.TaxRepository;
import com.axelor.apps.base.db.PartnerAddress;
import com.axelor.apps.base.db.repo.ABCAnalysisBaseRepository;
import com.axelor.apps.base.db.repo.ABCAnalysisRepository;
import com.axelor.apps.base.db.repo.AddressBaseRepository;
import com.axelor.apps.base.db.repo.AddressRepository;
import com.axelor.apps.base.db.repo.AlarmEngineBatchBaseRepository;
import com.axelor.apps.base.db.repo.AlarmEngineBatchRepository;
import com.axelor.apps.base.db.repo.BankAddressBaseRepository;
import com.axelor.apps.base.db.repo.BankAddressRepository;
import com.axelor.apps.base.db.repo.BankBaseRepository;
import com.axelor.apps.base.db.repo.BankRepository;
import com.axelor.apps.base.db.repo.BaseBatchBaseRepository;
import com.axelor.apps.base.db.repo.BaseBatchRepository;
import com.axelor.apps.base.db.repo.DurationBaseRepository;
import com.axelor.apps.base.db.repo.DurationRepository;
import com.axelor.apps.base.db.repo.ICalendarEventManagementRepository;
import com.axelor.apps.base.db.repo.ICalendarEventRepository;
import com.axelor.apps.base.db.repo.MailBatchBaseRepository;
import com.axelor.apps.base.db.repo.MailBatchRepository;
import com.axelor.apps.base.db.repo.MailingListMessageBaseRepository;
import com.axelor.apps.base.db.repo.MailingListMessageRepository;
import com.axelor.apps.base.db.repo.PartnerAddressRepository;
import com.axelor.apps.base.db.repo.PartnerBaseRepository;
import com.axelor.apps.base.db.repo.PartnerRepository;
import com.axelor.apps.base.db.repo.ProductBaseRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.SequenceBaseRepository;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.db.repo.TaxBaseRepository;
import com.axelor.apps.base.db.repo.TeamTaskBaseRepository;
import com.axelor.apps.base.db.repo.UserBaseRepository;
import com.axelor.apps.base.db.repo.YearBaseRepository;
import com.axelor.apps.base.db.repo.YearRepository;
import com.axelor.apps.base.service.ABCAnalysisService;
import com.axelor.apps.base.service.ABCAnalysisServiceImpl;
import com.axelor.apps.base.service.AddressService;
import com.axelor.apps.base.service.AddressServiceImpl;
import com.axelor.apps.base.service.BankDetailsService;
import com.axelor.apps.base.service.BankDetailsServiceImpl;
import com.axelor.apps.base.service.BankService;
import com.axelor.apps.base.service.BankServiceImpl;
import com.axelor.apps.base.service.BarcodeGeneratorService;
import com.axelor.apps.base.service.BarcodeGeneratorServiceImpl;
import com.axelor.apps.base.service.CompanyService;
import com.axelor.apps.base.service.CompanyServiceImpl;
import com.axelor.apps.base.service.DurationService;
import com.axelor.apps.base.service.DurationServiceImpl;
import com.axelor.apps.base.service.FrequencyService;
import com.axelor.apps.base.service.FrequencyServiceImpl;
import com.axelor.apps.base.service.MailServiceBaseImpl;
import com.axelor.apps.base.service.MapRestService;
import com.axelor.apps.base.service.MapRestServiceImpl;
import com.axelor.apps.base.service.PartnerPriceListService;
import com.axelor.apps.base.service.PartnerPriceListServiceImpl;
import com.axelor.apps.base.service.PartnerService;
import com.axelor.apps.base.service.PartnerServiceImpl;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.PeriodServiceImpl;
import com.axelor.apps.base.service.ProductMultipleQtyService;
import com.axelor.apps.base.service.ProductMultipleQtyServiceImpl;
import com.axelor.apps.base.service.ProductService;
import com.axelor.apps.base.service.ProductServiceImpl;
import com.axelor.apps.base.service.SophalService;
import com.axelor.apps.base.service.SophalServiceImpl;
import com.axelor.apps.base.service.TeamTaskService;
import com.axelor.apps.base.service.TeamTaskServiceImpl;
import com.axelor.apps.base.service.TradingNameService;
import com.axelor.apps.base.service.TradingNameServiceImpl;
import com.axelor.apps.base.service.YearService;
import com.axelor.apps.base.service.YearServiceImpl;
import com.axelor.apps.base.service.advanced.imports.AdvancedImportService;
import com.axelor.apps.base.service.advanced.imports.AdvancedImportServiceImpl;
import com.axelor.apps.base.service.advanced.imports.DataImportService;
import com.axelor.apps.base.service.advanced.imports.DataImportServiceImpl;
import com.axelor.apps.base.service.advanced.imports.FileFieldService;
import com.axelor.apps.base.service.advanced.imports.FileFieldServiceImpl;
import com.axelor.apps.base.service.advanced.imports.FileTabService;
import com.axelor.apps.base.service.advanced.imports.FileTabServiceImpl;
import com.axelor.apps.base.service.advancedExport.AdvancedExportService;
import com.axelor.apps.base.service.advancedExport.AdvancedExportServiceImpl;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.base.service.app.AppBaseServiceImpl;
import com.axelor.apps.base.service.app.AppService;
import com.axelor.apps.base.service.app.AppServiceImpl;
import com.axelor.apps.base.service.imports.ConvertDemoDataFileService;
import com.axelor.apps.base.service.imports.ConvertDemoDataFileServiceImpl;
import com.axelor.apps.base.service.imports.ImportCityService;
import com.axelor.apps.base.service.imports.ImportCityServiceImpl;
import com.axelor.apps.base.service.imports.ImportDemoDataService;
import com.axelor.apps.base.service.imports.ImportDemoDataServiceImpl;
import com.axelor.apps.base.service.message.MailAccountServiceBaseImpl;
import com.axelor.apps.base.service.message.MessageServiceBaseImpl;
import com.axelor.apps.base.service.message.TemplateMessageServiceBaseImpl;
import com.axelor.apps.base.service.tax.AccountManagementService;
import com.axelor.apps.base.service.tax.AccountManagementServiceImpl;
import com.axelor.apps.base.service.tax.FiscalPositionService;
import com.axelor.apps.base.service.tax.FiscalPositionServiceImpl;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.base.service.user.UserServiceImpl;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningServiceImp;
import com.axelor.apps.message.service.MailAccountServiceImpl;
import com.axelor.apps.message.service.MailServiceMessageImpl;
import com.axelor.apps.message.service.MessageServiceImpl;
import com.axelor.apps.message.service.TemplateMessageServiceImpl;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.base.service.ical.ICalendarEventService;
import com.axelor.base.service.ical.ICalendarEventServiceImpl;
import com.axelor.team.db.repo.TeamTaskRepository;
import com.axelor.apps.base.service.ConvertNumberToFrenchWordsService;
import com.axelor.apps.base.service.ConvertNumberToFrenchWordsServiceImpl;
public class BaseModule extends AxelorModule {
@Override
protected void configure() {
bind(ConvertNumberToFrenchWordsService.class).to(ConvertNumberToFrenchWordsServiceImpl.class);
bind(SophalService.class).to(SophalServiceImpl.class);
bind(AddressService.class).to(AddressServiceImpl.class);
bind(AdvancedExportService.class).to(AdvancedExportServiceImpl.class);
bind(UserService.class).to(UserServiceImpl.class);
bind(MessageServiceImpl.class).to(MessageServiceBaseImpl.class);
bind(MailAccountServiceImpl.class).to(MailAccountServiceBaseImpl.class);
bind(AccountManagementService.class).to(AccountManagementServiceImpl.class);
bind(FiscalPositionService.class).to(FiscalPositionServiceImpl.class);
bind(ProductService.class).to(ProductServiceImpl.class);
bind(TemplateMessageServiceImpl.class).to(TemplateMessageServiceBaseImpl.class);
bind(PartnerRepository.class).to(PartnerBaseRepository.class);
bind(DurationRepository.class).to(DurationBaseRepository.class);
bind(DurationService.class).to(DurationServiceImpl.class);
bind(AppBaseService.class).to(AppBaseServiceImpl.class);
bind(SequenceRepository.class).to(SequenceBaseRepository.class);
bind(ProductRepository.class).to(ProductBaseRepository.class);
bind(WeeklyPlanningService.class).to(WeeklyPlanningServiceImp.class);
bind(MailServiceMessageImpl.class).to(MailServiceBaseImpl.class);
bind(AddressRepository.class).to(AddressBaseRepository.class);
bind(YearRepository.class).to(YearBaseRepository.class);
bind(YearService.class).to(YearServiceImpl.class);
bind(AppServiceImpl.class).to(AppBaseServiceImpl.class);
bind(AppService.class).to(AppServiceImpl.class);
bind(BankService.class).to(BankServiceImpl.class);
bind(BankRepository.class).to(BankBaseRepository.class);
bind(CompanyService.class).to(CompanyServiceImpl.class);
bind(BankAddressRepository.class).to(BankAddressBaseRepository.class);
bind(UserRepository.class).to(UserBaseRepository.class);
bind(BankDetailsService.class).to(BankDetailsServiceImpl.class);
bind(ImportCityService.class).to(ImportCityServiceImpl.class);
bind(BaseBatchRepository.class).to(BaseBatchBaseRepository.class);
bind(MailBatchRepository.class).to(MailBatchBaseRepository.class);
bind(AlarmEngineBatchRepository.class).to(AlarmEngineBatchBaseRepository.class);
bind(TradingNameService.class).to(TradingNameServiceImpl.class);
bind(PartnerPriceListService.class).to(PartnerPriceListServiceImpl.class);
bind(ICalendarEventService.class).to(ICalendarEventServiceImpl.class);
bind(ICalendarEventRepository.class).to(ICalendarEventManagementRepository.class);
bind(ProductMultipleQtyService.class).to(ProductMultipleQtyServiceImpl.class);
bind(BarcodeGeneratorService.class).to(BarcodeGeneratorServiceImpl.class);
PartnerAddressRepository.modelPartnerFieldMap.put(PartnerAddress.class.getName(), "_parent");
bind(PeriodService.class).to(PeriodServiceImpl.class);
bind(ConvertDemoDataFileService.class).to(ConvertDemoDataFileServiceImpl.class);
bind(ImportDemoDataService.class).to(ImportDemoDataServiceImpl.class);
bind(MapRestService.class).to(MapRestServiceImpl.class);
bind(TaxRepository.class).to(TaxBaseRepository.class);
bind(TeamTaskRepository.class).to(TeamTaskBaseRepository.class);
bind(TeamTaskService.class).to(TeamTaskServiceImpl.class);
bind(FrequencyService.class).to(FrequencyServiceImpl.class);
bind(MailingListMessageRepository.class).to(MailingListMessageBaseRepository.class);
bind(ABCAnalysisService.class).to(ABCAnalysisServiceImpl.class);
bind(ABCAnalysisRepository.class).to(ABCAnalysisBaseRepository.class);
bind(AdvancedImportService.class).to(AdvancedImportServiceImpl.class);
bind(DataImportService.class).to(DataImportServiceImpl.class);
bind(FileTabService.class).to(FileTabServiceImpl.class);
bind(FileFieldService.class).to(FileFieldServiceImpl.class);
bind(PartnerService.class).to(PartnerServiceImpl.class);
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.report;
public interface IReport {
public static final String PRODUCT_CATALOG = "ProductCatalog_PGQL.rptdesign";
public static final String PRODUCT_SHEET = "ProductSheet.rptdesign";
public static final String PARTNER = "Partner.rptdesign";
public static final String PHONE_BOOK = "ContactPhoneBook.rptdesign";
public static final String COMPANY_PHONE_BOOK = "CompanyPhoneBook.rptdesign";
public static final String CLIENT_SITUATION = "ClientSituation.rptdesign";
public static final String MESSAGE_PDF = "MessagePDF.rptdesign";
public static final String ABC_ANALYSIS = "AbcAnalysis.rptdesign";
public static final String MEETING = "PVMeeting.rptdesign";
}

View File

@ -0,0 +1,95 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.report;
public interface ITranslation {
public static final String PRODUCT_SHEET_TITLE = /*$$(*/ "ProductSheet.title"; /*)*/
public static final String PRODUCT_SHEET_DESCRIPTION = /*$$(*/ "ProductSheet.description"; /*)*/
public static final String PRODUCT_SHEET_PRICE = /*$$(*/ "ProductSheet.price"; /*)*/
public static final String PRODUCT_SHEET_WARRANTY = /*$$(*/ "ProductSheet.warranty"; /*)*/
public static final String CLIENT_SITUATION_TITLE = /*$$(*/ "ClientSituation.title"; /*)*/
public static final String CLIENT_SITUATION_FOLLOWED_BY = /*$$(*/
"ClientSituation.followedBy"; /*)*/
public static final String CLIENT_SITUATION_TEAM = /*$$(*/ "ClientSituation.team"; /*)*/
public static final String CLIENT_SITUATION_ADDRESS = /*$$(*/ "ClientSituation.address"; /*)*/
public static final String CLIENT_SITUATION_FIXED_PHONE = /*$$(*/
"ClientSituation.fixedPhone"; /*)*/
public static final String CLIENT_SITUATION_EMAIL = /*$$(*/ "ClientSituation.email"; /*)*/
public static final String CLIENT_SITUATION_FAX = /*$$(*/ "ClientSituation.fax"; /*)*/
public static final String CLIENT_SITUATION_MOBILE = /*$$(*/ "ClientSituation.mobile"; /*)*/
public static final String CLIENT_SITUATION_WEBSITE = /*$$(*/ "ClientSituation.website"; /*)*/
public static final String CLIENT_SITUATION_NAME = /*$$(*/ "ClientSituation.name"; /*)*/
public static final String CLIENT_SITUATION_PHONE = /*$$(*/ "ClientSituation.phone"; /*)*/
public static final String CLIENT_SITUATION_FUNCTION = /*$$(*/ "ClientSituation.function"; /*)*/
public static final String CLIENT_SITUATION_CONTACTS = /*$$(*/ "ClientSituation.contacts"; /*)*/
public static final String CLIENT_SITUATION_ORDERS = /*$$(*/ "ClientSituation.orders"; /*)*/
public static final String CLIENT_SITUATION_DATE = /*$$(*/ "ClientSituation.date"; /*)*/
public static final String CLIENT_SITUATION_REFERENCE = /*$$(*/ "ClientSituation.reference"; /*)*/
public static final String CLIENT_SITUATION_CONTACT = /*$$(*/ "ClientSituation.contact"; /*)*/
public static final String CLIENT_SITUATION_TOTAL_HT = /*$$(*/ "ClientSituation.totalHT"; /*)*/
public static final String CLIENT_SITUATION_TOTAL_TAX = /*$$(*/ "ClientSituation.totalTax"; /*)*/
public static final String CLIENT_SITUATION_TOTAL_ATI = /*$$(*/ "ClientSituation.totalATI"; /*)*/
public static final String CLIENT_SITUATION_TO_INVOICE = /*$$(*/
"ClientSituation.toInvoice"; /*)*/
public static final String CLIENT_SITUATION_STATUS = /*$$(*/ "ClientSituation.status"; /*)*/
public static final String CLIENT_SITUATION_INVOICES = /*$$(*/ "ClientSituation.invoices"; /*)*/
public static final String CLIENT_SITUATION_COMMERCIAL_DATA = /*$$(*/
"ClientSituation.commercialData"; /*)*/
public static final String CLIENT_SITUATION_LAST_MEETING_DATE = /*$$(*/
"ClientSituation.lastMeetingDate"; /*)*/
public static final String CLIENT_SITUATION_WITH = /*$$(*/ "ClientSituation.with"; /*)*/
public static final String CLIENT_SITUATION_NEXT_MEETING_DATE = /*$$(*/
"ClientSituation.nextMeetingDate"; /*)*/
public static final String CLIENT_SITUATION_LAST_CALL = /*$$(*/ "ClientSituation.lastCall"; /*)*/
public static final String CLIENT_SITUATION_NEXT_OUTGOING_CALL = /*$$(*/
"ClientSituation.nextOutgoingCall"; /*)*/
public static final String CLIENT_SITUATION_LAST_ORDER = /*$$(*/
"ClientSituation.lastOrder"; /*)*/
public static final String CLIENT_SITUATION_AVG_ORDER_AMT = /*$$(*/
"ClientSituation.avgOrderAmt"; /*)*/
public static final String CLIENT_SITUATION_12_MONTH_TURNOVER = /*$$(*/
"ClientSituation.12MonthTurnover"; /*)*/
public static final String CLIENT_SITUATION_ORDER_FREQ = /*$$(*/
"ClientSituation.orderFreq"; /*)*/
public static final String CLIENT_SITUATION_NUM = /*$$(*/ "ClientSituation.num"; /*)*/
public static final String CLIENT_SITUATION_REF_DOC = /*$$(*/ "ClientSituation.refDoc"; /*)*/
public static final String CLIENT_SITUATION_CUST_REF = /*$$(*/ "ClientSituation.custRef"; /*)*/
public static final String CLIENT_SITUATION_TO_PAY = /*$$(*/ "ClientSituation.toPay"; /*)*/
public static final String CLIENT_SITUATION_DUE_DATE = /*$$(*/ "ClientSituation.dueDate"; /*)*/
public static final String CLIENT_SITUATION_FINANCIAL_DATA = /*$$(*/
"ClientSituation.financialData"; /*)*/
public static final String CLIENT_SITUATION_TOTAL_BALANCE = /*$$(*/
"ClientSituation.totalBalance"; /*)*/
public static final String CLIENT_SITUATION_DUE_BALANCE = /*$$(*/
"ClientSituation.dueBalance"; /*)*/
public static final String CLIENT_SITUATION_RECOVERABLE_BALANCE = /*$$(*/
"ClientSituation.recoverableBalance"; /*)*/
public static final String PRODUCT_CATALOG_SUMMARY = /*$$(*/ "ProductCatalog.summary"; /*)*/
public static final String PRODUCT_CATALOG_REFERENCE = /*$$(*/ "ProductCatalog.reference"; /*)*/
public static final String PRODUCT_CATALOG_DESCRIPTION = /*$$(*/
"ProductCatalog.description"; /*)*/
public static final String PRODUCT_CATALOG_PRICE = /*$$(*/ "ProductCatalog.price"; /*)*/
public static final String PHONE_BOOK_CONTACT_PHONE_BOOK = /*$$(*/
"Phonebook.contactPhonebook"; /*)*/
public static final String PHONE_BOOK_COMPANY_PHONE_BOOK = /*$$(*/
"Phonebook.companyPhonebook"; /*)*/
}

View File

@ -0,0 +1,37 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.ABCAnalysis;
import com.axelor.apps.base.db.ABCAnalysisClass;
import com.axelor.exception.AxelorException;
import java.util.List;
public interface ABCAnalysisService {
void reset(ABCAnalysis abcAnalysis);
void runAnalysis(ABCAnalysis abcAnalysis) throws AxelorException;
List<ABCAnalysisClass> initABCClasses();
void setSequence(ABCAnalysis abcAnalysis);
String printReport(ABCAnalysis abcAnalysis, String reportType) throws AxelorException;
void checkClasses(ABCAnalysis abcAnalysis) throws AxelorException;
}

View File

@ -0,0 +1,410 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import static com.axelor.apps.base.service.administration.AbstractBatch.FETCH_LIMIT;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.ABCAnalysis;
import com.axelor.apps.base.db.ABCAnalysisClass;
import com.axelor.apps.base.db.ABCAnalysisLine;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.base.db.repo.ABCAnalysisClassRepository;
import com.axelor.apps.base.db.repo.ABCAnalysisLineRepository;
import com.axelor.apps.base.db.repo.ABCAnalysisRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.report.IReport;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.apps.tool.StringTool;
import com.axelor.db.JPA;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class ABCAnalysisServiceImpl implements ABCAnalysisService {
protected ABCAnalysisLineRepository abcAnalysisLineRepository;
protected UnitConversionService unitConversionService;
protected SequenceService sequenceService;
protected ABCAnalysisRepository abcAnalysisRepository;
protected ProductRepository productRepository;
protected ABCAnalysisClassRepository abcAnalysisClassRepository;
private BigDecimal totalQty = BigDecimal.ZERO;
private BigDecimal totalWorth = BigDecimal.ZERO;
private BigDecimal cumulatedQty = BigDecimal.valueOf(0, 3);
private BigDecimal cumulatedWorth = BigDecimal.valueOf(0, 3);
private final String abcAnalysisSequenceCode = "abcAnalysis";
private List<ABCAnalysisClass> abcAnalysisClassList;
@Inject
public ABCAnalysisServiceImpl(
ABCAnalysisLineRepository abcAnalysisLineRepository,
UnitConversionService unitConversionService,
ABCAnalysisRepository abcAnalysisRepository,
ProductRepository productRepository,
ABCAnalysisClassRepository abcAnalysisClassRepository,
SequenceService sequenceService) {
this.abcAnalysisLineRepository = abcAnalysisLineRepository;
this.unitConversionService = unitConversionService;
this.abcAnalysisRepository = abcAnalysisRepository;
this.productRepository = productRepository;
this.abcAnalysisClassRepository = abcAnalysisClassRepository;
this.sequenceService = sequenceService;
}
@Override
public List<ABCAnalysisClass> initABCClasses() {
List<ABCAnalysisClass> abcAnalysisClassList = new ArrayList<>();
abcAnalysisClassList.add(
createAbcClass("A", 0, BigDecimal.valueOf(80), BigDecimal.valueOf(20)));
abcAnalysisClassList.add(
createAbcClass("B", 1, BigDecimal.valueOf(15), BigDecimal.valueOf(30)));
abcAnalysisClassList.add(createAbcClass("C", 2, BigDecimal.valueOf(5), BigDecimal.valueOf(50)));
return abcAnalysisClassList;
}
private ABCAnalysisClass createAbcClass(
String name, Integer sequence, BigDecimal worth, BigDecimal qty) {
ABCAnalysisClass abcAnalysisClass = new ABCAnalysisClass();
abcAnalysisClass.setName(name);
abcAnalysisClass.setSequence(sequence);
abcAnalysisClass.setWorth(worth);
abcAnalysisClass.setQty(qty);
return abcAnalysisClass;
}
@Override
@Transactional
public void reset(ABCAnalysis abcAnalysis) {
abcAnalysisLineRepository
.all()
.filter("self.abcAnalysis.id = :abcAnalysisId")
.bind("abcAnalysisId", abcAnalysis.getId())
.remove();
abcAnalysis.setStatusSelect(ABCAnalysisRepository.STATUS_DRAFT);
abcAnalysisRepository.save(abcAnalysis);
}
@Override
public void runAnalysis(ABCAnalysis abcAnalysis) throws AxelorException {
reset(abcAnalysis);
start(abcAnalysis);
getAbcAnalysisClassList(abcAnalysis);
createAllABCAnalysisLine(abcAnalysis);
doAnalysis(abcAnalysisRepository.find(abcAnalysis.getId()));
finish(abcAnalysisRepository.find(abcAnalysis.getId()));
}
@Transactional
protected void start(ABCAnalysis abcAnalysis) {
abcAnalysis.setStatusSelect(ABCAnalysisRepository.STATUS_ANALYZING);
abcAnalysisRepository.save(abcAnalysis);
}
private void getAbcAnalysisClassList(ABCAnalysis abcAnalysis) {
Query<ABCAnalysisClass> abcAnalysisClassQuery =
abcAnalysisClassRepository
.all()
.filter("self.abcAnalysis.id = :abcAnalysisId")
.bind("abcAnalysisId", abcAnalysis.getId())
.order("sequence");
this.abcAnalysisClassList = abcAnalysisClassQuery.fetch();
}
private Set<Product> getProductSet(ABCAnalysis abcAnalysis) {
Set<Product> productList = new HashSet<>();
String productCategoryQuery = getProductCategoryQuery();
String productFamilyQuery = getProductFamilyQuery();
if (!abcAnalysis.getProductSet().isEmpty()) {
productList.addAll(abcAnalysis.getProductSet());
}
if (!abcAnalysis.getProductCategorySet().isEmpty()) {
productList.addAll(
productRepository
.all()
.filter(
productCategoryQuery,
abcAnalysis.getProductCategorySet(),
ProductRepository.PRODUCT_TYPE_STORABLE)
.fetch());
}
if (!abcAnalysis.getProductFamilySet().isEmpty()) {
productList.addAll(
productRepository
.all()
.filter(
productFamilyQuery,
abcAnalysis.getProductFamilySet(),
ProductRepository.PRODUCT_TYPE_STORABLE)
.fetch());
}
return productList;
}
protected String getProductCategoryQuery() {
return "self.productCategory in (?1) AND self.productTypeSelect = ?2";
}
protected String getProductFamilyQuery() {
return "self.productFamily in (?1) AND self.productTypeSelect = ?2";
}
protected void createAllABCAnalysisLine(ABCAnalysis abcAnalysis) throws AxelorException {
int offset = 0;
List<Product> productList;
Query<Product> productQuery =
productRepository
.all()
.filter("self.id IN (" + StringTool.getIdListString(getProductSet(abcAnalysis)) + ")");
while (!(productList = productQuery.fetch(FETCH_LIMIT, offset)).isEmpty()) {
abcAnalysis = abcAnalysisRepository.find(abcAnalysis.getId());
offset += productList.size();
for (Product product : productList) {
product = productRepository.find(product.getId());
createABCAnalysisLineForEachProduct(abcAnalysis, product);
}
JPA.clear();
}
}
@Transactional(rollbackOn = {Exception.class})
protected void createABCAnalysisLineForEachProduct(ABCAnalysis abcAnalysis, Product product)
throws AxelorException {
Optional<ABCAnalysisLine> optionalAbcAnalysisLine = createABCAnalysisLine(abcAnalysis, product);
optionalAbcAnalysisLine.ifPresent(
abcAnalysisLine -> {
abcAnalysisLine = abcAnalysisLineRepository.find(abcAnalysisLine.getId());
if (abcAnalysisLine.getDecimalWorth().compareTo(BigDecimal.ZERO) == 0
&& abcAnalysisLine.getDecimalQty().compareTo(BigDecimal.ZERO) == 0) {
abcAnalysisLineRepository.remove(
abcAnalysisLineRepository.find(abcAnalysisLine.getId()));
}
});
}
@Transactional(rollbackOn = {Exception.class})
protected Optional<ABCAnalysisLine> createABCAnalysisLine(
ABCAnalysis abcAnalysis, Product product) throws AxelorException {
ABCAnalysisLine abcAnalysisLine = new ABCAnalysisLine();
abcAnalysisLine.setAbcAnalysis(abcAnalysis);
abcAnalysisLine.setProduct(product);
abcAnalysisLineRepository.save(abcAnalysisLine);
return Optional.of(abcAnalysisLine);
}
@Transactional
protected void setQtyWorth(
ABCAnalysisLine abcAnalysisLine, BigDecimal decimalQty, BigDecimal decimalWorth) {
abcAnalysisLine.setDecimalQty(decimalQty);
abcAnalysisLine.setDecimalWorth(decimalWorth);
abcAnalysisLineRepository.save(abcAnalysisLine);
}
protected void doAnalysis(ABCAnalysis abcAnalysis) {
List<ABCAnalysisLine> abcAnalysisLineList;
int offset = 0;
Query<ABCAnalysisLine> query =
abcAnalysisLineRepository
.all()
.filter("self.abcAnalysis.id = :abcAnalysisId")
.bind("abcAnalysisId", abcAnalysis.getId())
.order("-decimalWorth")
.order("id");
while (!(abcAnalysisLineList = query.fetch(FETCH_LIMIT, offset)).isEmpty()) {
offset += abcAnalysisLineList.size();
abcAnalysisLineList.forEach(this::analyzeLine);
JPA.clear();
}
}
@Transactional
protected void analyzeLine(ABCAnalysisLine abcAnalysisLine) {
computePercentage(abcAnalysisLine);
setABCAnalysisClass(abcAnalysisLine);
abcAnalysisLineRepository.save(abcAnalysisLine);
}
private void computePercentage(ABCAnalysisLine abcAnalysisLine) {
BigDecimal qty = BigDecimal.ZERO;
if (totalQty.compareTo(BigDecimal.ZERO) > 0) {
qty =
abcAnalysisLine
.getDecimalQty()
.multiply(BigDecimal.valueOf(100))
.divide(totalQty, 3, RoundingMode.HALF_EVEN);
}
BigDecimal worth = BigDecimal.ZERO;
if (totalWorth.compareTo(BigDecimal.ZERO) > 0) {
worth =
abcAnalysisLine
.getDecimalWorth()
.multiply(BigDecimal.valueOf(100))
.divide(totalWorth, 3, RoundingMode.HALF_EVEN);
}
incCumulatedQty(qty);
incCumulatedWorth(worth);
abcAnalysisLine.setQty(qty);
abcAnalysisLine.setCumulatedQty(cumulatedQty);
abcAnalysisLine.setWorth(worth);
abcAnalysisLine.setCumulatedWorth(cumulatedWorth);
}
protected void setABCAnalysisClass(ABCAnalysisLine abcAnalysisLine) {
BigDecimal maxQty = BigDecimal.ZERO;
BigDecimal maxWorth = BigDecimal.ZERO;
BigDecimal lineCumulatedQty =
abcAnalysisLine.getCumulatedQty().setScale(2, RoundingMode.HALF_EVEN);
BigDecimal lineCumulatedWorth =
abcAnalysisLine.getCumulatedWorth().setScale(2, RoundingMode.HALF_EVEN);
for (ABCAnalysisClass abcAnalysisClass : abcAnalysisClassList) {
maxQty = maxQty.add(abcAnalysisClass.getQty());
maxWorth = maxWorth.add(abcAnalysisClass.getWorth());
if (lineCumulatedQty.compareTo(maxQty) <= 0 && lineCumulatedWorth.compareTo(maxWorth) <= 0) {
abcAnalysisLine.setAbcAnalysisClass(
abcAnalysisClassRepository.find(abcAnalysisClass.getId()));
break;
}
}
}
protected void incTotalQty(BigDecimal totalQty) {
this.totalQty = this.totalQty.add(totalQty);
}
protected void incTotalWorth(BigDecimal totalWorth) {
this.totalWorth = this.totalWorth.add(totalWorth);
}
private void incCumulatedQty(BigDecimal cumulatedQty) {
this.cumulatedQty = this.cumulatedQty.add(cumulatedQty);
}
private void incCumulatedWorth(BigDecimal cumulatedWorth) {
this.cumulatedWorth = this.cumulatedWorth.add(cumulatedWorth);
}
@Transactional
protected void finish(ABCAnalysis abcAnalysis) {
abcAnalysis.setStatusSelect(ABCAnalysisRepository.STATUS_FINISHED);
abcAnalysisRepository.save(abcAnalysis);
}
@Override
public void setSequence(ABCAnalysis abcAnalysis) {
String abcAnalysisSequence = abcAnalysis.getAbcAnalysisSeq();
if (abcAnalysisSequence != null && !abcAnalysisSequence.isEmpty()) {
return;
}
Sequence sequence =
sequenceService.getSequence(abcAnalysisSequenceCode, abcAnalysis.getCompany());
if (sequence == null) {
return;
}
abcAnalysis.setAbcAnalysisSeq(sequenceService.getSequenceNumber(sequence));
}
@Override
public String printReport(ABCAnalysis abcAnalysis, String reportType) throws AxelorException {
if (abcAnalysis.getStatusSelect() != ABCAnalysisRepository.STATUS_FINISHED) {
throw new AxelorException(
abcAnalysis,
TraceBackRepository.TYPE_FUNCTIONNAL,
I18n.get(IExceptionMessage.ABC_CLASSES_INVALID_STATE_FOR_REPORTING));
}
String name = I18n.get("ABC Analysis") + " - " + abcAnalysis.getAbcAnalysisSeq();
return ReportFactory.createReport(IReport.ABC_ANALYSIS, name)
.addParam("abcAnalysisId", abcAnalysis.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addFormat(reportType)
.toAttach(abcAnalysis)
.generate()
.getFileLink();
}
@Override
public void checkClasses(ABCAnalysis abcAnalysis) throws AxelorException {
List<ABCAnalysisClass> abcAnalysisClassList = abcAnalysis.getAbcAnalysisClassList();
BigDecimal classQty, classWorth;
BigDecimal totalQty = BigDecimal.ZERO, totalWorth = BigDecimal.ZERO;
BigDecimal comparisonValue = new BigDecimal(100);
for (ABCAnalysisClass abcAnalysisClass : abcAnalysisClassList) {
classQty = abcAnalysisClass.getQty();
classWorth = abcAnalysisClass.getWorth();
if (classQty.compareTo(BigDecimal.ZERO) <= 0 || classWorth.compareTo(BigDecimal.ZERO) <= 0) {
throw new AxelorException(
abcAnalysis,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ABC_CLASSES_NEGATIVE_OR_NULL_QTY_OR_WORTH));
}
totalQty = totalQty.add(classQty);
totalWorth = totalWorth.add(classWorth);
}
if (totalQty.compareTo(comparisonValue) != 0 || totalWorth.compareTo(comparisonValue) != 0) {
throw new AxelorException(
abcAnalysis,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ABC_CLASSES_INVALID_QTY_OR_WORTH));
}
}
}

View File

@ -0,0 +1,104 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.Country;
import com.axelor.exception.AxelorException;
import com.axelor.meta.CallMethod;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import wslite.json.JSONException;
public interface AddressService {
public boolean check(String wsdlUrl);
public Map<String, Object> validate(String wsdlUrl, String search);
public com.qas.web_2005_02.Address select(String wsdlUrl, String moniker);
public int export(String path) throws IOException;
public Address createAddress(
String addressL2,
String addressL3,
String addressL4,
String addressL5,
String addressL6,
Country addressL7Country);
public Address getAddress(
String addressL2,
String addressL3,
String addressL4,
String addressL5,
String addressL6,
Country addressL7Country);
@CallMethod
public boolean checkAddressUsed(Long addressId);
/**
* Get or update latitude and longitude.
*
* @param address
* @return
* @throws JSONException
* @throws AxelorException
*/
Optional<Pair<BigDecimal, BigDecimal>> getOrUpdateLatLong(Address address)
throws AxelorException, JSONException;
/**
* Update latitude and longitude.
*
* @param address
* @throws AxelorException
* @throws JSONException
*/
Optional<Pair<BigDecimal, BigDecimal>> updateLatLong(Address address)
throws AxelorException, JSONException;
/**
* Reset latitude and longitude.
*
* @param address
*/
void resetLatLong(Address address);
public String computeFullName(Address address);
/**
* Used to fill the string field in invoice, sale/purchase order and stock move
*
* @param address
* @return the string field corresponding to the given address.
*/
String computeAddressStr(Address address);
/**
* Auto-completes some fields of the address thanks to the input zip.
*
* @param address
*/
public void autocompleteAddress(Address address);
}

View File

@ -0,0 +1,318 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.City;
import com.axelor.apps.base.db.Country;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PartnerAddress;
import com.axelor.apps.base.db.PickListEntry;
import com.axelor.apps.base.db.Street;
import com.axelor.apps.base.db.repo.AddressRepository;
import com.axelor.apps.base.db.repo.CityRepository;
import com.axelor.apps.base.db.repo.StreetRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.common.StringUtils;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import com.opencsv.CSVWriter;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wslite.json.JSONException;
@Singleton
public class AddressServiceImpl implements AddressService {
@Inject protected AddressRepository addressRepo;
@Inject protected com.axelor.apps.tool.address.AddressTool ads;
@Inject protected MapService mapService;
@Inject protected CityRepository cityRepository;
@Inject protected StreetRepository streetRepository;
protected static final Set<Function<Long, Boolean>> checkUsedFuncs = new LinkedHashSet<>();
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
static {
registerCheckUsedFunc(AddressServiceImpl::checkAddressUsedBase);
}
@Override
public boolean check(String wsdlUrl) {
return ads.doCanSearch(wsdlUrl);
}
@Override
public Map<String, Object> validate(String wsdlUrl, String search) {
return ads.doSearch(wsdlUrl, search);
}
@Override
public com.qas.web_2005_02.Address select(String wsdlUrl, String moniker) {
return ads.doGetAddress(wsdlUrl, moniker);
}
@Override
public int export(String path) throws IOException {
List<Address> addresses = addressRepo.all().filter("self.certifiedOk IS FALSE").fetch();
CSVWriter csv =
new CSVWriter(new java.io.FileWriter(path), "|".charAt(0), CSVWriter.NO_QUOTE_CHARACTER);
List<String> header = new ArrayList<>();
header.add("Id");
header.add("AddressL1");
header.add("AddressL2");
header.add("AddressL3");
header.add("AddressL4");
header.add("AddressL5");
header.add("AddressL6");
header.add("CodeINSEE");
csv.writeNext(header.toArray(new String[header.size()]));
List<String> items = new ArrayList<>();
for (Address a : addresses) {
items.add(a.getId() != null ? a.getId().toString() : "");
items.add(a.getAddressL2() != null ? a.getAddressL2() : "");
items.add(a.getAddressL3() != null ? a.getAddressL3() : "");
items.add(a.getAddressL4() != null ? a.getAddressL4() : "");
items.add(a.getAddressL5() != null ? a.getAddressL5() : "");
items.add(a.getAddressL6() != null ? a.getAddressL6() : "");
items.add(a.getInseeCode() != null ? a.getInseeCode() : "");
csv.writeNext(items.toArray(new String[items.size()]));
items.clear();
}
csv.close();
LOG.info("{} exported", path);
return addresses.size();
}
@Override
public Address createAddress(
String addressL2,
String addressL3,
String addressL4,
String addressL5,
String addressL6,
Country addressL7Country) {
Address address = new Address();
address.setAddressL2(addressL2);
address.setAddressL3(addressL3);
address.setAddressL4(addressL4);
address.setAddressL5(addressL5);
address.setAddressL6(addressL6);
address.setAddressL7Country(addressL7Country);
return address;
}
@Override
public Address getAddress(
String addressL2,
String addressL3,
String addressL4,
String addressL5,
String addressL6,
Country addressL7Country) {
return addressRepo
.all()
.filter(
"self.addressL2 = ?1 AND self.addressL3 = ?2 AND self.addressL4 = ?3 "
+ "AND self.addressL5 = ?4 AND self.addressL6 = ?5 AND self.addressL7Country = ?6",
addressL2,
addressL3,
addressL4,
addressL5,
addressL6,
addressL7Country)
.fetchOne();
}
@Override
public boolean checkAddressUsed(Long addressId) {
LOG.debug("Address Id to be checked = {}", addressId);
return checkUsedFuncs.stream().anyMatch(checkUsedFunc -> checkUsedFunc.apply(addressId));
}
protected static void registerCheckUsedFunc(Function<Long, Boolean> checkUsedFunc) {
checkUsedFuncs.add(checkUsedFunc);
}
private static boolean checkAddressUsedBase(Long addressId) {
return JPA.all(PartnerAddress.class).filter("self.address.id = ?1", addressId).fetchOne()
!= null
|| JPA.all(Partner.class).filter("self.mainAddress.id = ?1", addressId).fetchOne() != null
|| JPA.all(PickListEntry.class).filter("self.address.id = ?1", addressId).fetchOne()
!= null;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public Optional<Pair<BigDecimal, BigDecimal>> getOrUpdateLatLong(Address address)
throws AxelorException, JSONException {
Preconditions.checkNotNull(address, I18n.get(IExceptionMessage.ADDRESS_CANNOT_BE_NULL));
Optional<Pair<BigDecimal, BigDecimal>> latLong = getLatLong(address);
if (latLong.isPresent()) {
return latLong;
}
return updateLatLong(address);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public Optional<Pair<BigDecimal, BigDecimal>> updateLatLong(Address address)
throws AxelorException, JSONException {
Preconditions.checkNotNull(address, I18n.get(IExceptionMessage.ADDRESS_CANNOT_BE_NULL));
if (mapService.isConfigured() && StringUtils.notBlank(address.getFullName())) {
Map<String, Object> result = mapService.getMap(address.getFullName());
if (result == null) {
address.setIsValidLatLong(false);
return Optional.empty();
}
address.setIsValidLatLong(true);
BigDecimal latitude = (BigDecimal) result.get("latitude");
BigDecimal longitude = (BigDecimal) result.get("longitude");
setLatLong(address, Pair.of(latitude, longitude));
}
return getLatLong(address);
}
@Override
@Transactional
public void resetLatLong(Address address) {
Preconditions.checkNotNull(address, I18n.get(IExceptionMessage.ADDRESS_CANNOT_BE_NULL));
setLatLong(address, Pair.of(null, null));
}
protected void setLatLong(Address address, Pair<BigDecimal, BigDecimal> latLong) {
address.setLatit(latLong.getLeft());
address.setLongit(latLong.getRight());
}
protected Optional<Pair<BigDecimal, BigDecimal>> getLatLong(Address address) {
if (address.getLatit() != null && address.getLongit() != null) {
return Optional.of(Pair.of(address.getLatit(), address.getLongit()));
}
return Optional.empty();
}
@Override
public String computeFullName(Address address) {
String l2 = address.getAddressL2();
String l3 = address.getAddressL3();
String l4 = address.getAddressL4();
String l5 = address.getAddressL5();
String l6 = address.getAddressL6();
return (!Strings.isNullOrEmpty(l2) ? l2 : "")
+ (!Strings.isNullOrEmpty(l3) ? " " + l3 : "")
+ (!Strings.isNullOrEmpty(l4) ? " " + l4 : "")
+ (!Strings.isNullOrEmpty(l5) ? " " + l5 : "")
+ (!Strings.isNullOrEmpty(l6) ? " " + l6 : "");
}
@Override
public String computeAddressStr(Address address) {
StringBuilder addressString = new StringBuilder();
if (address == null) {
return "";
}
if (address.getAddressL2() != null) {
addressString.append(address.getAddressL2()).append("\n");
}
if (address.getAddressL3() != null) {
addressString.append(address.getAddressL3()).append("\n");
}
if (address.getAddressL4() != null) {
addressString.append(address.getAddressL4()).append("\n");
}
if (address.getAddressL5() != null) {
addressString.append(address.getAddressL5()).append("\n");
}
if (address.getAddressL6() != null) {
addressString.append(address.getAddressL6());
}
if (address.getAddressL7Country() != null) {
addressString = addressString.append("\n").append(address.getAddressL7Country().getName());
}
return addressString.toString();
}
@Override
public void autocompleteAddress(Address address) {
String zip = address.getZip();
if (zip == null) {
return;
}
Country country = address.getAddressL7Country();
List<City> cities =
cityRepository
.all()
.filter("self.zip = :zip AND self.country = :country")
.bind("zip", zip)
.bind("country", country)
.fetch();
City city = cities.size() == 1 ? cities.get(0) : null;
address.setCity(city);
address.setAddressL6(city != null ? zip + " " + city.getName() : null);
List<Street> streets =
streetRepository.all().filter("self.city = :city").bind("city", city).fetch();
if (streets.size() == 1) {
Street street = streets.get(0);
address.setStreet(street);
String name = street.getName();
String num = address.getStreetNumber();
address.setAddressL4(num != null ? num + " " + name : name);
} else {
address.setStreet(null);
address.setAddressL4(null);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.AdjustHistory;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.Year;
import com.axelor.apps.base.db.repo.AdjustHistoryRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.time.LocalDateTime;
public class AdjustHistoryService {
private AdjustHistoryRepository adjustHistoryRepo;
@Inject
public AdjustHistoryService(AdjustHistoryRepository adjustHistoryRepo) {
this.adjustHistoryRepo = adjustHistoryRepo;
}
@Transactional
public void setStartDate(Year year) {
AdjustHistory adjustHistory = new AdjustHistory();
adjustHistory.setFiscalYear(year);
adjustHistory.setStartDate(LocalDateTime.now());
adjustHistoryRepo.save(adjustHistory);
}
@Transactional
public void setStartDate(Period period) {
AdjustHistory adjustHistory = new AdjustHistory();
adjustHistory.setPeriod(period);
adjustHistory.setStartDate(LocalDateTime.now());
adjustHistoryRepo.save(adjustHistory);
}
@Transactional
public AdjustHistory setEndDate(Year year) {
AdjustHistory adjustHistory =
adjustHistoryRepo
.all()
.filter("self.fiscalYear.id = ? AND self.endDate IS NULL", year.getId())
.fetchOne();
adjustHistory.setEndDate(LocalDateTime.now());
adjustHistoryRepo.save(adjustHistory);
return adjustHistory;
}
@Transactional
public void setEndDate(Period period) {
AdjustHistory adjustHistory =
adjustHistoryRepo
.all()
.filter("self.period.id = ? AND self.endDate IS NULL", period.getId())
.fetchOne();
adjustHistory.setEndDate(LocalDateTime.now());
adjustHistoryRepo.save(adjustHistory);
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.BankAddress;
public class BankAddressService {
public String computeFullAddress(String label, String address, String code) {
// fullAddress = label address (code)
String fullAddress = "";
fullAddress = label;
if (fullAddress == null || fullAddress.equals("")) {
fullAddress = "";
} else {
fullAddress += " ";
}
fullAddress += address;
if (code == null || code.equals("")) {
return fullAddress;
}
return fullAddress + " ( " + code + " )";
}
public String computeFullAddress(BankAddress bankAddress) {
return this.computeFullAddress(
bankAddress.getLabel(), bankAddress.getAddress(), bankAddress.getCode());
}
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.exception.AxelorException;
import org.iban4j.IbanFormatException;
import org.iban4j.InvalidCheckDigitException;
import org.iban4j.UnsupportedCountryException;
public interface BankDetailsService {
/**
* This method allows to extract information from iban Update following fields :
*
* <ul>
* <li>BankCode
* <li>SortCode
* <li>AccountNbr
* <li>BbanKey
* <li>Bank
* </ul>
*
* @param bankDetails
* @return BankDetails
*/
BankDetails detailsIban(BankDetails bankDetails);
/**
* Method allowing to create a bank details
*
* @param accountNbr
* @param bankCode
* @param bbanKey
* @param bank
* @param ownerName
* @param partner
* @param sortCode
* @return
*/
BankDetails createBankDetails(
String accountNbr,
String bankCode,
String bbanKey,
Bank bank,
String ownerName,
Partner partner,
String sortCode);
/**
* Create domain for the field companyBankDetails.
*
* @param company
* @param paymentMode
* @return
* @throws AxelorException
*/
String createCompanyBankDetailsDomain(
Partner partner, Company company, PaymentMode paymentMode, Integer operationTypeSelect)
throws AxelorException;
/**
* @param company
* @param paymentMode
* @param partner
* @return default value for the field companyBankDetails
* @throws AxelorException
*/
BankDetails getDefaultCompanyBankDetails(
Company company, PaymentMode paymentMode, Partner partner, Integer operationTypeSelect)
throws AxelorException;
/**
* Get active company bank details filtered on a currency
*
* @param company
* @param currency
* @return A string field that can used as domain (Jpql WHERE clause)
*/
String getActiveCompanyBankDetails(Company company, Currency currency);
/**
* Get active company bank details
*
* @param company
* @return A string field that can used as domain (Jpql WHERE clause)
*/
String getActiveCompanyBankDetails(Company company);
/**
* Method to validate a iban.
*
* @param iban
* @throws IbanFormatException
* @throws InvalidCheckDigitException
* @throws UnsupportedCountryException
*/
void validateIban(String iban)
throws IbanFormatException, InvalidCheckDigitException, UnsupportedCountryException;
}

View File

@ -0,0 +1,193 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.tool.StringTool;
import com.axelor.exception.AxelorException;
import org.iban4j.CountryCode;
import org.iban4j.IbanFormatException;
import org.iban4j.IbanUtil;
import org.iban4j.InvalidCheckDigitException;
import org.iban4j.UnsupportedCountryException;
public class BankDetailsServiceImpl implements BankDetailsService {
/**
* This method allows to extract information from iban Update following fields :
*
* <ul>
* <li>BankCode
* <li>SortCode
* <li>AccountNbr
* <li>BbanKey
* <li>Bank
* </ul>
*
* @param bankDetails
* @return BankDetails
*/
@Override
public BankDetails detailsIban(BankDetails bankDetails) {
if (bankDetails.getIban() != null) {
bankDetails.setBankCode(StringTool.extractStringFromRight(bankDetails.getIban(), 23, 5));
bankDetails.setSortCode(StringTool.extractStringFromRight(bankDetails.getIban(), 18, 5));
bankDetails.setAccountNbr(StringTool.extractStringFromRight(bankDetails.getIban(), 13, 11));
bankDetails.setBbanKey(StringTool.extractStringFromRight(bankDetails.getIban(), 2, 2));
}
return bankDetails;
}
/**
* Method allowing to create a bank details
*
* @param accountNbr
* @param bankCode
* @param bbanKey
* @param bank
* @param ownerName
* @param partner
* @param sortCode
* @return
*/
@Override
public BankDetails createBankDetails(
String accountNbr,
String bankCode,
String bbanKey,
Bank bank,
String ownerName,
Partner partner,
String sortCode) {
BankDetails bankDetails = new BankDetails();
bankDetails.setAccountNbr(accountNbr);
bankDetails.setBankCode(bankCode);
bankDetails.setBbanKey(bbanKey);
bankDetails.setBank(bank);
bankDetails.setOwnerName(ownerName);
bankDetails.setPartner(partner);
bankDetails.setSortCode(sortCode);
return bankDetails;
}
/**
* In this implementation, we do not have the O2M in paymentMode. The bank details is from the
* company.
*
* @param company
* @param paymentMode
* @return
* @throws AxelorException
*/
@Override
public String createCompanyBankDetailsDomain(
Partner partner, Company company, PaymentMode paymentMode, Integer operationTypeSelect)
throws AxelorException {
if (company == null) {
return "self.id IN (0)";
}
return "self.id IN ("
+ StringTool.getIdListString(company.getBankDetailsSet())
+ ") AND self.active = true";
}
@Override
public BankDetails getDefaultCompanyBankDetails(
Company company, PaymentMode paymentMode, Partner partner, Integer operationTypeSelect)
throws AxelorException {
BankDetails bankDetails = company.getDefaultBankDetails();
if (bankDetails != null && bankDetails.getActive()) {
return company.getDefaultBankDetails();
} else {
return null;
}
}
/**
* Get active company bank details filtered on a currency
*
* @param company
* @param currency
* @return A string field that can used as domain (Jpql WHERE clause)
*/
public String getActiveCompanyBankDetails(Company company, Currency currency) {
String domain = getActiveCompanyBankDetails(company);
// filter on the currency if it is set in file format and in the bankdetails
if (currency != null) {
String fileFormatCurrencyId = currency.getId().toString();
domain += " AND (self.currency IS NULL OR self.currency.id = " + fileFormatCurrencyId + ")";
}
return domain;
}
/**
* Get active company bank details
*
* @param company
* @return A string field that can used as domain (Jpql WHERE clause)
*/
public String getActiveCompanyBankDetails(Company company) {
String domain = "";
if (company != null) {
String bankDetailsIds = StringTool.getIdListString(company.getBankDetailsSet());
if (company.getDefaultBankDetails() != null) {
bankDetailsIds += bankDetailsIds.equals("") ? "" : ",";
bankDetailsIds += company.getDefaultBankDetails().getId().toString();
}
if (bankDetailsIds.equals("")) {
return "";
}
domain = "self.id IN(" + bankDetailsIds + ")";
}
if (domain.equals("")) {
return domain;
}
// filter the result on active bank details
domain += " AND self.active = true";
return domain;
}
public void validateIban(String iban)
throws IbanFormatException, InvalidCheckDigitException, UnsupportedCountryException {
CountryCode countryCode = CountryCode.getByCode(IbanUtil.getCountryCode(iban));
if (countryCode == null) {
throw new UnsupportedCountryException("Country code is not supported.");
}
if (IbanUtil.isSupportedCountry(countryCode)) {
IbanUtil.validate(iban);
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Bank;
public interface BankService {
public void splitBic(Bank bank);
public void computeFullName(Bank bank);
}

View File

@ -0,0 +1,57 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Bank;
import com.axelor.apps.base.db.Country;
import com.axelor.apps.base.db.repo.CountryRepository;
import com.axelor.inject.Beans;
public class BankServiceImpl implements BankService {
@Override
public void splitBic(Bank bank) {
String bic = bank.getCode();
// BNPA FR PP XXX
// 0123 45 67 8910
bank.setBusinessPartyPrefix(bic.substring(0, 4));
String alpha2 = bic.substring(4, 6);
Country country =
Beans.get(CountryRepository.class).all().filter("alpha2code = ?", alpha2).fetchOne();
bank.setCountry(country);
bank.setBusinessPartySuffix(bic.substring(6, 8));
String branchId;
try {
branchId = bic.substring(8, 11);
} catch (IndexOutOfBoundsException e) {
branchId = "XXX";
} catch (Exception e) {
throw e;
}
bank.setBranchIdentifier(branchId);
}
@Override
public void computeFullName(Bank bank) {
bank.setFullName(bank.getCode() + " - " + bank.getBankName());
}
}

View File

@ -0,0 +1,28 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.BarcodeTypeConfig;
import com.axelor.exception.AxelorException;
import java.io.InputStream;
public interface BarcodeGeneratorService {
InputStream createBarCode(String serialno, BarcodeTypeConfig barcodeTypeConfig, boolean isPadding)
throws AxelorException;
}

View File

@ -0,0 +1,360 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.BarcodeTypeConfig;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Singleton;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class BarcodeGeneratorServiceImpl implements BarcodeGeneratorService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public InputStream createBarCode(
String serialno, BarcodeTypeConfig barcodeTypeConfig, boolean isPadding)
throws AxelorException {
if (serialno != null && barcodeTypeConfig != null) {
BarcodeFormat barcodeFormat = null;
switch (barcodeTypeConfig.getName()) {
case "AZTEC":
barcodeFormat = BarcodeFormat.AZTEC;
break;
case "CODABAR":
barcodeFormat = BarcodeFormat.CODABAR;
serialno = checkTypeForCodabar(serialno, barcodeFormat);
break;
case "CODE_39":
barcodeFormat = BarcodeFormat.CODE_39;
serialno = checkTypeForCode39(serialno, barcodeFormat);
break;
case "CODE_128":
barcodeFormat = BarcodeFormat.CODE_128;
break;
case "DATA_MATRIX":
barcodeFormat = BarcodeFormat.DATA_MATRIX;
break;
case "EAN_8":
barcodeFormat = BarcodeFormat.EAN_8;
serialno = checkTypeForEan8(serialno, barcodeFormat, isPadding);
break;
case "ITF":
barcodeFormat = BarcodeFormat.ITF;
serialno = checkTypeForItf(serialno, barcodeFormat, isPadding);
break;
case "PDF_417":
barcodeFormat = BarcodeFormat.PDF_417;
serialno = checkTypeForPdf417(serialno, barcodeFormat, isPadding);
break;
case "QR_CODE":
barcodeFormat = BarcodeFormat.QR_CODE;
break;
case "UPC_A":
barcodeFormat = BarcodeFormat.UPC_A;
serialno = checkTypeForUpca(serialno, barcodeFormat, isPadding);
break;
case "EAN_13":
barcodeFormat = BarcodeFormat.EAN_13;
serialno = checkTypeForEan13(serialno, barcodeFormat, isPadding);
break;
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_9));
}
return generateBarcode(serialno, barcodeTypeConfig, barcodeFormat);
}
return null;
}
public InputStream generateBarcode(
String serialno, BarcodeTypeConfig barcodeTypeConfig, BarcodeFormat barcodeFormat) {
final MultiFormatWriter writer = new MultiFormatWriter();
int height = barcodeTypeConfig.getHeight();
int width = barcodeTypeConfig.getWidth();
BitMatrix bt;
try {
bt = writer.encode(serialno, barcodeFormat, width, height);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] pixels = new int[width * height];
int index = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[index++] = bt.get(x, y) ? Color.BLACK.hashCode() : Color.WHITE.hashCode();
}
}
image.setRGB(0, 0, width, height, pixels, 0, width);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, "png", out);
return new ByteArrayInputStream(out.toByteArray());
} catch (WriterException | IOException e) {
LOG.trace(e.getMessage(), e);
}
return null;
}
// accepts only number with variable length
public String checkTypeForCodabar(String serialno, BarcodeFormat barcodeFormat)
throws AxelorException {
if (isNumber(serialno)) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_3),
serialno,
barcodeFormat,
null);
}
// accepts variable length of alphanumeric input but alphabet in upperCase
// only
public String checkTypeForCode39(String serialno, BarcodeFormat barcodeFormat)
throws AxelorException {
if (serialno.equals(serialno.toUpperCase())) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_4),
serialno,
barcodeFormat,
null);
}
// accepts only number with even length
public String checkTypeForItf(String serialno, BarcodeFormat barcodeFormat, Boolean isPadding)
throws AxelorException {
if (isPadding) {
if ((serialno.length() % 2) != 0) {
serialno += "1";
}
}
if (isNumber(serialno) && (serialno.length() % 2) == 0) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_2),
serialno,
barcodeFormat,
null);
}
// accepts alphanumeric input with min and max length limit
public String checkTypeForPdf417(String serialno, BarcodeFormat barcodeFormat, Boolean isPadding)
throws AxelorException {
if (isPadding) {
serialno = addCharPaddingBits(4, serialno, barcodeFormat);
}
// return serialno only if it is strictly alphanumeric
if (serialno.length() == 4 || serialno.length() == 5) {
if (isAlphanumeric(serialno)) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_6),
serialno,
barcodeFormat);
}
// return serialno if it is only number or only alphabets
else if (serialno.length() == 7 || serialno.length() == 8 || serialno.length() == 9) {
if (isNumber(serialno)) {
return serialno;
} else if (isCharacter(serialno)) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_8),
serialno,
barcodeFormat);
} else if (serialno.length() > 3 && serialno.length() < 12) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_5),
serialno,
barcodeFormat,
3,
12);
}
// accepts only number with fixed length of input
public String checkTypeForUpca(String serialno, BarcodeFormat barcodeFormat, Boolean isPadding)
throws AxelorException {
if (isPadding) {
serialno = addPaddingBits(11, serialno, barcodeFormat);
}
serialno = checkTypeForFixedLength(serialno, barcodeFormat, 11);
return serialno;
}
// accepts only number with fixed length of input
public String checkTypeForEan8(String serialno, BarcodeFormat barcodeFormat, Boolean isPadding)
throws AxelorException {
if (isPadding) {
serialno = addPaddingBits(7, serialno, barcodeFormat);
}
serialno = checkTypeForFixedLength(serialno, barcodeFormat, 7);
return serialno;
}
// accepts only number with fixed length of input
public String checkTypeForEan13(String serialno, BarcodeFormat barcodeFormat, Boolean isPadding)
throws AxelorException {
if (isPadding) {
serialno = addPaddingBits(12, serialno, barcodeFormat);
}
serialno = checkTypeForFixedLength(serialno, barcodeFormat, 12);
return serialno;
}
// type check for the barcodes which accept only numbers as input with fixed
// length like EAN_8, EAN_13, UPC_A
public String checkTypeForFixedLength(
String serialno, BarcodeFormat barcodeFormat, int barcodeLength) throws AxelorException {
if (isNumber(serialno) && barcodeLength == serialno.length()) {
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_1),
serialno,
barcodeFormat,
barcodeLength);
}
public boolean isNumber(String s) {
try {
Long.parseLong(s);
} catch (NumberFormatException e) {
return false;
} catch (NullPointerException e) {
return false;
}
return true;
}
public boolean isAlphanumeric(String s) {
String PATTERN = "([A-Za-z]+[0-9]|[0-9]+[A-Za-z])[A-Za-z0-9]*";
Pattern pattern = Pattern.compile(PATTERN);
if (pattern.matcher(s).matches()) {
return true;
}
return false;
}
public boolean isCharacter(String s) {
char[] charArr = s.toCharArray();
for (char c : charArr) {
if (!Character.isLetter(c)) {
return false;
}
}
return true;
}
// add padding bits for the barcodes which accepts input with fixed length
// like EAN_8, EAN_13, UPC_A
public String addPaddingBits(int barcodeLength, String serialno, BarcodeFormat barcodeFormat)
throws AxelorException {
int paddingbits;
int serialnoLength = serialno.length();
if (serialnoLength < barcodeLength) {
paddingbits = barcodeLength - serialnoLength;
for (int i = 0; i < paddingbits; i++) {
serialno += "1";
}
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_7),
serialno,
barcodeFormat,
barcodeLength);
}
// add char padding bits for the barcode like PDF_417
public String addCharPaddingBits(int barcodeLength, String serialno, BarcodeFormat barcodeFormat)
throws AxelorException {
int paddingbits;
int serialnoLength = serialno.length();
if (serialnoLength < barcodeLength) {
paddingbits = barcodeLength - serialnoLength;
for (int i = 0; i < paddingbits; i++) {
serialno += "a";
}
return serialno;
}
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BARCODE_GENERATOR_7),
serialno,
barcodeFormat,
barcodeLength);
}
}

View File

@ -0,0 +1,76 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Blocking;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.inject.Beans;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class BlockingService {
/**
* Checks if {@code partner} is blocked for the {@code blockingType}
*
* @param partner Partner to check blocking
* @param company Company associated with the blocking
* @param blockingType Type of blocking
* @return blocking if partner is blocked for provided company and blocking type, null otherwise
*/
public Blocking getBlocking(Partner partner, Company company, int blockingType) {
List<Blocking> blockings = partner.getBlockingList();
if (blockings != null && !blockings.isEmpty()) {
for (Blocking blocking : blockings) {
if (blocking.getCompanySet().contains(company)
&& blocking.getBlockingSelect().equals(blockingType)
&& blocking
.getBlockingToDate()
.compareTo(Beans.get(AppBaseService.class).getTodayDate())
>= 0) {
return blocking;
}
}
}
return null;
}
/**
* @param company
* @param blockingType
* @return the query to get blocked partners ids for the given company and blocking type
*/
public String listOfBlockedPartner(Company company, int blockingType) {
return String.format(
"SELECT DISTINCT partner.id FROM Partner partner "
+ "LEFT JOIN partner.blockingList blocking "
+ "LEFT JOIN blocking.companySet company "
+ "WHERE blocking.blockingSelect = %d "
+ "AND blocking.blockingToDate >= '%s' "
+ "AND company.id = %d",
blockingType,
Beans.get(AppBaseService.class)
.getTodayDate()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
company.getId());
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
public interface CompanyService {
/**
* Check whether the provided company has more than one active bank details. In that case, enable
* the manageMultiBanks boolean in the general object.
*
* @param company the company to check for multiple active bank details
*/
void checkMultiBanks(Company company);
}

View File

@ -0,0 +1,61 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.AppBase;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.inject.Beans;
import java.util.Set;
public class CompanyServiceImpl implements CompanyService {
/** {@inheritDoc} */
@Override
public void checkMultiBanks(Company company) {
if (countActiveBankDetails(company) > 1) {
AppBaseService appBaseService = Beans.get(AppBaseService.class);
AppBase appBase = appBaseService.getAppBase();
if (!appBase.getManageMultiBanks()) {
appBaseService.setManageMultiBanks(true);
}
}
}
/**
* Count the number of active bank details on the provided company.
*
* @param company the company on which we count the number of active bank details
* @return the number of active bank details
*/
private int countActiveBankDetails(Company company) {
int count = 0;
Set<BankDetails> bankDetailsSet = company.getBankDetailsSet();
if (bankDetailsSet != null) {
for (BankDetails bankDetails : bankDetailsSet) {
if (bankDetails.getActive()) {
++count;
}
}
}
return count;
}
}

View File

@ -0,0 +1,23 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2020 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
public interface ConvertNumberToFrenchWordsService {
public String convert(long number);
}

View File

@ -0,0 +1,218 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2020 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import java.text.DecimalFormat;
public class ConvertNumberToFrenchWordsServiceImpl implements ConvertNumberToFrenchWordsService {
private final String[] dizaineNames = {
"", //
"", //
"vingt", //
"trente", //
"quarante", //
"cinquante", //
"soixante", //
"soixante", //
"quatre-vingt", //
"quatre-vingt" //
};
private final String[] uniteNames1 = {
"", //
"un", //
"deux", //
"trois", //
"quatre", //
"cinq", //
"six", //
"sept", //
"huit", //
"neuf", //
"dix", //
"onze", //
"douze", //
"treize", //
"quatorze", //
"quinze", //
"seize", //
"dix-sept", //
"dix-huit", //
"dix-neuf" //
};
private final String[] uniteNames2 = {
"", //
"", //
"deux", //
"trois", //
"quatre", //
"cinq", //
"six", //
"sept", //
"huit", //
"neuf", //
"dix" //
};
private String convertZeroToHundred(int number) {
int laDizaine = number / 10;
int lUnite = number % 10;
String resultat = "";
switch (laDizaine) {
case 1:
case 7:
case 9:
lUnite = lUnite + 10;
break;
default:
}
String laLiaison = "";
if (laDizaine > 1) {
laLiaison = "-";
}
switch (lUnite) {
case 0:
laLiaison = "";
break;
case 1:
if (laDizaine == 8) {
laLiaison = "-";
} else {
laLiaison = " et ";
}
break;
case 11:
if (laDizaine == 7) {
laLiaison = " et ";
}
break;
default:
}
// dizaines en lettres
switch (laDizaine) {
case 0:
resultat = uniteNames1[lUnite];
break;
case 8:
if (lUnite == 0) {
resultat = dizaineNames[laDizaine];
} else {
resultat = dizaineNames[laDizaine] + laLiaison + uniteNames1[lUnite];
}
break;
default:
resultat = dizaineNames[laDizaine] + laLiaison + uniteNames1[lUnite];
}
return resultat;
}
private String convertLessThanOneThousand(int number) {
int lesCentaines = number / 100;
int leReste = number % 100;
String sReste = convertZeroToHundred(leReste);
String resultat;
switch (lesCentaines) {
case 0:
resultat = sReste;
break;
case 1:
if (leReste > 0) {
resultat = "cent " + sReste;
} else {
resultat = "cent";
}
break;
default:
if (leReste > 0) {
resultat = uniteNames2[lesCentaines] + " cent " + sReste;
} else {
resultat = uniteNames2[lesCentaines] + " cents";
}
}
return resultat;
}
public String convert(long number) {
if (number == 0) {
return "zero";
}
String snumber = Long.toString(number);
String mask = "000000000000";
DecimalFormat df = new DecimalFormat(mask);
snumber = df.format(number);
int lesMilliards = Integer.parseInt(snumber.substring(0, 3));
int lesMillions = Integer.parseInt(snumber.substring(3, 6));
int lesCentMille = Integer.parseInt(snumber.substring(6, 9));
int lesMille = Integer.parseInt(snumber.substring(9, 12));
String tradMilliards;
switch (lesMilliards) {
case 0:
tradMilliards = "";
break;
case 1:
tradMilliards = convertLessThanOneThousand(lesMilliards) + " milliard ";
break;
default:
tradMilliards = convertLessThanOneThousand(lesMilliards) + " milliards ";
}
String resultat = tradMilliards;
String tradMillions;
switch (lesMillions) {
case 0:
tradMillions = "";
break;
case 1:
tradMillions = convertLessThanOneThousand(lesMillions) + " million ";
break;
default:
tradMillions = convertLessThanOneThousand(lesMillions) + " millions ";
}
resultat = resultat + tradMillions;
String tradCentMille;
switch (lesCentMille) {
case 0:
tradCentMille = "";
break;
case 1:
tradCentMille = "mille ";
break;
default:
tradCentMille = convertLessThanOneThousand(lesCentMille) + " mille ";
}
resultat = resultat + tradCentMille;
String tradMille;
tradMille = convertLessThanOneThousand(lesMille);
resultat = resultat + tradMille;
return resultat;
}
}

View File

@ -0,0 +1,308 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.AppBase;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.CurrencyConversionLine;
import com.axelor.apps.base.db.repo.CurrencyConversionLineRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDate;
import java.time.Period;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wslite.http.HTTPClient;
import wslite.http.HTTPMethod;
import wslite.http.HTTPRequest;
import wslite.http.HTTPResponse;
import wslite.json.JSONArray;
import wslite.json.JSONException;
import wslite.json.JSONObject;
@Singleton
public class CurrencyConversionService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Inject protected AppBaseService appBaseService;
@Inject protected CurrencyConversionLineRepository cclRepo;
public void updateCurrencyConverion() throws AxelorException {
AppBase appBase = appBaseService.getAppBase();
LocalDate today = appBaseService.getTodayDate();
Map<Long, Set<Long>> currencyMap = new HashMap<Long, Set<Long>>();
for (CurrencyConversionLine ccl : appBase.getCurrencyConversionLineList()) {
if (currencyMap.containsKey(ccl.getEndCurrency().getId())) {
currencyMap.get(ccl.getEndCurrency().getId()).add(ccl.getStartCurrency().getId());
} else {
Set<Long> startCurrencyIds = new HashSet<>();
startCurrencyIds.add(ccl.getStartCurrency().getId());
currencyMap.put(ccl.getEndCurrency().getId(), startCurrencyIds);
}
}
for (Long key : currencyMap.keySet()) {
List<CurrencyConversionLine> cclList =
cclRepo
.all()
.filter(
"startCurrency.id IN (?1) AND endCurrency.id = ?2 AND fromDate <= ?3 AND toDate is null",
currencyMap.get(key),
key,
today)
.fetch();
for (CurrencyConversionLine ccl : cclList) {
LOG.trace("Currency Conversion Line without toDate : {}", ccl);
BigDecimal currentRate = BigDecimal.ZERO;
try {
currentRate = this.convert(ccl.getStartCurrency(), ccl.getEndCurrency());
} catch (Exception e) {
TraceBackService.trace(e);
}
if (currentRate.compareTo(new BigDecimal(-1)) == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CURRENCY_6));
}
ccl = cclRepo.find(ccl.getId());
BigDecimal previousRate = ccl.getExchangeRate();
if (currentRate.compareTo(previousRate) != 0) {
ccl.setToDate(today.minusDays(1));
this.saveCurrencyConversionLine(ccl);
String variations = this.getVariations(currentRate, previousRate);
this.createCurrencyConversionLine(
ccl.getStartCurrency(),
ccl.getEndCurrency(),
today,
currentRate,
appBase,
variations);
}
}
}
}
public BigDecimal convert(Currency currencyFrom, Currency currencyTo)
throws MalformedURLException, JSONException, AxelorException {
BigDecimal rate = new BigDecimal(-1);
LOG.trace("Currerncy conversion From: {} To: {}", new Object[] {currencyFrom, currencyTo});
String wsUrl = appBaseService.getAppBase().getCurrencyWsURL();
if (wsUrl == null) {
LOG.trace("Currency WS URL not configured");
return rate;
}
if (currencyFrom != null && currencyTo != null) {
Float rt =
this.validateAndGetRate(
1, wsUrl, currencyFrom, currencyTo, appBaseService.getTodayDate());
rate = BigDecimal.valueOf(rt).setScale(8, RoundingMode.HALF_EVEN);
// Float rt =
// Float.parseFloat(json.getJSONObject("rates").get(currencyTo.getCode()).toString());
// rate = BigDecimal.valueOf(rt).setScale(4,RoundingMode.HALF_EVEN);
} else LOG.trace("Currency from and to must be filled to get rate");
LOG.trace("Currerncy conversion rate: {}", new Object[] {rate});
return rate;
}
private Float validateAndGetRate(
int dayCount, String wsUrl, Currency currencyFrom, Currency currencyTo, LocalDate date)
throws MalformedURLException, JSONException, AxelorException {
HTTPResponse response = null;
if (dayCount < 8) {
HTTPClient httpclient = new HTTPClient();
HTTPRequest request = new HTTPRequest();
Map<String, Object> headers = new HashMap<>();
headers.put("Accept", "application/json");
request.setHeaders(headers);
URL url =
new URL(String.format(wsUrl, currencyFrom.getCode(), currencyTo.getCode(), date, date));
// URL url = new URL(String.format(wsUrl,currencyFrom.getCode()));
LOG.trace("Currency conversion webservice URL: {}", new Object[] {url.toString()});
request.setUrl(url);
request.setMethod(HTTPMethod.GET);
try {
response = httpclient.execute(request);
} catch (Exception e) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CURRENCY_6));
}
// JSONObject json = new JSONObject(response.getContentAsString());
LOG.trace(
"Webservice response code: {}, response message: {}",
response.getStatusCode(),
response.getStatusMessage());
if (response.getStatusCode() != 200) return -1f;
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.CURRENCY_7),
date.plus(Period.ofDays(1)),
appBaseService.getTodayDate()));
}
if (response.getContentAsString().isEmpty()) {
return this.validateAndGetRate(
(dayCount + 1), wsUrl, currencyFrom, currencyTo, date.minus(Period.ofDays(1)));
} else {
return this.getRateFromJson(currencyFrom, currencyTo, response);
}
}
private Float getRateFromJson(Currency currencyFrom, Currency currencyTo, HTTPResponse response)
throws JSONException {
int compareCode = currencyFrom.getCode().compareTo(currencyTo.getCode());
Float rt = null;
String[] currencyRateArr = new String[2];
JSONObject jsonResult = new JSONObject(response.getContentAsString());
JSONObject dataSets = new JSONObject(jsonResult.getJSONArray("dataSets").get(0).toString());
JSONObject series = new JSONObject(dataSets.getJSONObject("series").toString());
JSONObject seriesOf = null;
JSONObject observations = null;
JSONArray rateValue = null;
if (series.size() > 1) {
for (int i = 0; i < series.size(); i++) {
seriesOf = new JSONObject(series.getJSONObject("0:" + i + ":0:0:0").toString());
observations = new JSONObject(seriesOf.getJSONObject("observations").toString());
rateValue = new JSONArray(observations.get(observations.length() - 1).toString());
currencyRateArr[i] = rateValue.get(0).toString();
}
if (compareCode > 0) {
rt = Float.parseFloat(currencyRateArr[0]) / Float.parseFloat(currencyRateArr[1]);
} else {
rt = Float.parseFloat(currencyRateArr[1]) / Float.parseFloat(currencyRateArr[0]);
}
} else {
seriesOf = new JSONObject(series.getJSONObject("0:0:0:0:0").toString());
observations = new JSONObject(seriesOf.getJSONObject("observations").toString());
rateValue = new JSONArray(observations.get(observations.length() - 1).toString());
if (currencyTo.getCode().equals("EUR")) {
rt = 1.0f / Float.parseFloat(rateValue.get(0).toString());
} else {
rt = Float.parseFloat(rateValue.get(0).toString());
}
}
return rt;
}
public String getVariations(BigDecimal currentRate, BigDecimal previousRate) {
String variations = "0";
LOG.trace(
"Currency rate variation calculation for CurrentRate: {} PreviousRate: {}",
new Object[] {currentRate, previousRate});
if (currentRate != null
&& previousRate != null
&& previousRate.compareTo(BigDecimal.ZERO) != 0) {
BigDecimal diffRate = currentRate.subtract(previousRate);
BigDecimal variation =
diffRate.multiply(new BigDecimal(100)).divide(previousRate, RoundingMode.HALF_EVEN);
variation =
variation.setScale(AppBaseService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_EVEN);
variations = variation.toString() + "%";
}
LOG.trace("Currency rate variation result: {}", new Object[] {variations});
return variations;
}
@Transactional
public void createCurrencyConversionLine(
Currency currencyFrom,
Currency currencyTo,
LocalDate fromDate,
BigDecimal rate,
AppBase appBase,
String variations) {
LOG.trace(
"Create new currency conversion line CurrencyFrom: {}, CurrencyTo: {},FromDate: {},ConversionRate: {}, AppBase: {}, Variations: {}",
new Object[] {currencyFrom, currencyTo, fromDate, rate, appBase, variations});
CurrencyConversionLine ccl = new CurrencyConversionLine();
ccl.setStartCurrency(currencyFrom);
ccl.setEndCurrency(currencyTo);
ccl.setFromDate(fromDate);
ccl.setExchangeRate(rate);
ccl.setAppBase(appBase);
ccl.setVariations(variations);
cclRepo.save(ccl);
}
@Transactional
public void saveCurrencyConversionLine(CurrencyConversionLine ccl) {
cclRepo.save(ccl);
}
// public BigDecimal getRate(Currency currencyFrom, Currency currencyTo, LocalDate rateDate){
//
// LOG.debug("Get Last rate for CurrencyFrom: {} CurrencyTo: {} RateDate: {}",new
// Object[]{currencyFrom,currencyTo,rateDate});
//
// BigDecimal rate = null;
//
// if(currencyFrom != null && currencyTo != null && rateDate != null){
// currencyFrom = currencyRepo.find(currencyFrom.getId());
// currencyTo = currencyRepo.find(currencyTo.getId());
// CurrencyConversionLine ccl = cclRepo.all().filter("startCurrency = ?1 AND endCurrency = ?2
// AND fromDate <= ?3 AND (toDate >= ?3 OR toDate =
// null)",currencyFrom,currencyTo,rateDate).fetchOne();
// if(ccl != null)
// rate = ccl.getExchangeRate();
// }
//
// LOG.debug("Current Rate: {}",new Object[]{rate});
//
// return rate;
// }
}

View File

@ -0,0 +1,205 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.CurrencyConversionLine;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.CallMethod;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class CurrencyService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected AppBaseService appBaseService;
private LocalDate today;
@Inject
public CurrencyService(AppBaseService appBaseService) {
this.appBaseService = appBaseService;
this.today = appBaseService.getTodayDate();
}
public CurrencyService(LocalDate today) {
this.appBaseService = Beans.get(AppBaseService.class);
this.today = today;
}
@CallMethod
public BigDecimal getCurrencyConversionRate(Currency startCurrency, Currency endCurrency)
throws AxelorException {
return this.getCurrencyConversionRate(startCurrency, endCurrency, this.today);
}
public BigDecimal getCurrencyConversionRate(
Currency startCurrency, Currency endCurrency, LocalDate date) throws AxelorException {
// If the start currency is different from end currency
// So we convert the amount
if (startCurrency != null && endCurrency != null && !startCurrency.equals(endCurrency)) {
LocalDate dateToConvert = this.getDateToConvert(date);
boolean isInverse = true;
BigDecimal exchangeRate = null;
CurrencyConversionLine currencyConversionLine =
this.getCurrencyConversionLine(startCurrency, endCurrency, dateToConvert);
if (currencyConversionLine != null) {
exchangeRate = currencyConversionLine.getExchangeRate();
isInverse = false;
} else {
currencyConversionLine =
this.getCurrencyConversionLine(endCurrency, startCurrency, dateToConvert);
if (currencyConversionLine == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CURRENCY_1),
startCurrency.getName(),
endCurrency.getName(),
dateToConvert);
}
exchangeRate = currencyConversionLine.getExchangeRate();
}
if (exchangeRate == null || exchangeRate.compareTo(BigDecimal.ZERO) == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CURRENCY_2),
startCurrency.getName(),
endCurrency.getName(),
dateToConvert);
}
return isInverse
? BigDecimal.ONE.divide(exchangeRate, 10, RoundingMode.HALF_EVEN)
: exchangeRate;
}
return BigDecimal.ONE;
}
private CurrencyConversionLine getCurrencyConversionLine(
Currency startCurrency, Currency endCurrency, LocalDate localDate) {
List<CurrencyConversionLine> currencyConversionLineList =
appBaseService.getCurrencyConfigurationLineList();
if (currencyConversionLineList == null) {
return null;
}
log.debug(
"Currency from: {}, Currency to: {}, localDate: {}", startCurrency, endCurrency, localDate);
for (CurrencyConversionLine ccl : currencyConversionLineList) {
String cclStartCode = ccl.getStartCurrency().getCode();
String cclEndCode = ccl.getEndCurrency().getCode();
String startCode = startCurrency.getCode();
String endCode = endCurrency.getCode();
LocalDate fromDate = ccl.getFromDate();
LocalDate toDate = ccl.getToDate();
if (cclStartCode.equals(startCode) && cclEndCode.equals(endCode)) {
if ((fromDate.isBefore(localDate) || fromDate.equals(localDate))
&& (toDate == null || toDate.isAfter(localDate) || toDate.isEqual(localDate))) {
return ccl;
}
}
}
return null;
}
/**
* Convert the amount in start currency into the end currency according to the date to convert
*
* @param startCurrency
* @param endCurrency
* @param amount
* @param date
* @return
* @throws AxelorException
*/
public BigDecimal getAmountCurrencyConvertedAtDate(
Currency startCurrency, Currency endCurrency, BigDecimal amount, LocalDate date)
throws AxelorException {
// If the start currency is different from end currency
// So we convert the amount
if (startCurrency != null && endCurrency != null && !startCurrency.equals(endCurrency)) {
return this.getAmountCurrencyConvertedUsingExchangeRate(
amount, this.getCurrencyConversionRate(startCurrency, endCurrency, date));
}
return amount;
}
/**
* Convert the amount in start currency into the end currency according to the exchange rate
*
* @param startCurrency
* @param endCurrency
* @param amount
* @param exchangeRate
* @return
* @throws AxelorException
*/
public BigDecimal getAmountCurrencyConvertedUsingExchangeRate(
BigDecimal amount, BigDecimal exchangeRate) throws AxelorException {
// If the start currency is different from end currency
// So we convert the amount
if (exchangeRate.compareTo(BigDecimal.ONE) != 0) {
return amount.multiply(exchangeRate).setScale(2, RoundingMode.HALF_EVEN);
}
return amount;
}
public LocalDate getDateToConvert(LocalDate date) {
if (date == null) {
date = this.today;
}
return date;
}
}

View File

@ -0,0 +1,291 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.db.JPA;
import com.axelor.db.JpaSecurity;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.repo.MetaFieldRepository;
import com.axelor.rpc.filter.Filter;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class DuplicateObjectsService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Logger log = LoggerFactory.getLogger(DuplicateObjectsService.class);
@Inject private MetaFieldRepository metaFieldRepo;
@Transactional
public void removeDuplicate(List<Long> selectedIds, String modelName) {
List<Object> duplicateObjects = getDuplicateObject(selectedIds, modelName);
Object originalObjct = getOriginalObject(selectedIds, modelName);
List<MetaField> allField =
metaFieldRepo
.all()
.filter(
"(relationship = 'ManyToOne' AND typeName = ?1) OR (relationship = 'ManyToMany' AND (typeName = ?1 OR metaModel.name =?1))",
modelName)
.fetch();
for (MetaField metaField : allField) {
if ("ManyToOne".equals(metaField.getRelationship())) {
Query update =
JPA.em()
.createQuery(
"UPDATE "
+ metaField.getMetaModel().getFullName()
+ " self SET self."
+ metaField.getName()
+ " = :value WHERE self."
+ metaField.getName()
+ " in (:duplicates)");
update.setParameter("value", originalObjct);
update.setParameter("duplicates", duplicateObjects);
update.executeUpdate();
} else if ("ManyToMany".equals(metaField.getRelationship())) {
if (metaField.getTypeName().equals(modelName)) {
Query select =
JPA.em()
.createQuery(
"select self from "
+ metaField.getMetaModel().getFullName()
+ " self LEFT JOIN self."
+ metaField.getName()
+ " as x WHERE x IN (:ids)");
select.setParameter("ids", duplicateObjects);
List<?> list = select.getResultList();
for (Object obj : list) {
Set<Object> items =
(Set<Object>) Mapper.of(obj.getClass()).get(obj, metaField.getName());
for (Object dupObj : duplicateObjects) {
if (items.contains(dupObj)) {
items.remove(dupObj);
}
}
items.add(originalObjct);
}
}
Mapper mapper = Mapper.of(originalObjct.getClass());
Set<Object> existRelationalObjects =
(Set<Object>) mapper.get(originalObjct, metaField.getName());
for (int i = 0; i < duplicateObjects.size(); i++) {
Set<Object> newRelationalObjects =
(Set<Object>) mapper.get(duplicateObjects.get(i), metaField.getName());
if (newRelationalObjects != null) {
existRelationalObjects.addAll(newRelationalObjects);
mapper.set(duplicateObjects.get(i), metaField.getName(), new HashSet<>());
}
}
}
}
JPA.em().flush();
JPA.em().clear();
for (Object obj : getDuplicateObject(selectedIds, modelName)) {
JPA.em().remove(obj);
}
}
@Transactional
public Object getOriginalObject(List<Long> selectedIds, String modelName) {
Query originalObj =
JPA.em().createQuery("SELECT self FROM " + modelName + " self WHERE self.id = :ids");
originalObj.setParameter("ids", selectedIds.get(0));
Object originalObjct = originalObj.getSingleResult();
return originalObjct;
}
@Transactional
public List<Object> getDuplicateObject(List<Long> selectedIds, String modelName) {
Query duplicateObj =
JPA.em().createQuery("SELECT self FROM " + modelName + " self WHERE self.id IN (:ids)");
duplicateObj.setParameter("ids", selectedIds.subList(1, selectedIds.size()));
List<Object> duplicateObjects = duplicateObj.getResultList();
return duplicateObjects;
}
@Transactional
public List<Object> getAllSelectedObject(List<Long> selectedIds, String modelName) {
Query duplicateObj =
JPA.em().createQuery("SELECT self FROM " + modelName + " self WHERE self.id IN (:ids)");
duplicateObj.setParameter("ids", selectedIds);
List<Object> duplicateObjects = duplicateObj.getResultList();
return duplicateObjects;
}
@Transactional
public Object getWizardValue(Long id, String modelName, String nameColumn) {
Query selectedObj;
if (nameColumn == null) {
selectedObj =
JPA.em().createQuery("SELECT self.id FROM " + modelName + " self WHERE self.id = :id");
} else {
selectedObj =
JPA.em()
.createQuery(
"SELECT self.id ,self."
+ nameColumn
+ " FROM "
+ modelName
+ " self WHERE self.id = :id");
}
selectedObj.setParameter("id", id);
Object selectedObject = selectedObj.getSingleResult();
return selectedObject;
}
@SuppressWarnings("unchecked")
public Filter getJpaSecurityFilter(Class<? extends Model> beanClass) {
JpaSecurity jpaSecurity = Beans.get(JpaSecurity.class);
return jpaSecurity.getFilter(JpaSecurity.CAN_READ, beanClass, (Long) null);
}
/*
* find duplicate records
*/
public List<?> findDuplicatedRecordIds(
Set<String> fieldSet, Class<? extends Model> modelClass, String filter)
throws AxelorException {
if (fieldSet == null || fieldSet.isEmpty()) {
return null;
}
String concatedFields = concatFields(modelClass, fieldSet);
String subQuery = createSubQuery(modelClass, filter, concatedFields);
log.debug("Duplicate check subquery: {}", concatedFields);
return fetchDuplicatedRecordIds(modelClass, concatedFields, subQuery, filter);
}
/*
* get all records for duplicate records
*/ private String concatFields(Class<?> modelClass, Set<String> fieldSet)
throws AxelorException {
StringBuilder fields = new StringBuilder("LOWER(concat(");
Mapper mapper = Mapper.of(modelClass);
int count = 0;
for (String field : fieldSet) {
Property property = mapper.getProperty(field);
if (property == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.GENERAL_8),
field,
modelClass.getSimpleName());
}
if (property.isCollection()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.GENERAL_9),
field);
}
if (count != 0) {
fields.append(",");
}
count++;
fields.append("cast(self" + "." + field);
if (property.getTarget() != null) {
fields.append(".id");
}
fields.append(" as string)");
}
fields.append("))");
return fields.toString();
}
private String createSubQuery(Class<?> modelClass, String filter, String concatedFields) {
StringBuilder queryBuilder = new StringBuilder("SELECT ");
queryBuilder.append(concatedFields);
queryBuilder.append(" FROM ");
queryBuilder.append(modelClass.getSimpleName() + " self");
if (filter != null) {
queryBuilder.append(" WHERE " + filter);
}
queryBuilder.append(" GROUP BY ");
queryBuilder.append(concatedFields);
queryBuilder.append(" HAVING COUNT(self) > 1");
return queryBuilder.toString();
}
private List<?> fetchDuplicatedRecordIds(
Class<? extends Model> modelClass, String concatedFields, String subQuery, String filter) {
log.debug("Fetch duplicated records for: {}", modelClass);
StringBuilder queryBuilder = new StringBuilder("SELECT self.id FROM ");
queryBuilder.append(modelClass.getSimpleName() + " self");
queryBuilder.append(" WHERE ");
queryBuilder.append(concatedFields);
queryBuilder.append(" IN ");
queryBuilder.append("(" + subQuery + ")");
if (filter != null) {
queryBuilder.append(" AND " + filter);
}
Filter securityFilter = getJpaSecurityFilter(modelClass);
Object[] params = new Object[] {};
if (securityFilter != null) {
queryBuilder.append(" AND (" + securityFilter.getQuery() + ")");
log.debug("JPA filter query: {}", securityFilter.getQuery());
params = securityFilter.getParams().toArray();
log.debug("JPA filter params: {}", securityFilter.getParams());
}
String query = queryBuilder.toString();
;
log.debug("Final query prepared: {}", query);
Query finalQuery = JPA.em().createQuery(query);
for (int i = 0; i < params.length; i++) {
finalQuery.setParameter(i, params[i]);
}
return finalQuery.getResultList();
}
}

View File

@ -0,0 +1,28 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Duration;
import java.math.BigDecimal;
import java.time.LocalDate;
public interface DurationService {
LocalDate computeDuration(Duration duration, LocalDate date);
BigDecimal computeRatio(LocalDate start, LocalDate end, Duration duration);
}

View File

@ -0,0 +1,75 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Duration;
import com.axelor.apps.base.db.repo.DurationRepository;
import com.axelor.i18n.I18n;
import com.google.common.base.Preconditions;
import com.google.inject.Singleton;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
@Singleton
public class DurationServiceImpl implements DurationService {
@Override
public LocalDate computeDuration(Duration duration, LocalDate date) {
if (duration == null) {
return date;
}
switch (duration.getTypeSelect()) {
case DurationRepository.TYPE_MONTH:
return date.plusMonths(duration.getValue());
case DurationRepository.TYPE_DAY:
return date.plusDays(duration.getValue());
default:
return date;
}
}
@Override
public BigDecimal computeRatio(LocalDate start, LocalDate end, Duration duration) {
Preconditions.checkNotNull(
start, I18n.get("You can't compute a" + " duration ratio without start date."));
Preconditions.checkNotNull(
end, I18n.get("You can't compute a" + " duration ratio without end date."));
if (duration == null) {
return BigDecimal.ONE;
}
end = end.plus(1, ChronoUnit.DAYS);
if (duration.getTypeSelect() == DurationRepository.TYPE_MONTH) {
long months = ChronoUnit.MONTHS.between(start, end);
LocalDate theoryEnd = start.plusMonths(months);
long restDays = ChronoUnit.DAYS.between(theoryEnd, end);
long daysInMonth = ChronoUnit.DAYS.between(theoryEnd, theoryEnd.plusMonths(1));
return BigDecimal.valueOf(months)
.add(
BigDecimal.valueOf(restDays)
.divide(BigDecimal.valueOf(daysInMonth), MathContext.DECIMAL32))
.divide(BigDecimal.valueOf(duration.getValue()), MathContext.DECIMAL32);
} else {
long restDays = ChronoUnit.DAYS.between(start, end);
return BigDecimal.valueOf(restDays)
.divide(BigDecimal.valueOf(duration.getValue()), MathContext.DECIMAL32);
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Frequency;
import java.time.LocalDate;
import java.util.List;
public interface FrequencyService {
/**
* Computes summary of given {@link Frequency}.
*
* @return Summary of given {@link Frequency}
*/
String computeSummary(Frequency frequency);
/**
* Retrieves all possible dates for given {@link Frequency} between {@code startDate} and {@code
* endDate}. If fourth and last day of week are checked in given {@link Frequency} and it is the
* same date, it will only appear once in return list.
*/
List<LocalDate> getDates(Frequency frequency, LocalDate startDate, LocalDate endDate);
/** Retrieves months checked in given {@link Frequency}. */
List<Integer> getMonths(Frequency frequency);
/** Retrieves days of week checked in given {@link Frequency}. */
List<Integer> getDays(Frequency frequency);
/** Retrieves occurences checked in given {@link Frequency}. */
List<Integer> getOccurences(Frequency frequency);
}

View File

@ -0,0 +1,513 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Frequency;
import com.axelor.apps.base.db.repo.FrequencyRepository;
import com.axelor.apps.tool.date.DateTool;
import com.axelor.i18n.I18n;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class FrequencyServiceImpl implements FrequencyService {
@Override
public String computeSummary(Frequency frequency) {
StringBuilder summary = new StringBuilder();
// Frequency
if (frequency.getTypeSelect() != null) {
if (frequency.getTypeSelect().equals(FrequencyRepository.TYPE_EVERY_N_WEEKS)) {
if (frequency.getEveryNWeeks() == 1) {
summary.append(I18n.get("Every week"));
} else {
summary
.append(I18n.get("Every"))
.append(" ")
.append(frequency.getEveryNWeeks())
.append(" ")
.append(I18n.get("weeks"));
}
summary.append("~ ");
summary.append(
I18n.get("THE_SINGULAR", "THE_PLURAL", 2)); // force plural for other languages
} else if (frequency.getTypeSelect().equals(FrequencyRepository.TYPE_MONTH_DAYS)) {
summary.append(I18n.get("Every")).append(" ");
if (frequency.getFirst()) {
summary.append(I18n.get("first"));
}
if (frequency.getSecond()) {
if (frequency.getFirst()) {
summary.append(", ");
}
summary.append(I18n.get("second"));
}
if (frequency.getThird()) {
if (frequency.getFirst() || frequency.getSecond()) {
summary.append(", ");
}
summary.append(I18n.get("third"));
}
if (frequency.getFourth()) {
if (frequency.getFirst() || frequency.getSecond() || frequency.getThird()) {
summary.append(", ");
}
summary.append(I18n.get("fourth"));
}
if (frequency.getLast()) {
if (frequency.getFirst()
|| frequency.getSecond()
|| frequency.getThird()
|| frequency.getFourth()) {
summary.append(", ");
}
summary.append(I18n.get("last"));
}
int lastCommaIndex = summary.lastIndexOf(",");
if (lastCommaIndex != -1) {
summary.replace(lastCommaIndex, lastCommaIndex + 1, " " + I18n.get("and"));
}
}
}
summary.append(" ");
// Days
if (frequency.getMonday()
&& frequency.getTuesday()
&& frequency.getWednesday()
&& frequency.getThursday()
&& frequency.getFriday()
&& !(frequency.getSaturday() || frequency.getSunday())) {
summary.append(I18n.get("weekdays"));
} else if (frequency.getSaturday()
&& frequency.getSunday()
&& !(frequency.getMonday()
|| frequency.getTuesday()
|| frequency.getWednesday()
|| frequency.getThursday()
|| frequency.getFriday())) {
summary.append(I18n.get("weekends"));
} else if (frequency.getMonday()
&& frequency.getTuesday()
&& frequency.getWednesday()
&& frequency.getThursday()
&& frequency.getFriday()
&& frequency.getSaturday()
&& frequency.getSunday()) {
summary.append(I18n.get("days"));
} else {
if (frequency.getMonday()) {
summary.append(I18n.get("Monday", "Mondays", 2)); // force plural for other languages
}
if (frequency.getTuesday()) {
if (frequency.getMonday()) {
summary.append(", ");
}
summary.append(I18n.get("Tuesday", "Tuesdays", 2));
}
if (frequency.getWednesday()) {
if (frequency.getMonday() || frequency.getTuesday()) {
summary.append(", ");
}
summary.append(I18n.get("Wednesday", "Wednesdays", 2));
}
if (frequency.getThursday()) {
if (frequency.getMonday() || frequency.getTuesday() || frequency.getWednesday()) {
summary.append(", ");
}
summary.append(I18n.get("Thursday", "Thursdays", 2));
}
if (frequency.getFriday()) {
if (frequency.getMonday()
|| frequency.getTuesday()
|| frequency.getWednesday()
|| frequency.getThursday()) {
summary.append(", ");
}
summary.append(I18n.get("Friday", "Fridays", 2));
}
if (frequency.getSaturday()) {
if (frequency.getMonday()
|| frequency.getTuesday()
|| frequency.getWednesday()
|| frequency.getThursday()
|| frequency.getFriday()) {
summary.append(", ");
}
summary.append(I18n.get("Saturday", "Saturdays", 2));
}
if (frequency.getSunday()) {
if (frequency.getMonday()
|| frequency.getTuesday()
|| frequency.getWednesday()
|| frequency.getThursday()
|| frequency.getFriday()
|| frequency.getSaturday()) {
summary.append(", ");
}
summary.append(I18n.get("Sunday", "Sundays", 2));
}
int lastCommaIndex = summary.lastIndexOf(",");
if (lastCommaIndex != -1) {
summary.replace(lastCommaIndex, lastCommaIndex + 1, " " + I18n.get("and"));
}
}
summary.append(" ").append(I18n.get("of")).append(" ");
// Months
if (frequency.getJanuary()
&& frequency.getFebruary()
&& frequency.getMarch()
&& frequency.getApril()
&& frequency.getMay()
&& frequency.getJune()
&& frequency.getJuly()
&& frequency.getAugust()
&& frequency.getSeptember()
&& frequency.getOctober()
&& frequency.getNovember()
&& frequency.getDecember()) {
summary.append(I18n.get("each month"));
} else {
if (frequency.getJanuary()) {
summary.append(I18n.get("January"));
}
if (frequency.getFebruary()) {
if (frequency.getJanuary()) {
summary.append(", ");
}
summary.append(I18n.get("February"));
}
if (frequency.getMarch()) {
if (frequency.getJanuary() || frequency.getFebruary()) {
summary.append(", ");
}
summary.append(I18n.get("March"));
}
if (frequency.getApril()) {
if (frequency.getJanuary() || frequency.getFebruary() || frequency.getMarch()) {
summary.append(", ");
}
summary.append(I18n.get("April"));
}
if (frequency.getMay()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()) {
summary.append(", ");
}
summary.append(I18n.get("May"));
}
if (frequency.getJune()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()) {
summary.append(", ");
}
summary.append(I18n.get("June"));
}
if (frequency.getJuly()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()) {
summary.append(", ");
}
summary.append(I18n.get("July"));
}
if (frequency.getAugust()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()
|| frequency.getJuly()) {
summary.append(", ");
}
summary.append(I18n.get("August"));
}
if (frequency.getSeptember()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()
|| frequency.getJuly()
|| frequency.getAugust()) {
summary.append(", ");
}
summary.append(I18n.get("September"));
}
if (frequency.getOctober()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()
|| frequency.getJuly()
|| frequency.getAugust()
|| frequency.getSeptember()) {
summary.append(", ");
}
summary.append(I18n.get("October"));
}
if (frequency.getNovember()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()
|| frequency.getJuly()
|| frequency.getAugust()
|| frequency.getSeptember()
|| frequency.getOctober()) {
summary.append(", ");
}
summary.append(I18n.get("November"));
}
if (frequency.getDecember()) {
if (frequency.getJanuary()
|| frequency.getFebruary()
|| frequency.getMarch()
|| frequency.getApril()
|| frequency.getMay()
|| frequency.getJune()
|| frequency.getJuly()
|| frequency.getAugust()
|| frequency.getSeptember()
|| frequency.getOctober()
|| frequency.getNovember()) {
summary.append(", ");
}
summary.append(I18n.get("December"));
}
int lastCommaIndex = summary.lastIndexOf(",");
if (lastCommaIndex != -1) {
summary.replace(lastCommaIndex, lastCommaIndex + 1, " " + I18n.get("and"));
}
}
int everyNWeeksCommaIndex = summary.indexOf("~");
if (everyNWeeksCommaIndex != -1) {
summary.replace(everyNWeeksCommaIndex, everyNWeeksCommaIndex + 1, ",");
}
if (frequency.getEndDate() != null) {
summary.append(", ");
summary.append(I18n.get("until"));
summary.append(" ");
summary.append(frequency.getEndDate().toString());
}
return summary.toString();
}
@Override
public List<LocalDate> getDates(Frequency frequency, LocalDate startDate, LocalDate endDate) {
Set<LocalDate> dates = new HashSet<>();
List<Integer> years = getYears(startDate, endDate);
List<Integer> months = getMonths(frequency);
List<Integer> days = getDays(frequency);
if (frequency.getTypeSelect().equals(FrequencyRepository.TYPE_MONTH_DAYS)) {
List<Integer> occurences = getOccurences(frequency);
for (Integer year : years) {
for (Integer month : months) {
for (Integer day : days) {
for (Integer occurence : occurences) {
LocalDate date = getDay(day, occurence, year, month);
if (DateTool.isBetween(startDate, endDate, date)) {
dates.add(date);
}
}
}
}
}
} else {
Integer leap = frequency.getEveryNWeeks();
for (Integer year : years) {
for (Integer day : days) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_WEEK, day);
cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
do {
if (months.contains(cal.get(Calendar.MONTH) + 1)) {
LocalDate date =
LocalDate.of(year, cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE));
if (DateTool.isBetween(startDate, endDate, date)) {
dates.add(date);
}
}
cal.add(Calendar.DATE, leap * 7);
} while (cal.get(Calendar.YEAR) == year);
}
}
}
ArrayList<LocalDate> sortedDates = new ArrayList<>(dates);
sortedDates.sort(LocalDate::compareTo);
return sortedDates;
}
/** Retrieves a LocalDate instance of given date in arguments. */
public LocalDate getDay(int dayOfWeek, int dayOfWeekInMonth, int year, int month) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, dayOfWeek);
cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, dayOfWeekInMonth);
cal.set(Calendar.YEAR, year);
cal.set(
Calendar.MONTH,
month - 1); // here, months are counted from 0: so January is 0, February is 1, etc.
return LocalDate.of(year, month, cal.get(Calendar.DATE));
}
/** Retrieves all years between {@code startDate} and {@code endDate}. */
public List<Integer> getYears(LocalDate startDate, LocalDate endDate) {
ArrayList<Integer> years = new ArrayList<>();
int startYear = startDate.getYear();
years.add(startYear);
for (long i = 0; i < ChronoUnit.YEARS.between(startDate, endDate); i++) {
years.add(startYear++);
}
return years;
}
@Override
public List<Integer> getMonths(Frequency frequency) {
List<Integer> months = new ArrayList<>();
if (frequency.getJanuary()) {
months.add(1);
}
if (frequency.getFebruary()) {
months.add(2);
}
if (frequency.getMarch()) {
months.add(3);
}
if (frequency.getApril()) {
months.add(4);
}
if (frequency.getMay()) {
months.add(5);
}
if (frequency.getJune()) {
months.add(6);
}
if (frequency.getJuly()) {
months.add(7);
}
if (frequency.getAugust()) {
months.add(8);
}
if (frequency.getSeptember()) {
months.add(9);
}
if (frequency.getOctober()) {
months.add(10);
}
if (frequency.getNovember()) {
months.add(11);
}
if (frequency.getDecember()) {
months.add(12);
}
return months;
}
@Override
public List<Integer> getDays(Frequency frequency) {
List<Integer> days = new ArrayList<>();
if (frequency.getSunday()) {
days.add(1);
}
if (frequency.getMonday()) {
days.add(2);
}
if (frequency.getTuesday()) {
days.add(3);
}
if (frequency.getWednesday()) {
days.add(4);
}
if (frequency.getThursday()) {
days.add(5);
}
if (frequency.getFriday()) {
days.add(6);
}
if (frequency.getSaturday()) {
days.add(7);
}
return days;
}
@Override
public List<Integer> getOccurences(Frequency frequency) {
List<Integer> occurences = new ArrayList<>();
if (frequency.getFirst()) {
occurences.add(1);
}
if (frequency.getSecond()) {
occurences.add(2);
}
if (frequency.getThird()) {
occurences.add(3);
}
if (frequency.getFourth()) {
occurences.add(4);
}
if (frequency.getLast()) {
occurences.add(-1);
}
return occurences;
}
}

View File

@ -0,0 +1,212 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import static com.axelor.common.StringUtils.isBlank;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.repo.PartnerRepository;
import com.axelor.apps.message.service.MailServiceMessageImpl;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.mail.db.MailAddress;
import com.axelor.mail.db.MailFollower;
import com.axelor.mail.db.MailMessage;
import com.axelor.mail.db.repo.MailFollowerRepository;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Singleton;
import javax.mail.internet.InternetAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class MailServiceBaseImpl extends MailServiceMessageImpl {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public Model resolve(String email) {
final UserRepository users = Beans.get(UserRepository.class);
final User user =
users.all().filter("self.partner.emailAddress.address = ?1", email).fetchOne();
if (user != null) {
return user;
}
final PartnerRepository partners = Beans.get(PartnerRepository.class);
final Partner partner =
partners.all().filter("self.emailAddress.address = ?1", email).fetchOne();
if (partner != null) {
return partner;
}
return super.resolve(email);
}
@Override
public List<InternetAddress> findEmails(String matching, List<String> selected, int maxResult) {
// Users
List<String> selectedWithoutNull = new ArrayList<String>(selected);
for (int i = 0; i < selected.size(); i++) {
if (Strings.isNullOrEmpty(selected.get(i))) selectedWithoutNull.remove(i);
}
final List<String> where = new ArrayList<>();
final Map<String, Object> params = new HashMap<>();
where.add(
"((self.partner is not null AND self.partner.emailAddress is not null) OR (self.email is not null))");
if (!isBlank(matching)) {
where.add(
"(LOWER(self.partner.emailAddress.address) like LOWER(:email) OR LOWER(self.partner.fullName) like LOWER(:email) OR LOWER(self.email) like LOWER(:email) OR LOWER(self.name) like LOWER(:email))");
params.put("email", "%" + matching + "%");
}
if (selectedWithoutNull != null && !selectedWithoutNull.isEmpty()) {
where.add("self.partner.emailAddress.address not in (:selected)");
params.put("selected", selectedWithoutNull);
}
final String filter = Joiner.on(" AND ").join(where);
final Query<User> query = Query.of(User.class);
if (!isBlank(filter)) {
query.filter(filter);
query.bind(params);
}
final List<InternetAddress> addresses = new ArrayList<>();
for (User user : query.fetch(maxResult)) {
try {
if (user.getPartner() != null
&& user.getPartner().getEmailAddress() != null
&& !Strings.isNullOrEmpty(user.getPartner().getEmailAddress().getAddress())) {
final InternetAddress item =
new InternetAddress(
user.getPartner().getEmailAddress().getAddress(), user.getFullName());
addresses.add(item);
selectedWithoutNull.add(user.getPartner().getEmailAddress().getAddress());
} else if (!Strings.isNullOrEmpty(user.getEmail())) {
final InternetAddress item = new InternetAddress(user.getEmail(), user.getFullName());
addresses.add(item);
selectedWithoutNull.add(user.getEmail());
}
} catch (UnsupportedEncodingException e) {
TraceBackService.trace(e);
}
}
// Partners
final List<String> where2 = new ArrayList<>();
final Map<String, Object> params2 = new HashMap<>();
where2.add("self.emailAddress is not null");
if (!isBlank(matching)) {
where2.add(
"(LOWER(self.emailAddress.address) like LOWER(:email) OR LOWER(self.fullName) like LOWER(:email))");
params2.put("email", "%" + matching + "%");
}
if (selectedWithoutNull != null && !selectedWithoutNull.isEmpty()) {
where2.add("self.emailAddress.address not in (:selected)");
params2.put("selected", selectedWithoutNull);
}
final String filter2 = Joiner.on(" AND ").join(where2);
final Query<Partner> query2 = Query.of(Partner.class);
if (!isBlank(filter2)) {
query2.filter(filter2);
query2.bind(params2);
}
for (Partner partner : query2.fetch(maxResult)) {
try {
if (partner.getEmailAddress() != null
&& !Strings.isNullOrEmpty(partner.getEmailAddress().getAddress())) {
final InternetAddress item =
new InternetAddress(partner.getEmailAddress().getAddress(), partner.getFullName());
addresses.add(item);
}
} catch (UnsupportedEncodingException e) {
TraceBackService.trace(e);
}
}
return addresses;
}
@Override
protected Set<String> recipients(MailMessage message, Model entity) {
final Set<String> recipients = new LinkedHashSet<>();
final MailFollowerRepository followers = Beans.get(MailFollowerRepository.class);
String entityName = entity.getClass().getName();
PartnerRepository partnerRepo = Beans.get(PartnerRepository.class);
if (message.getRecipients() != null) {
for (MailAddress address : message.getRecipients()) {
recipients.add(address.getAddress());
}
}
for (MailFollower follower : followers.findAll(message)) {
if (follower.getArchived()) {
continue;
}
User user = follower.getUser();
if (user != null) {
if (!(user.getReceiveEmails()
&& user.getFollowedMetaModelSet()
.stream()
.anyMatch(x -> x.getFullName().equals(entityName)))) {
continue;
} else {
Partner partner = partnerRepo.findByUser(user);
if (partner != null && partner.getEmailAddress() != null) {
recipients.add(partner.getEmailAddress().getAddress());
} else if (user.getEmail() != null) {
recipients.add(user.getEmail());
}
}
} else {
if (follower.getEmail() != null) {
recipients.add(follower.getEmail().getAddress());
} else {
log.info("No email address found for follower : " + follower);
}
}
}
return Sets.filter(recipients, Predicates.notNull());
}
}

View File

@ -0,0 +1,57 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Address;
import com.axelor.exception.AxelorException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import wslite.json.JSONException;
public interface MapRestService {
/**
* Set data response.
*
* @param mainNode
* @param arrayNode
* @throws AxelorException
* @throws JSONException
*/
void setData(ObjectNode mainNode, ArrayNode arrayNode) throws AxelorException, JSONException;
/**
* Set error response.
*
* @param mainNode
* @param e
*/
void setError(ObjectNode mainNode, Exception e);
/**
* Make address string.
*
* @param address
* @param objectNode
* @return
* @throws AxelorException
* @throws JSONException
*/
String makeAddressString(Address address, ObjectNode objectNode)
throws AxelorException, JSONException;
}

View File

@ -0,0 +1,111 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.common.StringUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.math.BigDecimal;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import wslite.json.JSONException;
public class MapRestServiceImpl implements MapRestService {
@Override
public String makeAddressString(Address address, ObjectNode objectNode)
throws AxelorException, JSONException {
Optional<Pair<BigDecimal, BigDecimal>> latLong =
Beans.get(AddressService.class).getOrUpdateLatLong(address);
if (!latLong.isPresent()) {
return "";
}
objectNode.put("latit", latLong.get().getLeft());
objectNode.put("longit", latLong.get().getRight());
return makeAddressString(address);
}
@Override
public void setData(ObjectNode mainNode, ArrayNode arrayNode)
throws AxelorException, JSONException {
mainNode.put("status", 0);
mainNode.set("data", arrayNode);
Optional<Address> optionalAddress = Beans.get(UserService.class).getUserActiveCompanyAddress();
if (optionalAddress.isPresent()) {
Optional<Pair<BigDecimal, BigDecimal>> latLong =
Beans.get(AddressService.class).getOrUpdateLatLong(optionalAddress.get());
if (latLong.isPresent()) {
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode objectNode = factory.objectNode();
objectNode.put("lat", latLong.get().getLeft());
objectNode.put("lng", latLong.get().getRight());
mainNode.set("company", objectNode);
}
}
}
@Override
public void setError(ObjectNode mainNode, Exception e) {
TraceBackService.trace(e);
mainNode.put("status", -1);
mainNode.put("errorMsg", e.getLocalizedMessage());
}
private String makeAddressString(Address address) {
StringBuilder addressString = new StringBuilder();
if (StringUtils.notBlank(address.getAddressL2())) {
addressString.append(address.getAddressL2() + "<br/>");
}
if (StringUtils.notBlank(address.getAddressL3())) {
addressString.append(address.getAddressL3() + "<br/>");
}
if (StringUtils.notBlank(address.getAddressL4())) {
addressString.append(address.getAddressL4() + "<br/>");
}
if (StringUtils.notBlank(address.getAddressL5())) {
addressString.append(address.getAddressL5() + "<br/>");
}
if (StringUtils.notBlank(address.getAddressL6())) {
addressString.append(address.getAddressL6());
}
if (address.getAddressL7Country() != null) {
addressString = addressString.append("<br/>" + address.getAddressL7Country().getName());
}
return addressString.toString();
}
}

View File

@ -0,0 +1,547 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.AppBase;
import com.axelor.apps.base.db.repo.AppBaseRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.common.StringUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import groovy.util.XmlSlurper;
import groovy.util.slurpersupport.GPathResult;
import groovy.util.slurpersupport.Node;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.ws.rs.core.UriBuilder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wslite.json.JSONArray;
import wslite.json.JSONException;
import wslite.json.JSONObject;
import wslite.rest.ContentType;
import wslite.rest.RESTClient;
import wslite.rest.Response;
public class MapService {
@Inject protected AppBaseService appBaseService;
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private BigDecimal lat;
private BigDecimal lon;
public JSONObject geocodeGoogle(String qString) throws AxelorException, JSONException {
if (StringUtils.isBlank(qString)) {
return null;
}
// http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true_or_false
// TODO inject the rest client, or better, run it in the browser
RESTClient restClient = new RESTClient("https://maps.googleapis.com");
Map<String, Object> responseQuery = new HashMap<>();
responseQuery.put("address", qString.trim());
responseQuery.put("sensor", "false");
responseQuery.put("key", getGoogleMapsApiKey());
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("path", "/maps/api/geocode/json");
responseMap.put("accept", ContentType.JSON);
responseMap.put("query", responseQuery);
responseMap.put("connectTimeout", 5000);
responseMap.put("readTimeout", 10000);
responseMap.put("followRedirects", false);
responseMap.put("useCaches", false);
responseMap.put("sslTrustAllCerts", true);
JSONObject restResponse = getJSON(restClient.get(responseMap));
LOG.debug("Gmap response: {}", restResponse);
if (restResponse.containsKey("results")) {
JSONArray results = (JSONArray) restResponse.get("results");
if (CollectionUtils.isNotEmpty(results)) {
JSONObject result = (JSONObject) results.iterator().next();
if (result != null && result.containsKey("geometry")) {
return (JSONObject) ((JSONObject) result.get("geometry")).get("location");
}
}
}
throw new AxelorException(
appBaseService.getAppBase(),
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.MAP_RESPONSE_ERROR),
restResponse);
/*
* log.debug("restResponse = {}", restResponse)
* log.debug("restResponse.parsedResponseContent.text = {}",
* restResponse.parsedResponseContent.text)
*/
// def searchresults = new
// JsonSlurper().parseText(restResponse.parsedResponseContent.text);
/*
* LOG.debug("searchresults.status = {}", searchresults.status); if
* (searchresults.status == "OK") { /*
* log.debug("searchresults.results.size() = {}", searchresults.results.size())
* log.debug("searchresults.results[0] = {}", searchresults.results[0])
* log.debug("searchresults.results[0].address_components = {}",
* searchresults.results[0].address_components)
* log.debug("searchresults.results[0].geometry.location = {}",
* searchresults.results[0].geometry.location)
*/
/*
* def results = searchresults.results;
*
* if (results.size() > 1) { response.put("multiple", true); } def
* firstPlaceFound = results[0];
*
* if (firstPlaceFound) { BigDecimal lat = new
* BigDecimal(firstPlaceFound.geometry.location.lat); BigDecimal lng = new
* BigDecimal(firstPlaceFound.geometry.location.lng);
*
* response.put("lat", lat.setScale(10, RoundingMode.HALF_EVEN));
* response.put("lng", lng.setScale(10, RoundingMode.HALF_EVEN)); }
*/
// }
}
public Map<String, Object> getMapGoogle(String qString) throws AxelorException, JSONException {
LOG.debug("Query string: {}", qString);
JSONObject googleResponse = geocodeGoogle(qString);
LOG.debug("Google response: {}", googleResponse);
if (googleResponse != null) {
Map<String, Object> result = new HashMap<>();
BigDecimal latitude = new BigDecimal(googleResponse.get("lat").toString());
BigDecimal longitude = new BigDecimal(googleResponse.get("lng").toString());
LOG.debug("URL:" + "map/gmaps.html?x=" + latitude + "&y=" + longitude + "&z=18");
result.put(
"url",
"map/gmaps.html?key="
+ getGoogleMapsApiKey()
+ "&x="
+ latitude
+ "&y="
+ longitude
+ "&z=18");
result.put("latitude", latitude);
result.put("longitude", longitude);
return result;
}
return null;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public Map<String, Object> getMapOsm(String qString) {
Map<String, Object> result = new HashMap<>();
try {
BigDecimal latitude = BigDecimal.ZERO;
BigDecimal longitude = BigDecimal.ZERO;
RESTClient restClient = new RESTClient("https://nominatim.openstreetmap.org/");
Map<String, Object> mapQuery = new HashMap<>();
mapQuery.put("q", qString);
mapQuery.put("format", "xml");
mapQuery.put("polygon", true);
mapQuery.put("addressdetails", true);
Map<String, Object> mapResponse = new HashMap<>();
mapResponse.put("path", "/search");
mapResponse.put("accept", ContentType.JSON);
mapResponse.put("query", mapQuery);
mapResponse.put("connectTimeout", 10000);
mapResponse.put("readTimeout", 10000);
mapResponse.put("followRedirects", false);
mapResponse.put("useCaches", false);
mapResponse.put("sslTrustAllCerts", true);
Response restResponse = restClient.get(mapResponse);
GPathResult searchresults = new XmlSlurper().parseText(restResponse.getContentAsString());
Iterator<Node> iterator = searchresults.childNodes();
if (iterator.hasNext()) {
Node node = iterator.next();
Map attributes = node.attributes();
if (attributes.containsKey("lat") && attributes.containsKey("lon")) {
if (BigDecimal.ZERO.compareTo(latitude) == 0)
latitude = new BigDecimal(node.attributes().get("lat").toString());
if (BigDecimal.ZERO.compareTo(longitude) == 0)
longitude = new BigDecimal(node.attributes().get("lon").toString());
}
}
LOG.debug("OSMap qString: {}, latitude: {}, longitude: {}", qString, latitude, longitude);
if (BigDecimal.ZERO.compareTo(latitude) != 0 && BigDecimal.ZERO.compareTo(longitude) != 0) {
result.put("url", "map/oneMarker.html?x=" + latitude + "&y=" + longitude + "&z=18");
result.put("latitude", latitude);
result.put("longitude", longitude);
return result;
}
} catch (Exception e) {
TraceBackService.trace(e);
}
return null;
}
public Map<String, Object> getMap(String qString) throws AxelorException, JSONException {
LOG.debug("qString = {}", qString);
switch (appBaseService.getAppBase().getMapApiSelect()) {
case AppBaseRepository.MAP_API_GOOGLE:
return getMapGoogle(qString);
case AppBaseRepository.MAP_API_OPEN_STREET_MAP:
return getMapOsm(qString);
default:
return null;
}
}
public String getMapUrl(Pair<BigDecimal, BigDecimal> latLong) {
return getMapUrl(latLong.getLeft(), latLong.getRight(), null);
}
public String getMapUrl(Pair<BigDecimal, BigDecimal> latLong, String title) {
return getMapUrl(latLong.getLeft(), latLong.getRight(), title);
}
public String getMapUrl(BigDecimal latitude, BigDecimal longitude) {
return getMapUrl(latitude, longitude, null);
}
public String getMapUrl(BigDecimal latitude, BigDecimal longitude, String title) {
try {
switch (appBaseService.getAppBase().getMapApiSelect()) {
case AppBaseRepository.MAP_API_GOOGLE:
final String uri = "map/gmaps.html";
UriBuilder ub = UriBuilder.fromUri(uri);
ub.queryParam("key", getGoogleMapsApiKey());
ub.queryParam("x", String.valueOf(latitude));
ub.queryParam("y", String.valueOf(longitude));
ub.queryParam("z", String.valueOf(18));
ub.queryParam("title", title);
return ub.build().toString();
case AppBaseRepository.MAP_API_OPEN_STREET_MAP:
return "map/oneMarker.html?x=" + latitude + "&y=" + longitude + "&z=18";
default:
return null;
}
} catch (Exception e) {
TraceBackService.trace(e);
return getErrorURI(e.getMessage());
}
}
public String getDirectionUrl(
String key,
Pair<BigDecimal, BigDecimal> departureLatLong,
Pair<BigDecimal, BigDecimal> arrivalLatLong) {
return getDirectionUrl(
key,
departureLatLong.getLeft(),
departureLatLong.getRight(),
arrivalLatLong.getLeft(),
arrivalLatLong.getRight());
}
public String getDirectionUrl(
String key, BigDecimal dLat, BigDecimal dLon, BigDecimal aLat, BigDecimal aLon) {
String queryParam = "dx=" + dLat + "&dy=" + dLon + "&ax=" + aLat + "&ay=" + aLon;
if (appBaseService.getAppBase().getMapApiSelect()
== AppBaseRepository.MAP_API_OPEN_STREET_MAP) {
return "map/osm-directions.html?" + queryParam;
}
return "map/directions.html?" + queryParam + "&key=" + key;
}
public Map<String, Object> getDirectionMapGoogle(
String dString,
BigDecimal dLat,
BigDecimal dLon,
String aString,
BigDecimal aLat,
BigDecimal aLon) {
LOG.debug("departureString = {}", dString);
LOG.debug("arrivalString = {}", aString);
Map<String, Object> result = new HashMap<>();
try {
if (!checkNotNullNotZero(dLat) || !checkNotNullNotZero(dLon)) {
getGoogleResponse(dString);
dLat = lat;
dLon = lon;
}
LOG.debug("departureLat = {}, departureLng={}", dLat, dLon);
if (!checkNotNullNotZero(aLat) || !checkNotNullNotZero(aLon)) {
getGoogleResponse(aString);
aLat = lat;
aLon = lon;
}
LOG.debug("arrivalLat = {}, arrivalLng={}", aLat, aLon);
if (checkNotNullNotZero(dLat)
&& checkNotNullNotZero(dLon)
&& checkNotNullNotZero(aLon)
&& checkNotNullNotZero(aLat)) {
result.put(
"url",
"map/directions.html?key="
+ getGoogleMapsApiKey()
+ "&dx="
+ dLat
+ "&dy="
+ dLon
+ "&ax="
+ aLat
+ "&ay="
+ aLon);
result.put("aLat", aLat);
result.put("dLat", dLat);
return result;
}
} catch (Exception e) {
TraceBackService.trace(e);
}
return null;
}
protected void getGoogleResponse(String key) throws AxelorException, JSONException {
@SuppressWarnings("unchecked")
Map<String, Object> googleResponse = geocodeGoogle(key);
lat = lon = BigDecimal.ZERO;
if (googleResponse != null) {
lat = new BigDecimal(googleResponse.get("lat").toString());
lon = new BigDecimal(googleResponse.get("lng").toString());
}
}
protected Boolean checkNotNullNotZero(BigDecimal value) {
Boolean flag = false;
if (value != null && BigDecimal.ZERO.compareTo(value) != 0) {
flag = true;
}
return flag;
}
public void testGMapService() throws AxelorException, JSONException {
RESTClient restClient = new RESTClient("https://maps.googleapis.com");
Map<String, Object> responseQuery = new HashMap<>();
responseQuery.put("address", "google");
responseQuery.put("sensor", "false");
responseQuery.put("key", getGoogleMapsApiKey());
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("path", "/maps/api/geocode/json");
responseMap.put("accept", ContentType.JSON);
responseMap.put("query", responseQuery);
responseMap.put("connectTimeout", 5000);
responseMap.put("readTimeout", 10000);
responseMap.put("followRedirects", false);
responseMap.put("useCaches", false);
responseMap.put("sslTrustAllCerts", true);
Response response = restClient.get(responseMap);
getJSON(response);
}
private JSONObject getJSON(Response response) throws AxelorException, JSONException {
LOG.debug(
"Gmap connection status code: {}, message: {}",
response.getStatusCode(),
response.getStatusMessage());
AppBase appBase = appBaseService.getAppBase();
if (response.getStatusCode() != HttpStatus.SC_OK) {
String msg = String.format("%d: %s", response.getStatusCode(), response.getStatusMessage());
throw new AxelorException(appBase, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, msg);
}
JSONObject json = new JSONObject(response.getContentAsString());
String status = json.getString("status");
if (!"OK".equalsIgnoreCase(status)) {
String msg =
json.has("error_message")
? String.format("%s: %s", status, json.getString("error_message"))
: status;
throw new AxelorException(appBase, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, msg);
}
return json;
}
public String getMapURI(String name) {
return appBaseService.getAppBase().getMapApiSelect() == AppBaseRepository.MAP_API_GOOGLE
? getMapURI(name, null)
: getOsmMapURI(name, null);
}
public String getMapURI(String name, Long id) {
final String uri = "map/gmap-objs.html";
try {
UriBuilder ub = UriBuilder.fromUri(uri);
ub.queryParam("key", getGoogleMapsApiKey());
ub.queryParam("object", name);
if (id != null) {
ub.queryParam("id", id);
}
return ub.build().toString();
} catch (Exception e) {
TraceBackService.trace(e);
return getErrorURI(e.getMessage());
}
}
private String getErrorURI(String msg) {
final String uri = "map/error.html";
try {
UriBuilder ub = UriBuilder.fromUri(uri);
ub.queryParam("msg", msg);
return ub.build().toString();
} catch (Exception e) {
TraceBackService.trace(e);
}
return uri;
}
public String getGoogleMapsApiKey() {
Preconditions.checkArgument(
StringUtils.notBlank(appBaseService.getAppBase().getGoogleMapsApiKey()),
I18n.get(IExceptionMessage.MAP_GOOGLE_MAPS_API_KEY_MISSING));
return appBaseService.getAppBase().getGoogleMapsApiKey();
}
public boolean isConfigured() {
switch (appBaseService.getAppBase().getMapApiSelect()) {
case AppBaseRepository.MAP_API_GOOGLE:
return StringUtils.notBlank(appBaseService.getAppBase().getGoogleMapsApiKey());
case AppBaseRepository.MAP_API_OPEN_STREET_MAP:
return true;
default:
return false;
}
}
public String getOsmMapURI(String name) {
return getOsmMapURI(name, null);
}
public String getOsmMapURI(String name, Long id) {
final String uri = "map/osm-objs.html";
try {
UriBuilder ub = UriBuilder.fromUri(uri);
ub.queryParam("object", name);
if (id != null) {
ub.queryParam("id", id);
}
return ub.build().toString();
} catch (Exception e) {
TraceBackService.trace(e);
return getErrorURI(e.getMessage());
}
}
public Map<String, Object> getDirectionMapOsm(
String dString,
BigDecimal dLat,
BigDecimal dLon,
String aString,
BigDecimal aLat,
BigDecimal aLon) {
LOG.debug("departureString = {}", dString);
LOG.debug("arrivalString = {}", aString);
Map<String, Object> result = new HashMap<>();
try {
if (!checkNotNullNotZero(dLat) || !checkNotNullNotZero(dLon)) {
getOsmResponse(dString);
dLat = lat;
dLon = lon;
}
LOG.debug("departureLat = {}, departureLng={}", dLat, dLon);
if (!checkNotNullNotZero(aLat) || !checkNotNullNotZero(aLon)) {
getOsmResponse(aString);
aLat = lat;
aLon = lon;
}
LOG.debug("arrivalLat = {}, arrivalLng={}", aLat, aLon);
if (checkNotNullNotZero(dLat)
&& checkNotNullNotZero(dLon)
&& checkNotNullNotZero(aLon)
&& checkNotNullNotZero(aLat)) {
result.put(
"url",
"map/osm-directions.html?"
+ "dx="
+ dLat
+ "&dy="
+ dLon
+ "&ax="
+ aLat
+ "&ay="
+ aLon);
result.put("aLat", aLat);
result.put("dLat", dLat);
return result;
}
} catch (Exception e) {
TraceBackService.trace(e);
}
return null;
}
protected void getOsmResponse(String qString) throws AxelorException, JSONException {
Map<String, Object> osmResponse = getMapOsm(qString);
lat = lon = BigDecimal.ZERO;
if (osmResponse != null) {
lat = new BigDecimal(osmResponse.get("latitude").toString());
lon = new BigDecimal(osmResponse.get("longitude").toString());
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PartnerPriceList;
import com.axelor.apps.base.db.PriceList;
import com.axelor.exception.AxelorException;
public interface PartnerPriceListService {
/**
* Allows to check the dates in a price list.
*
* @param partnerPriceList
* @throws AxelorException if two price lists are scheduled on the same time.
*/
void checkDates(PartnerPriceList partnerPriceList) throws AxelorException;
/**
* @param partner
* @param priceListTypeSelect
* @return the default price list from the partner null if partner is null, or no price list was
* found for the given partner
*/
PriceList getDefaultPriceList(Partner partner, int priceListTypeSelect);
/**
* @param partner
* @param priceListTypeSelect
* @return the domain for the partner and the type
*/
String getPriceListDomain(Partner partner, int priceListTypeSelect);
/**
* @param partner
* @param priceListTypeSelect
* @return the partner price list for the given type
*/
public PartnerPriceList getPartnerPriceList(Partner partner, int priceListTypeSelect);
}

View File

@ -0,0 +1,176 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PartnerPriceList;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.base.db.repo.PartnerPriceListRepository;
import com.axelor.apps.base.db.repo.PartnerRepository;
import com.axelor.apps.base.db.repo.PriceListRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.tool.StringTool;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class PartnerPriceListServiceImpl implements PartnerPriceListService {
protected AppBaseService appBaseService;
@Inject
public PartnerPriceListServiceImpl(AppBaseService appBaseService) {
this.appBaseService = appBaseService;
}
@Override
public void checkDates(PartnerPriceList partnerPriceList) throws AxelorException {
Set<PriceList> priceListSet = partnerPriceList.getPriceListSet();
if (priceListSet == null) {
return;
}
Set<PriceList> sortedPriceListSet =
priceListSet
.stream()
.sorted(
Comparator.comparing(
priceList ->
priceList.getApplicationBeginDate() != null
? priceList.getApplicationBeginDate()
: LocalDate.MIN))
.collect(Collectors.toSet());
LocalDate beginDate;
LocalDate previousEndDate = LocalDate.MIN;
String previousTitle = "";
for (PriceList priceList : sortedPriceListSet) {
beginDate =
priceList.getApplicationBeginDate() != null
? priceList.getApplicationBeginDate()
: LocalDate.MIN;
if (beginDate.compareTo(previousEndDate) < 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
String.format(
I18n.get(IExceptionMessage.PARTNER_PRICE_LIST_DATE_INCONSISTENT),
previousTitle.replace("%", "%%"),
priceList.getTitle().replace("%", "%%")),
partnerPriceList);
}
previousEndDate =
priceList.getApplicationEndDate() != null
? priceList.getApplicationEndDate()
: LocalDate.MAX;
previousTitle = priceList.getTitle();
}
}
@Override
public PriceList getDefaultPriceList(Partner partner, int priceListTypeSelect) {
if (partner == null) {
return null;
}
partner = Beans.get(PartnerRepository.class).find(partner.getId());
PartnerPriceList partnerPriceList = getPartnerPriceList(partner, priceListTypeSelect);
if (partnerPriceList == null) {
return null;
}
Set<PriceList> priceListSet = partnerPriceList.getPriceListSet();
if (priceListSet == null) {
return null;
}
List<PriceList> priceLists =
priceListSet
.stream()
.filter(
priceList ->
(priceList.getApplicationBeginDate() == null
|| priceList
.getApplicationBeginDate()
.compareTo(appBaseService.getTodayDate())
<= 0)
&& (priceList.getApplicationEndDate() == null
|| priceList
.getApplicationEndDate()
.compareTo(appBaseService.getTodayDate())
>= 0))
.collect(Collectors.toList());
if (priceLists.size() == 1) {
return priceLists.get(0);
} else {
return null;
}
}
public String getPriceListDomain(Partner partner, int priceListTypeSelect) {
if (partner == null) {
return "self.id IN (0)";
}
// get all non exclusive partner price lists
List<PartnerPriceList> partnerPriceLists =
Beans.get(PartnerPriceListRepository.class)
.all()
.filter("self.typeSelect = :_priceListTypeSelect " + "AND self.isExclusive = false")
.bind("_priceListTypeSelect", priceListTypeSelect)
.fetch();
// get (maybe exclusive) list for the partner
PartnerPriceList partnerPriceList = getPartnerPriceList(partner, priceListTypeSelect);
if (partnerPriceList != null && partnerPriceList.getIsExclusive()) {
partnerPriceLists.add(partnerPriceList);
}
if (partnerPriceLists.isEmpty()) {
return "self.id IN (0)";
}
List<PriceList> priceLists =
partnerPriceLists
.stream()
.flatMap(partnerPriceList1 -> partnerPriceList1.getPriceListSet().stream())
.filter(
priceList ->
priceList.getIsActive()
&& (priceList.getApplicationBeginDate() == null
|| priceList
.getApplicationBeginDate()
.compareTo(appBaseService.getTodayDate())
<= 0)
&& (priceList.getApplicationEndDate() == null
|| priceList
.getApplicationEndDate()
.compareTo(appBaseService.getTodayDate())
>= 0))
.collect(Collectors.toList());
return "self.id IN (" + StringTool.getIdListString(priceLists) + ")";
}
public PartnerPriceList getPartnerPriceList(Partner partner, int priceListTypeSelect) {
if (priceListTypeSelect == PriceListRepository.TYPE_SALE) {
return partner.getSalePartnerPriceList();
} else if (priceListTypeSelect == PriceListRepository.TYPE_PURCHASE) {
return partner.getPurchasePartnerPriceList();
}
return null;
}
}

View File

@ -0,0 +1,147 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.message.db.EmailAddress;
import com.axelor.exception.AxelorException;
import com.axelor.meta.CallMethod;
import java.util.List;
import java.util.Map;
public interface PartnerService {
Partner createPartner(
String name,
String firstName,
String fixedPhone,
String mobilePhone,
EmailAddress emailAddress,
Currency currency,
Address deliveryAddress,
Address mainInvoicingAddress);
void onSave(Partner partner) throws AxelorException;
void setPartnerFullName(Partner partner);
@CallMethod
String computeFullName(Partner partner);
@CallMethod
String computeSimpleFullName(Partner partner);
Map<String, String> getSocialNetworkUrl(String name, String firstName, Integer typeSelect);
List<Long> findPartnerMails(Partner partner);
List<Long> findContactMails(Partner partner);
List<Long> findMailsFromPartner(Partner partner);
void resetDefaultAddress(Partner partner, String addrTypeQuery);
Partner addPartnerAddress(
Partner partner, Address address, Boolean isDefault, Boolean isInvoicing, Boolean isDelivery);
void addContactToPartner(Partner contact);
@CallMethod
Address getInvoicingAddress(Partner partner);
@CallMethod
Address getDeliveryAddress(Partner partner);
Address getDefaultAddress(Partner partner);
Partner savePartner(Partner partner);
BankDetails getDefaultBankDetails(Partner partner);
String getSIRENNumber(Partner partner) throws AxelorException;
void convertToIndividualPartner(Partner partner);
/**
* Check if the partner in view has a duplicate.
*
* @param partner a context partner object
* @return if there is a duplicate partner
*/
boolean isThereDuplicatePartner(Partner partner);
/**
* Check if the partner in view has an archived duplicate.
*
* @param partner a context partner object
* @return a found archived partner or null.
*/
Partner isThereDuplicatePartnerInArchive(Partner partner);
/**
* Search for the sale price list for the current date in the partner.
*
* @param partner
* @return the sale price list for the partner null if no active price list has been found
*/
PriceList getSalePriceList(Partner partner);
/**
* Get the partner language code. If null, return the default partner language.
*
* @param partner
* @return
*/
String getPartnerLanguageCode(Partner partner);
/**
* Normalize phone number.
*
* @param phoneNumber
* @return
*/
String normalizePhoneNumber(String phoneNumber);
/**
* Check phone number.
*
* @param phoneNumber
* @return
*/
boolean checkPhoneNumber(String phoneNumber);
/**
* Get phone number field name.
*
* @param actionName
* @return
*/
String getPhoneNumberFieldName(String actionName);
void setCompanyStr(Partner partner);
String computeCompanyStr(Partner partner);
String getPartnerDomain(Partner partner);
void setPartnerBarCodeSeq(Partner partner);
}

View File

@ -0,0 +1,736 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import static com.axelor.apps.base.db.repo.PartnerRepository.PARTNER_TYPE_INDIVIDUAL;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.PartnerAddress;
import com.axelor.apps.base.db.PartnerPriceList;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.base.db.repo.PartnerAddressRepository;
import com.axelor.apps.base.db.repo.PartnerRepository;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.message.db.EmailAddress;
import com.axelor.common.StringUtils;
import com.axelor.db.JPA;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.inject.Singleton;
import javax.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class PartnerServiceImpl implements PartnerService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected PartnerRepository partnerRepo;
protected AppBaseService appBaseService;
@Inject protected BarcodeGeneratorService barcodeGeneratorService;
@Inject private MetaFiles metaFiles;
@Inject
public PartnerServiceImpl(PartnerRepository partnerRepo, AppBaseService appBaseService) {
this.partnerRepo = partnerRepo;
this.appBaseService = appBaseService;
}
private Pattern phoneNumberPattern =
Pattern.compile("^\\+?(?:[0-9]{2,3}(?:\\s|\\.)?){3,6}[0-9]{2,3}$");
public Partner createPartner(
String name,
String firstName,
String fixedPhone,
String mobilePhone,
EmailAddress emailAddress,
Currency currency,
Address deliveryAddress,
Address mainInvoicingAddress) {
Partner partner = new Partner();
partner.setName(name);
partner.setFirstName(firstName);
partner.setPartnerTypeSelect(PartnerRepository.PARTNER_TYPE_COMPANY);
partner.setIsProspect(true);
partner.setFixedPhone(fixedPhone);
partner.setMobilePhone(mobilePhone);
partner.setEmailAddress(emailAddress);
partner.setCurrency(currency);
this.setPartnerFullName(partner);
Partner contact = new Partner();
contact.setPartnerTypeSelect(PARTNER_TYPE_INDIVIDUAL);
contact.setIsContact(true);
contact.setName(name);
contact.setFirstName(firstName);
contact.setMainPartner(partner);
partner.addContactPartnerSetItem(contact);
this.setPartnerFullName(contact);
if (deliveryAddress == mainInvoicingAddress) {
addPartnerAddress(partner, mainInvoicingAddress, true, true, true);
} else {
addPartnerAddress(partner, deliveryAddress, true, false, true);
addPartnerAddress(partner, mainInvoicingAddress, true, true, false);
}
return partner;
}
@Override
public void onSave(Partner partner) throws AxelorException {
if (partner.getPartnerSeq() == null
&& appBaseService.getAppBase().getGeneratePartnerSequence()) {
String seq = Beans.get(SequenceService.class).getSequenceNumber(SequenceRepository.PARTNER);
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PARTNER_1));
}
partner.setPartnerSeq(seq);
}
if (partner.getEmailAddress() != null) {
long existEmailCount =
partnerRepo
.all()
.filter(
"self.id != ?1 and self.emailAddress = ?2",
partner.getId(),
partner.getEmailAddress())
.count();
if (existEmailCount > 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_UNIQUE_KEY,
I18n.get(IExceptionMessage.PARTNER_EMAIL_EXIST));
}
}
updatePartnerAddress(partner);
if (partner.getPartnerTypeSelect() == PARTNER_TYPE_INDIVIDUAL) {
partner.setContactPartnerSet(new HashSet<>());
}
if (!partner.getIsContact() && partner.getContactPartnerSet() != null) {
for (Partner contact : partner.getContactPartnerSet()) {
if (contact.getMainPartner() == null) {
contact.setMainPartner(partner);
}
}
}
this.setPartnerFullName(partner);
this.setCompanyStr(partner);
this.setPartnerBarCodeSeq(partner);
}
/**
* Updates M2O and O2M fields of partner that manage partner addresses. This method ensures
* consistency between these two fields.
*
* @param partner
* @throws AxelorException
*/
protected void updatePartnerAddress(Partner partner) throws AxelorException {
Address address = partner.getMainAddress();
if (!partner.getIsContact() && !partner.getIsEmployee()) {
if (partner.getPartnerAddressList() != null) {
partner.setMainAddress(checkDefaultAddress(partner));
}
} else if (address == null) {
partner.removePartnerAddressListItem(
JPA.all(PartnerAddress.class)
.filter("self.partner = :partnerId AND self.isDefaultAddr = 't'")
.bind("partnerId", partner.getId())
.fetchOne());
} else if (partner.getPartnerAddressList() != null
&& partner
.getPartnerAddressList()
.stream()
.map(PartnerAddress::getAddress)
.noneMatch(address::equals)) {
PartnerAddress mainAddress = new PartnerAddress();
mainAddress.setAddress(address);
mainAddress.setIsDefaultAddr(true);
mainAddress.setIsDeliveryAddr(true);
mainAddress.setIsInvoicingAddr(true);
partner.addPartnerAddressListItem(mainAddress);
}
}
/**
* Ensures that there is exactly one default invoicing address and no more than one default
* delivery address. If the partner address list is valid, returns the default invoicing address.
*
* @param partner
* @throws AxelorException
*/
protected Address checkDefaultAddress(Partner partner) throws AxelorException {
List<PartnerAddress> partnerAddressList = partner.getPartnerAddressList();
Address defaultInvoicingAddress = null;
Address defaultDeliveryAddress = null;
if (partnerAddressList != null) {
for (PartnerAddress partnerAddress : partnerAddressList) {
if (partnerAddress.getIsDefaultAddr() && partnerAddress.getIsInvoicingAddr()) {
if (defaultInvoicingAddress != null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY, I18n.get(IExceptionMessage.ADDRESS_8));
}
defaultInvoicingAddress = partnerAddress.getAddress();
}
if (partnerAddress.getIsDefaultAddr() && partnerAddress.getIsDeliveryAddr()) {
if (defaultDeliveryAddress != null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY, I18n.get(IExceptionMessage.ADDRESS_9));
}
defaultDeliveryAddress = partnerAddress.getAddress();
}
}
}
return defaultInvoicingAddress;
}
@Override
public void setPartnerFullName(Partner partner) {
partner.setSimpleFullName(this.computeSimpleFullName(partner));
partner.setFullName(this.computeFullName(partner));
}
@Override
public String computeFullName(Partner partner) {
if (!Strings.isNullOrEmpty(partner.getPartnerSeq())) {
return partner.getPartnerSeq() + " - " + partner.getSimpleFullName();
}
return computeSimpleFullName(partner);
}
@Override
public String computeSimpleFullName(Partner partner) {
if (!Strings.isNullOrEmpty(partner.getName())
&& !Strings.isNullOrEmpty(partner.getFirstName())) {
return partner.getName() + " " + partner.getFirstName();
} else if (!Strings.isNullOrEmpty(partner.getName())) {
return partner.getName();
} else if (!Strings.isNullOrEmpty(partner.getFirstName())) {
return partner.getFirstName();
} else {
return "" + partner.getId();
}
}
@Override
public Map<String, String> getSocialNetworkUrl(
String name, String firstName, Integer typeSelect) {
Map<String, String> urlMap = new HashMap<String, String>();
if (typeSelect == 2) {
name =
firstName != null && name != null
? firstName + "+" + name
: name == null ? firstName : name;
}
name = name == null ? "" : name;
urlMap.put(
"google",
"<a class='fa fa-google' href='https://www.google.com/?gws_rd=cr#q="
+ name
+ "' target='_blank' />");
urlMap.put(
"facebook",
"<a class='fa fa-facebook' href='https://www.facebook.com/search/more/?q="
+ name
+ "&init=public"
+ "' target='_blank'/>");
urlMap.put(
"twitter",
"<a class='fa fa-twitter' href='https://twitter.com/search?q="
+ name
+ "' target='_blank' />");
urlMap.put(
"linkedin",
"<a class='fa fa-linkedin' href='https://www.linkedin.com/company/"
+ name
+ "' target='_blank' />");
if (typeSelect == 2) {
urlMap.put(
"linkedin",
"<a class='fa fa-linkedin' href='http://www.linkedin.com/pub/dir/"
+ name.replace("+", "/")
+ "' target='_blank' />");
}
urlMap.put(
"youtube",
"<a class='fa fa-youtube' href='https://www.youtube.com/results?search_query="
+ name
+ "' target='_blank' />");
return urlMap;
}
@Override
public List<Long> findPartnerMails(Partner partner) {
List<Long> idList = new ArrayList<Long>();
idList.addAll(this.findMailsFromPartner(partner));
Set<Partner> contactSet = partner.getContactPartnerSet();
if (contactSet != null && !contactSet.isEmpty()) {
for (Partner contact : contactSet) {
idList.addAll(this.findMailsFromPartner(contact));
}
}
return idList;
}
@Override
public List<Long> findContactMails(Partner partner) {
List<Long> idList = new ArrayList<Long>();
idList.addAll(this.findMailsFromPartner(partner));
return idList;
}
@SuppressWarnings("unchecked")
@Override
public List<Long> findMailsFromPartner(Partner partner) {
String query =
"SELECT DISTINCT(email.id) FROM Message as email WHERE email.mediaTypeSelect = 2 AND "
+ "(email.relatedTo1Select = 'com.axelor.apps.base.db.Partner' AND email.relatedTo1SelectId = "
+ partner.getId()
+ ") "
+ "OR (email.relatedTo2Select = 'com.axelor.apps.base.db.Partner' AND email.relatedTo2SelectId = "
+ partner.getId()
+ ")";
if (partner.getEmailAddress() != null) {
query += "OR (email.fromEmailAddress.id = " + partner.getEmailAddress().getId() + ")";
}
return JPA.em().createQuery(query).getResultList();
}
private PartnerAddress createPartnerAddress(Address address, Boolean isDefault) {
PartnerAddress partnerAddress = new PartnerAddress();
partnerAddress.setAddress(address);
partnerAddress.setIsDefaultAddr(isDefault);
return partnerAddress;
}
@Transactional
@Override
public void resetDefaultAddress(Partner partner, String addrTypeQuery) {
if (partner.getId() != null) {
PartnerAddressRepository partnerAddressRepo = Beans.get(PartnerAddressRepository.class);
PartnerAddress partnerAddress =
partnerAddressRepo
.all()
.filter(
"self.partner.id = ? AND self.isDefaultAddr = true" + addrTypeQuery,
partner.getId())
.fetchOne();
if (partnerAddress != null) {
partnerAddress.setIsDefaultAddr(false);
partnerAddressRepo.save(partnerAddress);
}
}
}
@Override
public Partner addPartnerAddress(
Partner partner,
Address address,
Boolean isDefault,
Boolean isInvoicing,
Boolean isDelivery) {
PartnerAddress partnerAddress = createPartnerAddress(address, isDefault);
if (isDefault != null && isDefault) {
LOG.debug("Add partner address : isDelivery = {}", isDelivery);
LOG.debug("Add partner address : isInvoicing = {}", isInvoicing);
String query =
String.format(
" AND self.isDeliveryAddr = %s AND self.isInvoicingAddr = %s",
isDelivery, isInvoicing);
resetDefaultAddress(partner, query);
}
partnerAddress.setIsInvoicingAddr(isInvoicing);
partnerAddress.setIsDeliveryAddr(isDelivery);
partnerAddress.setIsDefaultAddr(isDefault);
partner.addPartnerAddressListItem(partnerAddress);
return partner;
}
@Override
public void addContactToPartner(Partner contact) {
if (contact.getMainPartner() != null) {
Partner partner = contact.getMainPartner();
partner.addContactPartnerSetItem(contact);
savePartner(partner);
}
}
protected Address getAddress(Partner partner, String querySpecific, String queryComman) {
if (partner != null) {
PartnerAddressRepository partnerAddressRepo = Beans.get(PartnerAddressRepository.class);
List<PartnerAddress> partnerAddressList =
partnerAddressRepo.all().filter(querySpecific, partner.getId()).fetch();
if (partnerAddressList.isEmpty()) {
partnerAddressList = partnerAddressRepo.all().filter(queryComman, partner.getId()).fetch();
if (partnerAddressList.isEmpty()) {
partnerAddressList =
partnerAddressRepo.all().filter("self.partner.id = ?1", partner.getId()).fetch();
}
}
if (partnerAddressList.size() == 1) {
return partnerAddressList.get(0).getAddress();
}
for (PartnerAddress partnerAddress : partnerAddressList) {
if (partnerAddress.getIsDefaultAddr()) {
return partnerAddress.getAddress();
}
}
}
return null;
}
@Override
public Address getInvoicingAddress(Partner partner) {
return getAddress(
partner,
"self.partner.id = ?1 AND self.isInvoicingAddr = true AND self.isDeliveryAddr = false AND self.isDefaultAddr = true",
"self.partner.id = ?1 AND self.isInvoicingAddr = true");
}
@Override
public Address getDeliveryAddress(Partner partner) {
return getAddress(
partner,
"self.partner.id = ?1 AND self.isDeliveryAddr = true AND self.isInvoicingAddr = false AND self.isDefaultAddr = true",
"self.partner.id = ?1 AND self.isDeliveryAddr = true");
}
@Override
public Address getDefaultAddress(Partner partner) {
return getAddress(
partner,
"self.partner.id = ?1 AND self.isDeliveryAddr = true AND self.isInvoicingAddr = true AND self.isDefaultAddr = true",
"self.partner.id = ?1 AND self.isDefaultAddr = true");
}
@Transactional
@Override
public Partner savePartner(Partner partner) {
return partnerRepo.save(partner);
}
@Override
public BankDetails getDefaultBankDetails(Partner partner) {
for (BankDetails bankDetails : partner.getBankDetailsList()) {
if (bankDetails.getIsDefault()) {
return bankDetails;
}
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
@Override
public String getSIRENNumber(Partner partner) throws AxelorException {
char[] Str = new char[9];
if (partner.getRegistrationCode() == null || partner.getRegistrationCode().isEmpty()) {
throw new AxelorException(
partner,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PARTNER_2),
I18n.get(IExceptionMessage.EXCEPTION),
partner.getName());
} else {
String registrationCode = partner.getRegistrationCode();
// remove whitespace in the registration code before using it
registrationCode.replaceAll("\\s", "").getChars(0, 9, Str, 0);
}
return new String(Str);
}
@Transactional
@Override
public void convertToIndividualPartner(Partner partner) {
partner.setIsContact(false);
partner.setPartnerTypeSelect(PARTNER_TYPE_INDIVIDUAL);
addPartnerAddress(partner, partner.getMainAddress(), true, false, false);
partner.setMainAddress(null);
}
public boolean isThereDuplicatePartner(Partner partner) {
return isThereDuplicatePartnerQuery(partner, false) != null;
}
/**
* Search for the sale price list for the current date in the partner.
*
* @param partner
* @return the sale price list for the partner null if no active price list has been found
*/
@Override
public PriceList getSalePriceList(Partner partner) {
PartnerPriceList partnerPriceList = partner.getSalePartnerPriceList();
if (partnerPriceList == null) {
return null;
}
Set<PriceList> priceListSet = partnerPriceList.getPriceListSet();
if (priceListSet == null) {
return null;
}
LocalDate today = Beans.get(AppBaseService.class).getTodayDate();
List<PriceList> candidatePriceListList = new ArrayList<>();
for (PriceList priceList : priceListSet) {
LocalDate beginDate =
priceList.getApplicationBeginDate() != null
? priceList.getApplicationBeginDate()
: LocalDate.MIN;
LocalDate endDate =
priceList.getApplicationEndDate() != null
? priceList.getApplicationEndDate()
: LocalDate.MAX;
if (beginDate.compareTo(today) <= 0 && today.compareTo(endDate) <= 0) {
candidatePriceListList.add(priceList);
}
}
// if we found multiple price list, then the user will have to select one
if (candidatePriceListList.size() == 1) {
return candidatePriceListList.get(0);
} else {
return null;
}
}
/**
* Get the partner language code. If null, return the default partner language.
*
* @param partner
* @return
*/
@Override
public String getPartnerLanguageCode(Partner partner) {
String locale = null;
if (partner != null && partner.getLanguage() != null) {
locale = partner.getLanguage().getCode();
}
if (!Strings.isNullOrEmpty(locale)) {
return locale;
}
return Beans.get(AppBaseService.class).getDefaultPartnerLanguageCode();
}
/**
* Normalize phone number.
*
* @param phoneNumber
* @return
*/
@Override
public String normalizePhoneNumber(String phoneNumber) {
return StringUtils.isBlank(phoneNumber) ? null : phoneNumber.replaceAll("\\s|\\.|-", "");
}
/**
* Check phone number.
*
* @param phoneNumber
* @return
*/
@Override
public boolean checkPhoneNumber(String phoneNumber) {
return StringUtils.isBlank(phoneNumber)
? false
: phoneNumberPattern.matcher(phoneNumber).matches();
}
/**
* Get phone number field name.
*
* @param actionName
* @return
*/
@Override
public String getPhoneNumberFieldName(String actionName) {
Preconditions.checkNotNull(actionName, I18n.get("Action name cannot be null."));
return actionName.substring(actionName.lastIndexOf('-') + 1);
}
@Override
public void setCompanyStr(Partner partner) {
partner.setCompanyStr(this.computeCompanyStr(partner));
}
@Override
public String computeCompanyStr(Partner partner) {
String companyStr = "";
if (partner.getCompanySet() != null && partner.getCompanySet().size() > 0) {
for (Company company : partner.getCompanySet()) {
companyStr += company.getCode() + ",";
}
return companyStr.substring(0, companyStr.length() - 1);
}
return null;
}
@Override
public String getPartnerDomain(Partner partner) {
String domain = "";
if (partner != null) {
if (partner.getCurrency() != null) {
domain += String.format(" AND self.currency.id = %d", partner.getCurrency().getId());
}
if (partner.getSalePartnerPriceList() != null) {
domain +=
String.format(
" AND self.salePartnerPriceList.id = %s",
partner.getSalePartnerPriceList().getId());
}
if (partner.getFiscalPosition() != null) {
domain +=
String.format(" AND self.fiscalPosition.id = %s", partner.getFiscalPosition().getId());
}
}
return domain;
}
public Partner isThereDuplicatePartnerInArchive(Partner partner) {
return isThereDuplicatePartnerQuery(partner, true);
}
protected Partner isThereDuplicatePartnerQuery(Partner partner, boolean isInArchived) {
String newName = this.computeSimpleFullName(partner);
if (Strings.isNullOrEmpty(newName)) {
return null;
}
Long partnerId = partner.getId();
String filter =
"lower(self.simpleFullName) = lower(:newName) "
+ "and self.partnerTypeSelect = :_partnerTypeSelect ";
if (partner != null) {
filter += "and self.id != :partnerId ";
}
if (isInArchived) {
filter += "and self.archived = true ";
} else {
filter += "and ( self.archived != true OR self.archived is null ) ";
}
Query<Partner> partnerQuery =
partnerRepo
.all()
.filter(filter)
.bind("newName", newName)
.bind("_partnerTypeSelect", partner.getPartnerTypeSelect());
if (partner != null) {
partnerQuery = partnerQuery.bind("partnerId", partnerId);
}
return partnerQuery.fetchOne();
}
public void setPartnerBarCodeSeq(Partner partner) {
if (partner.getBarCodeSeq() == null) {
try {
boolean addPadding = false;
InputStream inStream = null;
inStream =
barcodeGeneratorService.createBarCode(
partner.getPartnerSeq(),
appBaseService.getAppBase().getBarcodeTypeConfigPartnerSeq(),
addPadding);
if (inStream != null) {
MetaFile barcodeFile =
metaFiles.upload(inStream, String.format("PartnerBarCodeSeq%d.png", partner.getId()));
partner.setBarCodeSeq(barcodeFile);
}
} catch (IOException e) {
e.printStackTrace();
} catch (AxelorException e) {
throw new ValidationException(e.getMessage());
}
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.time.LocalDate;
public interface PeriodService {
/**
* Recupère la bonne période pour la date passée en paramètre
*
* @param date
* @param company
* @return
* @throws AxelorException
*/
public Period getActivePeriod(LocalDate date, Company company, int typeSelect)
throws AxelorException;
public Period getPeriod(LocalDate date, Company company, int typeSelect);
public Period getNextPeriod(Period period) throws AxelorException;
public void testOpenPeriod(Period period) throws AxelorException;
public void close(Period period) throws AxelorException;
@Transactional
public void adjust(Period period);
/**
* Check if the period corresponding to the date and the company is closed
*
* @param company
* @param date
* @throws AxelorException
*/
public void checkPeriod(Company company, LocalDate date) throws AxelorException;
/**
* Check if the periods corresponding to the dates and the company are closed.
*
* @param company
* @param fromDate
* @param toDate
*/
public void checkPeriod(Company company, LocalDate fromDate, LocalDate toDate)
throws AxelorException;
/**
* @param period
* @throws AxelorException if the period is closed
*/
public void checkPeriod(Period period) throws AxelorException;
}

View File

@ -0,0 +1,201 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.repo.PeriodRepository;
import com.axelor.apps.base.db.repo.YearRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class PeriodServiceImpl implements PeriodService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected PeriodRepository periodRepo;
protected AdjustHistoryService adjustHistoryService;
@Inject
public PeriodServiceImpl(PeriodRepository periodRepo, AdjustHistoryService adjustHistoryService) {
this.periodRepo = periodRepo;
this.adjustHistoryService = adjustHistoryService;
}
/**
* Fetches the active period with the date, company and type in parameter
*
* @param date
* @param company
* @param typeSelect
* @return
* @throws AxelorException
*/
public Period getActivePeriod(LocalDate date, Company company, int typeSelect)
throws AxelorException {
Period period = this.getPeriod(date, company, typeSelect);
if (period == null || (period.getStatusSelect() == PeriodRepository.STATUS_CLOSED)) {
String dateStr = date != null ? date.toString() : "";
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PERIOD_1),
company.getName(),
dateStr);
}
LOG.debug("Period : {}", period);
return period;
}
public Period getPeriod(LocalDate date, Company company, int typeSelect) {
return periodRepo
.all()
.filter(
"self.year.company = ?1 and self.fromDate <= ?2 and self.toDate >= ?2 and self.year.typeSelect = ?3",
company,
date,
typeSelect)
.fetchOne();
}
public Period getNextPeriod(Period period) throws AxelorException {
Period nextPeriod =
periodRepo
.all()
.filter(
"self.fromDate > ?1 AND self.year.company = ?2 AND self.statusSelect = ?3",
period.getToDate(),
period.getYear().getCompany(),
PeriodRepository.STATUS_OPENED)
.fetchOne();
if (nextPeriod == null || nextPeriod.getStatusSelect() == PeriodRepository.STATUS_CLOSED) {
throw new AxelorException(
period,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PERIOD_1),
period.getYear().getCompany().getName());
}
LOG.debug("Next Period : {}", nextPeriod);
return nextPeriod;
}
public void testOpenPeriod(Period period) throws AxelorException {
if (period.getStatusSelect() == PeriodRepository.STATUS_CLOSED) {
throw new AxelorException(
period,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PERIOD_2));
}
}
public void close(Period period) throws AxelorException {
if (period.getStatusSelect() == PeriodRepository.STATUS_ADJUSTING) {
adjustHistoryService.setEndDate(period);
}
this.updateClosePeriod(period);
}
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
protected void updateClosePeriod(Period period) {
period.setStatusSelect(PeriodRepository.STATUS_CLOSED);
period.setClosureDateTime(LocalDateTime.now());
periodRepo.save(period);
}
@Transactional
public void adjust(Period period) {
period = periodRepo.find(period.getId());
adjustHistoryService.setStartDate(period);
period.setStatusSelect(PeriodRepository.STATUS_ADJUSTING);
periodRepo.save(period);
}
/**
* Check if the period corresponding to the date and the company is closed
*
* @param company
* @param date
* @throws AxelorException
*/
public void checkPeriod(Company company, LocalDate date) throws AxelorException {
this.checkPeriod(company, date, date);
}
/**
* Check if the periods corresponding to the dates and the company are closed.
*
* @param company
* @param fromDate
* @param toDate
*/
public void checkPeriod(Company company, LocalDate fromDate, LocalDate toDate)
throws AxelorException {
List<Period> periodList =
periodRepo
.all()
.filter(
"self.year.typeSelect = :_typeSelect "
+ "AND self.year.company = :_company "
+ "AND ((self.fromDate <= :_fromDate "
+ "AND self.toDate >= :_fromDate) "
+ "OR (self.fromDate <= :_toDate "
+ "AND self.toDate >= :_toDate))")
.bind("_typeSelect", YearRepository.TYPE_PAYROLL)
.bind("_company", company)
.bind("_fromDate", fromDate)
.bind("_toDate", toDate)
.fetch();
if (periodList == null) {
return;
}
for (Period period : periodList) {
checkPeriod(period);
}
}
/**
* @param period
* @throws AxelorException if the period is closed
*/
public void checkPeriod(Period period) throws AxelorException {
if (period != null && period.getStatusSelect() == PeriodRepository.STATUS_CLOSED) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.PAY_PERIOD_CLOSED),
period.getName());
}
}
}

View File

@ -0,0 +1,266 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.base.db.PriceListLine;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.AppBaseRepository;
import com.axelor.apps.base.db.repo.PriceListLineRepository;
import com.axelor.apps.base.db.repo.PriceListRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PriceListService {
@Inject private PriceListLineRepository priceListLineRepo;
@Inject private PriceListRepository priceListRepo;
@Inject protected AppBaseService appBaseService;
public PriceListLine getPriceListLine(
Product product, BigDecimal qty, PriceList priceList, BigDecimal price) {
PriceListLine priceListLine = null;
List<PriceListLine> priceListLineList = null;
BigDecimal tempDiscountPrevious = null;
BigDecimal tempDiscountCurrent = null;
if (product != null && priceList != null) {
priceListLineList =
Beans.get(PriceListLineRepository.class)
.all()
.filter(
"self.product = ?1 AND self.minQty <= ?2 AND self.priceList.id = ?3 ORDER BY self.minQty DESC",
product,
qty,
priceList.getId())
.fetch();
if ((priceListLineList == null || priceListLineList.isEmpty())
&& product.getProductCategory() != null) {
priceListLineList =
priceListLineRepo
.all()
.filter(
"self.productCategory = ?1 AND self.minQty <= ?2 AND self.priceList.id = ?3 ORDER BY self.minQty DESC",
product.getProductCategory(),
qty,
priceList.getId())
.fetch();
}
}
if (priceListLineList != null && !priceListLineList.isEmpty()) {
if (priceListLineList.size() > 1) {
for (PriceListLine tempPriceListLine : priceListLineList) {
tempDiscountCurrent = this.getUnitPriceDiscounted(tempPriceListLine, price);
if (tempDiscountPrevious == null
|| tempDiscountPrevious.compareTo(tempDiscountCurrent) == 1) {
tempDiscountPrevious = tempDiscountCurrent;
priceListLine = tempPriceListLine;
}
}
} else {
priceListLine = priceListLineList.get(0);
}
}
return priceListLine;
}
public int getDiscountTypeSelect(PriceListLine priceListLine) {
return priceListLine.getAmountTypeSelect();
}
public BigDecimal getDiscountAmount(PriceListLine priceListLine, BigDecimal unitPrice) {
switch (priceListLine.getTypeSelect()) {
case PriceListLineRepository.TYPE_ADDITIONNAL:
return priceListLine.getAmount().negate();
case PriceListLineRepository.TYPE_DISCOUNT:
return priceListLine.getAmount();
case PriceListLineRepository.TYPE_REPLACE:
return unitPrice.subtract(priceListLine.getAmount());
default:
return BigDecimal.ZERO;
}
}
public BigDecimal getUnitPriceDiscounted(PriceListLine priceListLine, BigDecimal unitPrice) {
switch (priceListLine.getTypeSelect()) {
case PriceListLineRepository.TYPE_ADDITIONNAL:
if (priceListLine.getAmountTypeSelect() == PriceListLineRepository.AMOUNT_TYPE_FIXED) {
return unitPrice.add(priceListLine.getAmount());
} else if (priceListLine.getAmountTypeSelect()
== PriceListLineRepository.AMOUNT_TYPE_PERCENT) {
return unitPrice.multiply(
BigDecimal.ONE.add(priceListLine.getAmount().divide(new BigDecimal(100))));
}
case PriceListLineRepository.TYPE_DISCOUNT:
if (priceListLine.getAmountTypeSelect() == PriceListLineRepository.AMOUNT_TYPE_FIXED) {
return unitPrice.subtract(priceListLine.getAmount());
} else if (priceListLine.getAmountTypeSelect()
== PriceListLineRepository.AMOUNT_TYPE_PERCENT) {
return unitPrice.multiply(
BigDecimal.ONE.subtract(priceListLine.getAmount().divide(new BigDecimal(100))));
}
case PriceListLineRepository.TYPE_REPLACE:
return priceListLine.getAmount();
default:
return unitPrice;
}
}
public BigDecimal getUnitPriceDiscounted(PriceList priceList, BigDecimal unitPrice) {
BigDecimal discountPercent = priceList.getGeneralDiscount();
return unitPrice.multiply(BigDecimal.ONE.subtract(discountPercent.divide(new BigDecimal(100))));
}
public BigDecimal computeDiscount(
BigDecimal unitPrice, int discountTypeSelect, BigDecimal discountAmount) {
if (discountTypeSelect == PriceListLineRepository.AMOUNT_TYPE_FIXED) {
return unitPrice
.subtract(discountAmount)
.setScale(appBaseService.getNbDecimalDigitForSalePrice(), RoundingMode.HALF_UP);
} else if (discountTypeSelect == PriceListLineRepository.AMOUNT_TYPE_PERCENT) {
return unitPrice
.multiply(new BigDecimal(100).subtract(discountAmount))
.divide(
new BigDecimal(100),
appBaseService.getNbDecimalDigitForSalePrice(),
RoundingMode.HALF_UP);
}
return unitPrice;
}
public Map<String, Object> getReplacedPriceAndDiscounts(
PriceList priceList, PriceListLine priceListLine, BigDecimal price) {
int discountTypeSelect = 0;
if (priceListLine != null) {
discountTypeSelect = priceListLine.getTypeSelect();
}
Map<String, Object> discounts = getDiscounts(priceList, priceListLine, price);
if (discounts != null) {
int computeMethodDiscountSelect =
appBaseService.getAppBase().getComputeMethodDiscountSelect();
if ((computeMethodDiscountSelect == AppBaseRepository.INCLUDE_DISCOUNT_REPLACE_ONLY
&& discountTypeSelect == PriceListLineRepository.TYPE_REPLACE)
|| computeMethodDiscountSelect == AppBaseRepository.INCLUDE_DISCOUNT) {
price =
computeDiscount(
price,
(int) discounts.get("discountTypeSelect"),
(BigDecimal) discounts.get("discountAmount"));
discounts.put("price", price);
discounts.put("discountTypeSelect", PriceListLineRepository.AMOUNT_TYPE_NONE);
discounts.put("discountAmount", BigDecimal.ZERO);
}
}
return discounts;
}
public Map<String, Object> getDiscounts(
PriceList priceList, PriceListLine priceListLine, BigDecimal price) {
Map<String, Object> discounts = new HashMap<>();
if (priceListLine != null) {
discounts.put(
"discountAmount",
this.getDiscountAmount(priceListLine, price)
.setScale(appBaseService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_UP));
discounts.put("discountTypeSelect", this.getDiscountTypeSelect(priceListLine));
} else {
BigDecimal discountAmount =
priceList
.getGeneralDiscount()
.setScale(appBaseService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_UP);
discounts.put("discountAmount", discountAmount);
if (discountAmount.compareTo(BigDecimal.ZERO) == 0) {
discounts.put("discountTypeSelect", PriceListLineRepository.AMOUNT_TYPE_NONE);
} else {
discounts.put("discountTypeSelect", PriceListLineRepository.AMOUNT_TYPE_PERCENT);
}
}
return discounts;
}
@Transactional
public PriceList historizePriceList(PriceList priceList) {
PriceList historizedPriceList = priceListRepo.copy(priceList, false);
historizedPriceList.setIsActive(false);
List<PriceListLine> priceListLineList = priceList.getPriceListLineList();
for (PriceListLine priceListLine : priceListLineList) {
PriceListLine newPriceListLine = priceListLineRepo.copy(priceListLine, false);
newPriceListLine.setPriceList(null);
historizedPriceList.addPriceListLineListItem(newPriceListLine);
}
priceListRepo.save(historizedPriceList);
priceList.addHistorizedPriceListItem(historizedPriceList);
priceListRepo.save(priceList);
return priceList;
}
/**
* Check applicationBeginDate and applicationEndDate
*
* @param priceList
* @throws AxelorException if the two dates are not an interval
*/
public void checkDates(PriceList priceList) throws AxelorException {
LocalDate beginDate = priceList.getApplicationBeginDate();
LocalDate endDate = priceList.getApplicationEndDate();
if (beginDate == null || endDate == null) {
return;
}
if (beginDate.compareTo(endDate) > 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRICE_LIST_DATE_WRONG_ORDER));
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.ProductMultipleQty;
import com.axelor.rpc.ActionResponse;
import java.math.BigDecimal;
import java.util.List;
public interface ProductMultipleQtyService {
public boolean checkMultipleQty(BigDecimal qty, List<ProductMultipleQty> productMultipleQties);
public String toStringMultipleQty(List<ProductMultipleQty> productMultipleQties);
public void checkMultipleQty(
BigDecimal qty,
List<ProductMultipleQty> productMultipleQties,
boolean allowToForce,
ActionResponse response);
}

View File

@ -0,0 +1,102 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.ProductMultipleQty;
import com.axelor.apps.tool.ContextTool;
import com.axelor.i18n.I18n;
import com.axelor.rpc.ActionResponse;
import com.google.common.base.Strings;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductMultipleQtyServiceImpl implements ProductMultipleQtyService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public boolean checkMultipleQty(BigDecimal qty, List<ProductMultipleQty> productMultipleQties) {
if (productMultipleQties.size() == 0) {
return true;
}
for (ProductMultipleQty productMultipleQty : productMultipleQties) {
if (productMultipleQty.getMultipleQty().compareTo(BigDecimal.ZERO) != 0) {
LOG.debug(
"Check on multiple qty : {}, Modulo : {}",
qty,
qty.remainder(productMultipleQty.getMultipleQty()));
if (qty.remainder(productMultipleQty.getMultipleQty()).compareTo(BigDecimal.ZERO) == 0) {
return true;
}
}
}
return false;
}
public String toStringMultipleQty(List<ProductMultipleQty> productMultipleQties) {
String message = "";
for (ProductMultipleQty productMultipleQty : productMultipleQties) {
if (message.length() > 0) {
message += " " + I18n.get("or") + " ";
}
message += productMultipleQty.getMultipleQty();
if (!Strings.isNullOrEmpty(productMultipleQty.getName())) {
message += " (" + productMultipleQty.getName() + ")";
}
}
return message;
}
public void checkMultipleQty(
BigDecimal qty,
List<ProductMultipleQty> productMultipleQties,
boolean allowToForce,
ActionResponse response) {
boolean isMultiple = this.checkMultipleQty(qty, productMultipleQties);
if (isMultiple) {
response.setAttr("multipleQtyNotRespectedLabel", "hidden", true);
response.setValue("$qtyValid", true);
} else {
String spanClass =
allowToForce ? ContextTool.SPAN_CLASS_WARNING : ContextTool.SPAN_CLASS_IMPORTANT;
String message =
String.format(
I18n.get("Quantity should be a multiple of %s"),
this.toStringMultipleQty(productMultipleQties));
String title = ContextTool.formatLabel(message, spanClass, 75);
response.setAttr("multipleQtyNotRespectedLabel", "title", title);
response.setAttr("multipleQtyNotRespectedLabel", "hidden", false);
response.setValue("$qtyValid", allowToForce);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.ProductVariant;
import com.axelor.apps.base.db.ProductVariantConfig;
import com.axelor.apps.base.db.ProductVariantValue;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
public interface ProductService {
@Transactional
public void updateProductPrice(Product product);
public String getSequence() throws AxelorException;
/**
* Retourne le prix d'un produit à une date t.
*
* @param product
* @param date
* @return
*/
public BigDecimal getPrice(Product product, boolean isPurchase);
public void updateSalePrice(Product product);
@Transactional
public void generateProductVariants(Product productModel);
public Product createProduct(Product productModel, ProductVariant productVariant, int seq);
/**
* @param productVariant
* @param applicationPriceSelect - 1 : Sale price - 2 : Cost price
* @return
*/
public BigDecimal getProductExtraPrice(ProductVariant productVariant, int applicationPriceSelect);
public ProductVariant createProductVariant(
ProductVariantConfig productVariantConfig,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2,
ProductVariantValue productVariantValue3,
ProductVariantValue productVariantValue4);
public void copyProduct(Product product, Product copy);
}

View File

@ -0,0 +1,429 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.ProductVariant;
import com.axelor.apps.base.db.ProductVariantAttr;
import com.axelor.apps.base.db.ProductVariantConfig;
import com.axelor.apps.base.db.ProductVariantValue;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.ProductVariantRepository;
import com.axelor.apps.base.db.repo.ProductVariantValueRepository;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.meta.MetaFiles;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
public class ProductServiceImpl implements ProductService {
protected ProductVariantService productVariantService;
protected ProductVariantRepository productVariantRepo;
protected SequenceService sequenceService;
protected AppBaseService appBaseService;
protected ProductRepository productRepo;
@Inject
public ProductServiceImpl(
ProductVariantService productVariantService,
ProductVariantRepository productVariantRepo,
SequenceService sequenceService,
AppBaseService appBaseService,
ProductRepository productRepo) {
this.productVariantService = productVariantService;
this.productVariantRepo = productVariantRepo;
this.sequenceService = sequenceService;
this.appBaseService = appBaseService;
this.productRepo = productRepo;
}
@Inject private MetaFiles metaFiles;
@Override
@Transactional
public void updateProductPrice(Product product) {
this.updateSalePrice(product);
productRepo.save(product);
}
public String getSequence() throws AxelorException {
String seq = sequenceService.getSequenceNumber(SequenceRepository.PRODUCT);
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCT_NO_SEQUENCE));
}
return seq;
}
/**
* Retourne le prix d'un produit à une date t.
*
* @param product
* @param date
* @return
*/
@Override
public BigDecimal getPrice(Product product, boolean isPurchase) {
if (isPurchase) {
return product.getPurchasePrice();
} else {
return product.getSalePrice();
}
}
@Override
public void updateSalePrice(Product product) {
BigDecimal managePriceCoef = product.getManagPriceCoef();
if (product.getCostPrice() != null) {
if (product.getProductVariant() != null) {
product.setCostPrice(
product
.getCostPrice()
.add(
this.getProductExtraPrice(
product.getProductVariant(),
ProductVariantValueRepository.APPLICATION_COST_PRICE)));
}
}
if (product.getCostPrice() != null && managePriceCoef != null) {
product.setSalePrice(
(product.getCostPrice().multiply(managePriceCoef))
.setScale(appBaseService.getNbDecimalDigitForUnitPrice(), BigDecimal.ROUND_HALF_UP));
if (product.getProductVariant() != null) {
product.setSalePrice(
product
.getSalePrice()
.add(
this.getProductExtraPrice(
product.getProductVariant(),
ProductVariantValueRepository.APPLICATION_SALE_PRICE)));
}
}
if (product.getProductVariantConfig() != null && product.getManageVariantPrice()) {
this.updateSalePriceOfVariant(product);
}
}
private void updateSalePriceOfVariant(Product product) {
List<? extends Product> productVariantList =
productRepo.all().filter("self.parentProduct = ?1", product).fetch();
for (Product productVariant : productVariantList) {
productVariant.setCostPrice(product.getCostPrice());
productVariant.setSalePrice(product.getSalePrice());
productVariant.setManagPriceCoef(product.getManagPriceCoef());
this.updateSalePrice(productVariant);
}
}
@Override
@Transactional
public void generateProductVariants(Product productModel) {
List<ProductVariant> productVariantList =
this.getProductVariantList(productModel.getProductVariantConfig());
int seq = 1;
for (ProductVariant productVariant : productVariantList) {
productVariantRepo.save(productVariant);
productRepo.save(this.createProduct(productModel, productVariant, seq++));
}
}
@Override
public Product createProduct(Product productModel, ProductVariant productVariant, int seq) {
String description = "";
String internalDescription = "";
if (productModel.getDescription() != null) {
description = productModel.getDescription();
}
if (productModel.getInternalDescription() != null) {
internalDescription = productModel.getInternalDescription();
}
description += "<br>" + productVariant.getName();
internalDescription += "<br>" + productVariant.getName();
Product product =
new Product(
productModel.getName() + " (" + productVariant.getName() + ")",
productModel.getCode() + "-" + seq,
description,
internalDescription,
productModel.getPicture(),
productModel.getProductCategory(),
productModel.getProductFamily(),
productModel.getUnit(),
productModel.getSaleSupplySelect(),
productModel.getProductTypeSelect(),
productModel.getProcurementMethodSelect(),
productModel.getSaleCurrency(),
productModel.getPurchaseCurrency(),
productModel.getStartDate(),
productModel.getEndDate());
productModel.setIsModel(true);
product.setIsModel(false);
product.setParentProduct(productModel);
product.setProductVariant(productVariant);
product.setCostPrice(productModel.getCostPrice());
product.setSalePrice(productModel.getSalePrice());
product.setManagPriceCoef(productModel.getManagPriceCoef());
this.updateSalePrice(product);
return product;
}
/**
* @param productVariant
* @param applicationPriceSelect - 1 : Sale price - 2 : Cost price
* @return
*/
@Override
public BigDecimal getProductExtraPrice(
ProductVariant productVariant, int applicationPriceSelect) {
BigDecimal extraPrice = BigDecimal.ZERO;
ProductVariantValue productVariantValue1 = productVariant.getProductVariantValue1();
ProductVariantValue productVariantValue2 = productVariant.getProductVariantValue2();
ProductVariantValue productVariantValue3 = productVariant.getProductVariantValue3();
ProductVariantValue productVariantValue4 = productVariant.getProductVariantValue4();
if (productVariantValue1 != null
&& productVariantValue1.getApplicationPriceSelect() == applicationPriceSelect) {
extraPrice = extraPrice.add(productVariantValue1.getPriceExtra());
}
if (productVariantValue2 != null) {
extraPrice = extraPrice.add(productVariantValue2.getPriceExtra());
}
if (productVariantValue3 != null) {
extraPrice = extraPrice.add(productVariantValue3.getPriceExtra());
}
if (productVariantValue4 != null) {
extraPrice = extraPrice.add(productVariantValue4.getPriceExtra());
}
return extraPrice;
}
private List<ProductVariant> getProductVariantList(ProductVariantConfig productVariantConfig) {
List<ProductVariant> productVariantList = Lists.newArrayList();
if (productVariantConfig.getProductVariantAttr1() != null
&& productVariantConfig.getProductVariantValue1Set() != null) {
for (ProductVariantValue productVariantValue1 :
productVariantConfig.getProductVariantValue1Set()) {
productVariantList.addAll(
this.getProductVariantList(productVariantConfig, productVariantValue1));
}
}
return productVariantList;
}
private List<ProductVariant> getProductVariantList(
ProductVariantConfig productVariantConfig, ProductVariantValue productVariantValue1) {
List<ProductVariant> productVariantList = Lists.newArrayList();
if (productVariantConfig.getProductVariantAttr2() != null
&& productVariantConfig.getProductVariantValue2Set() != null) {
for (ProductVariantValue productVariantValue2 :
productVariantConfig.getProductVariantValue2Set()) {
productVariantList.addAll(
this.getProductVariantList(
productVariantConfig, productVariantValue1, productVariantValue2));
}
} else {
productVariantList.add(
this.createProductVariant(productVariantConfig, productVariantValue1, null, null, null));
}
return productVariantList;
}
private List<ProductVariant> getProductVariantList(
ProductVariantConfig productVariantConfig,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2) {
List<ProductVariant> productVariantList = Lists.newArrayList();
if (productVariantConfig.getProductVariantAttr3() != null
&& productVariantConfig.getProductVariantValue3Set() != null) {
for (ProductVariantValue productVariantValue3 :
productVariantConfig.getProductVariantValue3Set()) {
productVariantList.addAll(
this.getProductVariantList(
productVariantConfig,
productVariantValue1,
productVariantValue2,
productVariantValue3));
}
} else {
productVariantList.add(
this.createProductVariant(
productVariantConfig, productVariantValue1, productVariantValue2, null, null));
}
return productVariantList;
}
private List<ProductVariant> getProductVariantList(
ProductVariantConfig productVariantConfig,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2,
ProductVariantValue productVariantValue3) {
List<ProductVariant> productVariantList = Lists.newArrayList();
if (productVariantConfig.getProductVariantAttr4() != null
&& productVariantConfig.getProductVariantValue4Set() != null) {
for (ProductVariantValue productVariantValue4 :
productVariantConfig.getProductVariantValue4Set()) {
productVariantList.add(
this.createProductVariant(
productVariantConfig,
productVariantValue1,
productVariantValue2,
productVariantValue3,
productVariantValue4));
}
} else {
productVariantList.add(
this.createProductVariant(
productVariantConfig,
productVariantValue1,
productVariantValue2,
productVariantValue3,
null));
}
return productVariantList;
}
@Override
public ProductVariant createProductVariant(
ProductVariantConfig productVariantConfig,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2,
ProductVariantValue productVariantValue3,
ProductVariantValue productVariantValue4) {
ProductVariantAttr productVariantAttr1 = null,
productVariantAttr2 = null,
productVariantAttr3 = null,
productVariantAttr4 = null;
if (productVariantValue1 != null) {
productVariantAttr1 = productVariantConfig.getProductVariantAttr1();
}
if (productVariantValue2 != null) {
productVariantAttr2 = productVariantConfig.getProductVariantAttr2();
}
if (productVariantValue3 != null) {
productVariantAttr3 = productVariantConfig.getProductVariantAttr3();
}
if (productVariantValue4 != null) {
productVariantAttr4 = productVariantConfig.getProductVariantAttr4();
}
return productVariantService.createProductVariant(
productVariantAttr1,
productVariantAttr2,
productVariantAttr3,
productVariantAttr4,
productVariantValue1,
productVariantValue2,
productVariantValue3,
productVariantValue4,
false);
}
public void copyProduct(Product product, Product copy) {
copy.setBarCode(null);
try {
if (product.getPicture() != null) {
File file = MetaFiles.getPath(product.getPicture()).toFile();
copy.setPicture(metaFiles.upload(file));
}
} catch (IOException e) {
e.printStackTrace();
}
copy.setStartDate(null);
copy.setEndDate(null);
copy.setCostPrice(BigDecimal.ZERO);
copy.setPurchasePrice(BigDecimal.ZERO);
}
}

View File

@ -0,0 +1,524 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.ProductVariant;
import com.axelor.apps.base.db.ProductVariantAttr;
import com.axelor.apps.base.db.ProductVariantValue;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.ProductVariantRepository;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductVariantService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ProductRepository productRepo;
protected ProductVariantRepository productVariantRepo;
@Inject
public ProductVariantService(
ProductRepository productRepo, ProductVariantRepository productVariantRepo) {
this.productRepo = productRepo;
this.productVariantRepo = productVariantRepo;
}
public ProductVariant createProductVariant(
ProductVariantAttr productVariantAttr1,
ProductVariantAttr productVariantAttr2,
ProductVariantAttr productVariantAttr3,
ProductVariantAttr productVariantAttr4,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2,
ProductVariantValue productVariantValue3,
ProductVariantValue productVariantValue4,
boolean usedForStock) {
ProductVariant productVariant = new ProductVariant();
productVariant.setProductVariantAttr1(productVariantAttr1);
productVariant.setProductVariantAttr2(productVariantAttr2);
productVariant.setProductVariantAttr3(productVariantAttr3);
productVariant.setProductVariantAttr4(productVariantAttr4);
productVariant.setProductVariantValue1(productVariantValue1);
productVariant.setProductVariantValue2(productVariantValue2);
productVariant.setProductVariantValue3(productVariantValue3);
productVariant.setProductVariantValue4(productVariantValue4);
productVariant.setUsedForStock(usedForStock);
return productVariant;
}
public ProductVariantValue createProductVariantValue(
ProductVariantAttr productVariantAttr, String code, String name, BigDecimal priceExtra) {
ProductVariantValue productVariantValue = new ProductVariantValue();
productVariantValue.setCode(code);
productVariantValue.setName(name);
productVariantValue.setPriceExtra(priceExtra);
productVariantValue.setProductVariantAttr(productVariantAttr);
return productVariantValue;
}
public ProductVariantAttr createProductVariantAttr(String name) {
ProductVariantAttr productVariantAttr = new ProductVariantAttr();
productVariantAttr.setName(name);
productVariantAttr.setProductVariantValueList(new ArrayList<ProductVariantValue>());
return productVariantAttr;
}
public boolean equalsName(ProductVariant productVariant1, ProductVariant productVariant2) {
if (productVariant1 != null
&& productVariant2 != null
&& productVariant1.getName().equals(productVariant2.getName())) {
return true;
}
return false;
}
public boolean equals(ProductVariant productVariant1, ProductVariant productVariant2) {
if (productVariant1 != null
&& productVariant2 != null
&& productVariant1.getProductVariantAttr1().equals(productVariant2.getProductVariantAttr1())
&& productVariant1.getProductVariantAttr2().equals(productVariant2.getProductVariantAttr2())
&& productVariant1.getProductVariantAttr3().equals(productVariant2.getProductVariantAttr3())
&& productVariant1.getProductVariantAttr4().equals(productVariant2.getProductVariantAttr4())
&& productVariant1
.getProductVariantValue1()
.equals(productVariant2.getProductVariantValue1())
&& productVariant1
.getProductVariantValue2()
.equals(productVariant2.getProductVariantValue2())
&& productVariant1
.getProductVariantValue3()
.equals(productVariant2.getProductVariantValue3())
&& productVariant1
.getProductVariantValue4()
.equals(productVariant2.getProductVariantValue4())) {
return true;
}
return false;
}
private ProductVariant getProductVariant(
ProductVariantAttr productVariantAttr1,
ProductVariantAttr productVariantAttr2,
ProductVariantAttr productVariantAttr3,
ProductVariantAttr productVariantAttr4,
ProductVariantValue productVariantValue1,
ProductVariantValue productVariantValue2,
ProductVariantValue productVariantValue3,
ProductVariantValue productVariantValue4,
boolean usedForStock) {
return productVariantRepo
.all()
.filter(
"self.productVariantAttr1 = ?1 AND self.productVariantAttr2 = ?2 AND self.productVariantAttr3 = ?3 AND "
+ "self.productVariantAttr4 = ?4 AND self.productVariantValue1 = ?5 AND self.productVariantValue2 = ?6 AND self.productVariantValue3 = ?7 AND "
+ "self.productVariantValue4 = ?8 AND self.usedForStock = 'true'",
productVariantAttr1,
productVariantAttr2,
productVariantAttr3,
productVariantAttr4,
productVariantValue1,
productVariantValue2,
productVariantValue3,
productVariantValue4,
usedForStock)
.fetchOne();
}
public ProductVariant copyProductVariant(ProductVariant productVariant, boolean usedForStock) {
return this.createProductVariant(
productVariant.getProductVariantAttr1(),
productVariant.getProductVariantAttr2(),
productVariant.getProductVariantAttr3(),
productVariant.getProductVariantAttr4(),
productVariant.getProductVariantValue1(),
productVariant.getProductVariantValue2(),
productVariant.getProductVariantValue3(),
productVariant.getProductVariantValue4(),
usedForStock);
}
public ProductVariant getStockProductVariant(ProductVariant productVariant) {
ProductVariant stockProductVariant =
this.getProductVariant(
productVariant.getProductVariantAttr1(),
productVariant.getProductVariantAttr2(),
productVariant.getProductVariantAttr3(),
productVariant.getProductVariantAttr4(),
productVariant.getProductVariantValue1(),
productVariant.getProductVariantValue2(),
productVariant.getProductVariantValue3(),
productVariant.getProductVariantValue4(),
true);
if (stockProductVariant == null) {
stockProductVariant = this.copyProductVariant(productVariant, true);
}
return stockProductVariant;
}
/**
* Méthode permettant de récupérer la déclinaison de produit en fonction des attributs du parent
* (produit fabriqué)
*
* @param parentProduct Le produit finis ou semi-finis
* @param productModel Le modele de produit (Produit consommé)
* @return La déclinaison de produit consommé
*/
public Product getProductVariant(Product parentProduct, Product productModel) {
ProductVariant productVariant = parentProduct.getProductVariant();
if (productVariant != null && productModel.getIsModel()) {
return this.getProductVariant(productVariant, productModel);
}
return productModel;
}
private Product getProductVariant(ProductVariant parentProductVariant, Product productSearched) {
LOG.debug(
"Recherche d'un variant du produit {} ayant des attributs communs avec {}",
productSearched.getCode(),
parentProductVariant.getName());
ProductVariantValue productVariantValue1 = parentProductVariant.getProductVariantValue1();
ProductVariantValue productVariantValue2 = parentProductVariant.getProductVariantValue2();
ProductVariantValue productVariantValue3 = parentProductVariant.getProductVariantValue3();
ProductVariantValue productVariantValue4 = parentProductVariant.getProductVariantValue4();
if (productVariantValue1 != null) {
LOG.debug(
"Recherche d'un variant de produit ayant au moins comme attributs {} : {}",
productVariantValue1.getProductVariantAttr().getCode(),
productVariantValue1.getCode());
List<? extends Product> productList =
productRepo
.all()
.filter(
"self.parentProduct = ?1 "
+ "AND ((self.productVariant.productVariantAttr1.code = ?2 AND self.productVariant.productVariantValue1.code = ?3) "
+ "OR (self.productVariant.productVariantAttr2.code = ?2 AND self.productVariant.productVariantValue2.code = ?3) "
+ "OR (self.productVariant.productVariantAttr3.code = ?2 AND self.productVariant.productVariantValue3.code = ?3) "
+ "OR (self.productVariant.productVariantAttr4.code = ?2 AND self.productVariant.productVariantValue4.code = ?3)) ",
productSearched,
productVariantValue1.getProductVariantAttr().getCode(),
productVariantValue1.getCode())
.fetch();
if (productList == null || productList.isEmpty()) {
return productSearched;
}
Product productFind = null;
int nbAttr = 0;
for (Product product : productList) {
if (productVariantValue1 != null
&& productVariantValue2 != null
&& productVariantValue3 != null
&& productVariantValue4 != null) {
// 4
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)
&& this.containsProductVariantValue(product, productVariantValue4)) {
LOG.debug(
"Variant de produit trouvé directement : {} avec l'ensemble des attributs (4) en commun",
product.getCode());
return product;
}
// 3
if (nbAttr < 3) {
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 3;
}
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 3;
}
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue3)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 3;
}
if (this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 3;
}
}
if (nbAttr < 2) {
// 2
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue3)
&& this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 2;
}
}
if (nbAttr < 1) {
// 1
if (this.containsProductVariantValue(product, productVariantValue1)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue2)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue4)) {
productFind = product;
nbAttr = 1;
}
}
}
if (productVariantValue1 != null
&& productVariantValue2 != null
&& productVariantValue3 != null) {
// 3
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)) {
LOG.debug(
"Variant de produit trouvé directement : {} avec l'ensemble des attributs (3) en commun",
product.getCode());
return product;
}
if (nbAttr < 2) {
// 2
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 2;
}
if (this.containsProductVariantValue(product, productVariantValue2)
&& this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 2;
}
}
if (nbAttr < 1) {
// 1
if (this.containsProductVariantValue(product, productVariantValue1)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue2)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue3)) {
productFind = product;
nbAttr = 1;
}
}
}
if (productVariantValue1 != null && productVariantValue2 != null) {
// 2
if (this.containsProductVariantValue(product, productVariantValue1)
&& this.containsProductVariantValue(product, productVariantValue2)) {
LOG.debug(
"Variant de produit trouvé directement : {} avec l'ensemble des attributs (2) en commun",
product.getCode());
return product;
}
if (nbAttr < 1) {
// 1
if (this.containsProductVariantValue(product, productVariantValue1)) {
productFind = product;
nbAttr = 1;
}
if (this.containsProductVariantValue(product, productVariantValue2)) {
productFind = product;
nbAttr = 1;
}
}
}
if (productVariantValue1 != null) {
if (this.containsProductVariantValue(product, productVariantValue1)) {
LOG.debug(
"Variant de produit trouvé directement : {} avec l'ensemble des attributs (1) en commun",
product.getCode());
return product;
}
}
}
if (productFind != null) {
LOG.debug(
"Variant de produit trouvé : {} avec {} attributs en commun",
productFind.getCode(),
nbAttr);
return productFind;
}
}
return productSearched;
}
private boolean containsProductVariantValue(
Product product, ProductVariantValue productVariantValue) {
ProductVariant productVariantFind = product.getProductVariant();
ProductVariantValue productVariantValue1 = productVariantFind.getProductVariantValue1();
ProductVariantValue productVariantValue2 = productVariantFind.getProductVariantValue2();
ProductVariantValue productVariantValue3 = productVariantFind.getProductVariantValue3();
ProductVariantValue productVariantValue4 = productVariantFind.getProductVariantValue4();
if ((productVariantValue1 != null
&& productVariantValue1.getCode().equals(productVariantValue.getCode())
&& productVariantValue1
.getProductVariantAttr()
.getCode()
.equals(productVariantValue.getProductVariantAttr().getCode()))
|| (productVariantValue2 != null
&& productVariantValue2.getCode().equals(productVariantValue.getCode())
&& productVariantValue2
.getProductVariantAttr()
.getCode()
.equals(productVariantValue.getProductVariantAttr().getCode()))
|| (productVariantValue3 != null
&& productVariantValue3.getCode().equals(productVariantValue.getCode())
&& productVariantValue3
.getProductVariantAttr()
.getCode()
.equals(productVariantValue.getProductVariantAttr().getCode()))
|| (productVariantValue4 != null
&& productVariantValue4.getCode().equals(productVariantValue.getCode())
&& productVariantValue4
.getProductVariantAttr()
.getCode()
.equals(productVariantValue.getProductVariantAttr().getCode()))) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,103 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.ShippingCoef;
import com.axelor.apps.base.db.repo.ShippingCoefRepository;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.util.List;
public class ShippingCoefService {
protected ShippingCoefRepository shippingCoefRepo;
@Inject
public ShippingCoefService(ShippingCoefRepository shippingCoefRepo) {
this.shippingCoefRepo = shippingCoefRepo;
}
/**
* Get the shipping coefficient of a product using the supplier catalog
*
* @param product
* @param partner
* @param company
* @param qty
* @return the shipping coefficient for the given product, partner and company.
*/
public BigDecimal getShippingCoefDefByPartner(
Product product, Partner partner, Company company, BigDecimal qty) {
BigDecimal shippingCoef = BigDecimal.ONE;
if (partner == null || company == null) {
return shippingCoef;
}
List<ShippingCoef> shippingCoefList =
shippingCoefRepo
.all()
.filter(
"self.supplierCatalog.product.id = :productId"
+ " AND self.supplierCatalog.supplierPartner.id = :partnerId"
+ " AND self.supplierCatalog.minQty <= :qty")
.bind("productId", product.getId())
.bind("partnerId", partner.getId())
.bind("qty", qty)
.order("supplierCatalog.minQty")
.fetch();
if (shippingCoefList == null || shippingCoefList.isEmpty()) {
return shippingCoef;
}
for (ShippingCoef shippingCoefObject : shippingCoefList) {
if (company.equals(shippingCoefObject.getCompany())) {
shippingCoef = shippingCoefObject.getShippingCoef();
}
}
return shippingCoef;
}
/**
* Get the shipping coefficient of a product according to the product configuration
*
* @param product
* @param supplierPartner
* @param company
* @param qty
* @return the shipping coefficient for a product
*/
public BigDecimal getShippingCoef(
Product product, Partner supplierPartner, Company company, BigDecimal qty) {
BigDecimal shippingCoef;
if (product.getDefShipCoefByPartner()) {
shippingCoef = getShippingCoefDefByPartner(product, supplierPartner, company, qty);
} else {
shippingCoef = product.getShippingCoef();
}
if (shippingCoef.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ONE;
}
return shippingCoef;
}
}

View File

@ -0,0 +1,25 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2020 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
public interface SophalService {
String GenerateCode(Product product);
}

View File

@ -0,0 +1,87 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2020 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
import com.axelor.db.JPA;
import com.google.inject.servlet.RequestScoped;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RequestScoped
public class SophalServiceImpl implements SophalService {
protected Logger log = LoggerFactory.getLogger(getClass());
@Override
public String GenerateCode(Product product) {
String num;
String result;
String var;
if (product.getUnitCodeSelect() != null) {
var =
product.getFamilleProduit().getCode()
+ product.getSousFamilleProduit().getCode()
+ product.getUnitCodeSelect();
} else {
var =
product.getFamilleProduit().getCode() + product.getSousFamilleProduit().getCode() + "00";
}
if (product.getCode() == null) {
Query q =
JPA.em()
.createQuery(
"select self.code FROM Product as self WHERE self.codeCreated = ?1 AND self.code LIKE ?2 ORDER BY self.code DESC",
String.class);
q.setParameter(1, true);
q.setParameter(2, var + "%");
if (q.getResultList().size() == 0) {
result = null;
} else {
result = (String) q.getResultList().get(0);
}
if (result != null) {
String arr[] = result.split(var);
String nbrString = arr[1];
int nbrInt = Integer.parseInt(nbrString);
nbrInt = nbrInt + 1;
num = Integer.toString(nbrInt);
String padding = "000".substring(num.length()) + num;
result = var + padding;
} else {
result = var + "001";
}
} else {
result = product.getCode();
}
return result;
}
}

View File

@ -0,0 +1,37 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Frequency;
import com.axelor.team.db.TeamTask;
public interface TeamTaskService {
/** Generates reccurent tasks from given {@link TeamTask} and {@link Frequency} */
void generateTasks(TeamTask teamTask, Frequency frequency);
/**
* Updates fields of next task of given {@link TeamTask}, recursively.
*
* <p>This method DOES NOT update potential parent.
*/
void updateNextTask(TeamTask teamTask);
/** Removes all next tasks of given {@link TeamTask}. */
void removeNextTasks(TeamTask teamTask);
}

View File

@ -0,0 +1,132 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Frequency;
import com.axelor.apps.base.db.repo.FrequencyRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.inject.Beans;
import com.axelor.team.db.TeamTask;
import com.axelor.team.db.repo.TeamTaskRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class TeamTaskServiceImpl implements TeamTaskService {
protected TeamTaskRepository teamTaskRepo;
@Inject
public TeamTaskServiceImpl(TeamTaskRepository teamTaskRepo) {
this.teamTaskRepo = teamTaskRepo;
}
@Override
@Transactional
public void generateTasks(TeamTask teamTask, Frequency frequency) {
List<LocalDate> taskDates =
Beans.get(FrequencyService.class)
.getDates(frequency, teamTask.getTaskDate(), frequency.getEndDate());
taskDates.removeIf(date -> date.equals(teamTask.getTaskDate()));
// limit how many TeamTask will be generated at once
Integer limitNumberTasksGenerated =
Beans.get(AppBaseService.class).getAppBase().getLimitNumberTasksGenerated();
if (taskDates.size() > limitNumberTasksGenerated) {
taskDates = taskDates.subList(0, limitNumberTasksGenerated);
}
TeamTask lastTask = teamTask;
for (LocalDate date : taskDates) {
TeamTask newTeamTask = teamTaskRepo.copy(teamTask, false);
setModuleFields(teamTask, date, newTeamTask);
teamTaskRepo.save(newTeamTask);
lastTask.setNextTeamTask(newTeamTask);
teamTaskRepo.save(lastTask);
lastTask = newTeamTask;
}
}
protected void setModuleFields(TeamTask teamTask, LocalDate date, TeamTask newTeamTask) {
newTeamTask.setIsFirst(false);
newTeamTask.setHasDateOrFrequencyChanged(false);
newTeamTask.setDoApplyToAllNextTasks(false);
newTeamTask.setFrequency(
Beans.get(FrequencyRepository.class).copy(teamTask.getFrequency(), false));
newTeamTask.setTaskDate(date);
newTeamTask.setTaskDeadline(date);
newTeamTask.setNextTeamTask(null);
}
@Override
@Transactional
public void updateNextTask(TeamTask teamTask) {
TeamTask nextTeamTask = teamTask.getNextTeamTask();
if (nextTeamTask != null) {
updateModuleFields(teamTask, nextTeamTask);
teamTaskRepo.save(nextTeamTask);
updateNextTask(nextTeamTask);
}
}
protected void updateModuleFields(TeamTask teamTask, TeamTask nextTeamTask) {
nextTeamTask.setName(teamTask.getName());
nextTeamTask.setTeam(teamTask.getTeam());
nextTeamTask.setPriority(teamTask.getPriority());
nextTeamTask.setStatus(teamTask.getStatus());
nextTeamTask.setTaskDuration(teamTask.getTaskDuration());
nextTeamTask.setAssignedTo(teamTask.getAssignedTo());
nextTeamTask.setDescription(teamTask.getDescription());
}
@Override
@Transactional
public void removeNextTasks(TeamTask teamTask) {
List<TeamTask> teamTasks = getAllNextTasks(teamTask);
teamTask.setNextTeamTask(null);
teamTask.setHasDateOrFrequencyChanged(false);
teamTaskRepo.save(teamTask);
for (TeamTask teamTaskToRemove : teamTasks) {
teamTaskRepo.remove(teamTaskToRemove);
}
}
/** Returns next tasks from given {@link TeamTask}. */
public List<TeamTask> getAllNextTasks(TeamTask teamTask) {
List<TeamTask> teamTasks = new ArrayList<>();
TeamTask current = teamTask;
while (current.getNextTeamTask() != null) {
current = current.getNextTeamTask();
teamTasks.add(current);
}
for (TeamTask tt : teamTasks) {
tt.setNextTeamTask(null);
teamTaskRepo.save(tt);
}
return teamTasks;
}
}

View File

@ -0,0 +1,34 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.PrintingSettings;
import com.axelor.apps.base.db.TradingName;
import java.util.List;
public interface TradingNameService {
List<PrintingSettings> getPrintingSettingsList(TradingName tradingName, Company company);
/**
* @param tradingName
* @param company
* @return the default printing settings for the given trading name and company.
*/
PrintingSettings getDefaultPrintingSettings(TradingName tradingName, Company company);
}

View File

@ -0,0 +1,62 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.PrintingSettings;
import com.axelor.apps.base.db.TradingName;
import com.axelor.apps.base.db.TradingNamePrintingSettings;
import com.axelor.db.JPA;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class TradingNameServiceImpl implements TradingNameService {
@Override
public List<PrintingSettings> getPrintingSettingsList(TradingName tradingName, Company company) {
List<PrintingSettings> printingSettingsList = new ArrayList<>();
if (company == null) {
return printingSettingsList;
}
if (tradingName == null || company.getId() == null || tradingName.getId() == null) {
if (company.getPrintingSettings() != null) {
printingSettingsList.add(company.getPrintingSettings());
}
} else {
List<TradingNamePrintingSettings> tradingNamePrintingSettingsList =
JPA.all(TradingNamePrintingSettings.class)
.filter("self.company.id = :company AND self.tradingName.id = :tradingName")
.bind("company", company.getId())
.bind("tradingName", tradingName.getId())
.fetch();
printingSettingsList =
tradingNamePrintingSettingsList
.stream()
.map(TradingNamePrintingSettings::getPrintingSettings)
.collect(Collectors.toList());
}
return printingSettingsList;
}
@Override
public PrintingSettings getDefaultPrintingSettings(TradingName tradingName, Company company) {
List<PrintingSettings> printingSettingsList = getPrintingSettingsList(tradingName, company);
return printingSettingsList.isEmpty() ? null : printingSettingsList.get(0);
}
}

View File

@ -0,0 +1,170 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.UnitConversion;
import com.axelor.apps.base.db.repo.UnitConversionRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.tool.template.TemplateMaker;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Locale;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RequestScoped
public class UnitConversionService {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final char TEMPLATE_DELIMITER = '$';
private static final int DEFAULT_COEFFICIENT_SCALE = 12;
protected TemplateMaker maker;
@Inject protected AppBaseService appBaseService;
@Inject protected UnitConversionRepository unitConversionRepo;
/**
* Convert a value from a unit to another
*
* @param startUnit The starting unit
* @param endUnit The end unit
* @param value The value to convert
* @param scale The wanted scale of the result
* @param product Optionnal, a product used for complex conversions. Input null if needless.
* @return The converted value with the specified scale
* @throws AxelorException
*/
public BigDecimal convert(
Unit startUnit, Unit endUnit, BigDecimal value, int scale, Product product)
throws AxelorException {
if (startUnit == null || endUnit == null)
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.UNIT_CONVERSION_2));
if (startUnit.equals(endUnit)) return value;
else {
try {
BigDecimal coefficient =
this.getCoefficient(unitConversionRepo.all().fetch(), startUnit, endUnit, product);
return value.multiply(coefficient).setScale(scale, RoundingMode.HALF_EVEN);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
return value;
}
/**
* Get the conversion coefficient between two units from a conversion list. If the start unit and
* the end unit can not be found in the list, then the units are swapped. If there still isn't any
* result, an Exception is thrown.
*
* @param unitConversionList A list of conversions between units
* @param startUnit The start unit
* @param endUnit The end unit
* @param product Optionnal, a product used for complex conversions. INput null if needless.
* @return A conversion coefficient to convert from startUnit to endUnit.
* @throws AxelorException The required units are not found in the conversion list.
* @throws CompilationFailedException
* @throws ClassNotFoundException
* @throws IOException
*/
public BigDecimal getCoefficient(
List<? extends UnitConversion> unitConversionList,
Unit startUnit,
Unit endUnit,
Product product)
throws AxelorException, CompilationFailedException, ClassNotFoundException, IOException {
/* Looking for the start unit and the end unit in the unitConversionList to get the coefficient */
if (product != null) {
this.maker = new TemplateMaker(Locale.FRENCH, TEMPLATE_DELIMITER, TEMPLATE_DELIMITER);
this.maker.setContext(product, "Product");
}
String eval = null;
for (UnitConversion unitConversion : unitConversionList) {
if (unitConversion.getStartUnit().equals(startUnit)
&& unitConversion.getEndUnit().equals(endUnit)) {
if (unitConversion.getTypeSelect() == UnitConversionRepository.TYPE_COEFF) {
return unitConversion.getCoef();
} else if (product != null) {
maker.setTemplate(unitConversion.getFormula());
eval = maker.make();
CompilerConfiguration conf = new CompilerConfiguration();
ImportCustomizer customizer = new ImportCustomizer();
customizer.addStaticStars("java.lang.Math");
conf.addCompilationCustomizers(customizer);
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding, conf);
return new BigDecimal(shell.evaluate(eval).toString());
}
}
/* The endUnit become the start unit and the startUnit become the end unit */
if (unitConversion.getStartUnit().equals(endUnit)
&& unitConversion.getEndUnit().equals(startUnit)) {
if (unitConversion.getTypeSelect() == UnitConversionRepository.TYPE_COEFF
&& unitConversion.getCoef().compareTo(BigDecimal.ZERO) != 0) {
return BigDecimal.ONE.divide(
unitConversion.getCoef(), DEFAULT_COEFFICIENT_SCALE, RoundingMode.HALF_EVEN);
} else if (product != null) {
maker.setTemplate(unitConversion.getFormula());
eval = maker.make();
CompilerConfiguration conf = new CompilerConfiguration();
ImportCustomizer customizer = new ImportCustomizer();
customizer.addStaticStars("java.lang.Math");
conf.addCompilationCustomizers(customizer);
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding, conf);
BigDecimal result = new BigDecimal(shell.evaluate(eval).toString());
if (result.compareTo(BigDecimal.ZERO) != 0) {
return BigDecimal.ONE.divide(result, DEFAULT_COEFFICIENT_SCALE, RoundingMode.HALF_EVEN);
}
}
}
}
/* If there is no startUnit and endUnit in the UnitConversion list so we throw an exception */
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.UNIT_CONVERSION_1),
startUnit.getName(),
endUnit.getName());
}
}

View File

@ -0,0 +1,32 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.Year;
import com.axelor.exception.AxelorException;
import java.time.LocalDate;
import java.util.List;
public interface YearService {
public Year getYear(LocalDate date, Company company);
List<Period> generatePeriods(Year year) throws AxelorException;
}

View File

@ -0,0 +1,89 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.Year;
import com.axelor.apps.base.db.repo.YearRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class YearServiceImpl implements YearService {
protected YearRepository yearRepository;
@Inject
public YearServiceImpl(YearRepository yearRepository) {
this.yearRepository = yearRepository;
}
public List<Period> generatePeriods(Year year) throws AxelorException {
List<Period> periods = new ArrayList<Period>();
Integer duration = year.getPeriodDurationSelect();
LocalDate fromDate = year.getFromDate();
LocalDate toDate = year.getToDate();
LocalDate periodToDate = fromDate;
Integer periodNumber = 1;
int c = 0;
int loopLimit = 1000;
while (periodToDate.isBefore(toDate)) {
if (periodNumber != 1) fromDate = fromDate.plusMonths(duration);
if (c >= loopLimit) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY, I18n.get(IExceptionMessage.PERIOD_3));
}
c += 1;
periodToDate = fromDate.plusMonths(duration).minusDays(1);
if (periodToDate.isAfter(toDate)) periodToDate = toDate;
if (fromDate.isAfter(toDate)) continue;
Period period = new Period();
period.setFromDate(fromDate);
period.setToDate(periodToDate);
period.setYear(year);
period.setName(String.format("%02d", periodNumber) + "/" + year.getCode());
period.setCode(
(String.format("%02d", periodNumber)
+ "/"
+ year.getCode()
+ "_"
+ year.getCompany().getCode())
.toUpperCase());
period.setStatusSelect(year.getStatusSelect());
periods.add(period);
periodNumber++;
}
return periods;
}
@Override
public Year getYear(LocalDate date, Company company) {
return yearRepository
.all()
.filter("self.company = ?1 AND self.fromDate < ?2 AND self.toDate >= ?2", company, date)
.fetchOne();
}
}

View File

@ -0,0 +1,236 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.base.db.repo.BatchRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.auth.db.AuditableModel;
import com.axelor.db.EntityHelper;
import com.axelor.db.JPA;
import com.axelor.db.Model;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.common.base.Preconditions;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractBatch {
private static final ThreadLocal<Long> threadBatchId = new ThreadLocal<>();
public static final int FETCH_LIMIT = 10;
@Inject protected AppBaseService appBaseService;
protected static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected Batch batch;
protected Model model;
@Inject protected BatchRepository batchRepo;
private int done;
private int anomaly;
protected AbstractBatch() {
this.batch = new Batch();
this.batch.setStartDate(ZonedDateTime.now());
this.done = 0;
this.anomaly = 0;
this.batch.setDone(this.done);
this.batch.setAnomaly(this.anomaly);
JPA.runInTransaction(() -> batch = JPA.persist(batch));
}
public Batch getBatch() {
return batch;
}
/**
* Returns the currently running batch.
*
* @return The Batch instance currently being run or <code>null</code> if no batch is in progress.
*/
public static Batch getCurrentBatch() {
return JPA.find(Batch.class, getCurrentBatchId());
}
/**
* Returns the ID of the currently running batch.
*
* @return The currently running batch's ID or <code>0</code> if no batch is being run (allowing
* it to be directly used with {@link TraceBackService}.
*/
public static long getCurrentBatchId() {
Long id = threadBatchId.get();
return id == null ? 0 : id;
}
public Batch run(AuditableModel model) {
Preconditions.checkNotNull(model);
if (threadBatchId.get() != null) {
throw new IllegalStateException(I18n.get(IExceptionMessage.ABSTRACT_BATCH_2));
}
if (isRunnable(model)) {
try {
threadBatchId.set(batch.getId());
start();
process();
stop();
return batch;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadBatchId.remove();
unarchived();
}
} else {
throw new RuntimeException(I18n.get(IExceptionMessage.ABSTRACT_BATCH_1));
}
}
protected abstract void process();
protected boolean isRunnable(Model model) {
this.model = model;
return !Boolean.TRUE.equals(model.getArchived());
}
protected void start() throws IllegalAccessException {
LOG.info("Début batch {} ::: {}", model, batch.getStartDate());
model.setArchived(true);
associateModel();
checkPoint();
}
/**
* As {@code batch} entity can be detached from the session, call {@code Batch.find()} get the
* entity in the persistant context. Warning : {@code batch} entity have to be saved before.
*/
protected void stop() {
findBatch();
batch.setEndDate(ZonedDateTime.now());
batch.setDuration(getDuring());
checkPoint();
LOG.info("Fin batch {} ::: {}", model, batch.getEndDate());
}
protected void incrementDone() {
findBatch();
_incrementDone();
}
protected void _incrementDone() {
done += 1;
batch.setDone(done);
checkPoint();
LOG.debug("Done ::: {}", done);
}
protected void incrementAnomaly() {
findBatch();
_incrementAnomaly();
}
protected void _incrementAnomaly() {
anomaly += 1;
batch.setAnomaly(anomaly);
checkPoint();
LOG.debug("Anomaly ::: {}", anomaly);
}
protected void addComment(String comment) {
findBatch();
batch.setComments(comment);
checkPoint();
}
@Transactional
protected Batch checkPoint() {
return findBatch();
}
@Transactional
protected void unarchived() {
try {
final Class<? extends Model> entityClass = EntityHelper.getEntityClass(model);
final Long modelId = model.getId();
model = JPA.find(entityClass, modelId);
model.setArchived(false);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
private Long getDuring() {
return ChronoUnit.MINUTES.between(batch.getStartDate(), batch.getEndDate());
}
private void associateModel() throws IllegalAccessException {
LOG.debug("ASSOCIATE batch:{} TO model:{}", batch, model);
for (Field field : batch.getClass().getDeclaredFields()) {
LOG.debug(
"TRY TO ASSOCIATE field:{} TO model:{}",
field.getType().getName(),
model.getClass().getName());
if (isAssociable(field)) {
LOG.debug("FIELD ASSOCIATE TO MODEL");
field.setAccessible(true);
field.set(batch, model);
field.setAccessible(false);
break;
}
}
}
private boolean isAssociable(Field field) {
return field.getType().equals(EntityHelper.getEntityClass(model));
}
protected Batch findBatch() {
if (!JPA.em().contains(batch)) {
batch = JPA.find(Batch.class, batch.getId());
}
return batch;
}
}

View File

@ -0,0 +1,75 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
public abstract class AbstractBatchService {
/**
* Get batch model class.
*
* @return
*/
protected abstract Class<? extends Model> getModelClass();
/**
* Run a batch with the given batch model.
*
* @param model
* @return
* @throws AxelorException
*/
public abstract Batch run(Model model) throws AxelorException;
/**
* Run a batch from its code.
*
* @param code
* @return
* @throws AxelorException
*/
public Batch run(String code) throws AxelorException {
Model model = findModelByCode(code);
if (model == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BASE_BATCH_2),
code);
}
return run(model);
}
/**
* Find batch model by its code.
*
* @param code
* @return
*/
public Model findModelByCode(String code) {
return Query.of(getModelClass()).filter("self.code = :code").bind("code", code).fetchOne();
}
}

View File

@ -0,0 +1,341 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.repo.GroupRepository;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaAction;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.MetaTranslation;
import com.axelor.meta.db.repo.MetaFileRepository;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.axelor.meta.db.repo.MetaTranslationRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class ExportDbObjectService {
@Inject UserService uis;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static String[] fieldAttrs = new String[] {"name", "type", "title"};
public String[] csvHeaders =
new String[] {
"object", "module", "name", "type", "title_en", "title_fr", "help_en", "help_fr", "url"
};
private List<String[]> fieldDataList = new ArrayList<String[]>();
private List<String> objectList = new ArrayList<String>();
private Map<String, Object> objectMap = new HashMap<String, Object>();
private Group group = null;
@Transactional
public MetaFile exportObject() {
// group = AuthUtils.getUser().getGroup();
group = Beans.get(GroupRepository.class).all().filter("self.code = 'admins'").fetchOne();
try {
log.debug("Attachment dir: {}", AppSettings.get().get("file.upload.dir"));
String uploadDir = AppSettings.get().get("file.upload.dir");
if (uploadDir == null || !new File(uploadDir).exists()) {
return null;
}
String appSrc = AppSettings.get().get("application.src");
log.debug("Module dir: {}", appSrc);
if (appSrc == null) {
return null;
}
File moduleDir = new File(appSrc);
if (!moduleDir.exists()) {
return null;
}
MetaFile metaFile = new MetaFile();
String fileName =
"ExportObject-"
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyMMddHHmmSS"))
+ ".csv";
metaFile.setFileName(fileName);
metaFile.setFilePath(fileName);
metaFile = Beans.get(MetaFileRepository.class).save(metaFile);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
updateObjectMap(
Arrays.asList(moduleDir.listFiles()), saxParserFactory.newSAXParser(), new XmlHandler());
writeObjects(MetaFiles.getPath(metaFile).toFile());
return metaFile;
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
return null;
}
private void writeObjects(File objectFile) {
try {
List<? extends MetaMenu> menuList =
Beans.get(MetaMenuRepository.class)
.all()
.filter("self.parent = null AND self.left = true AND ?1 MEMBER OF self.groups", group)
.order("-priority")
.order("id")
.fetch();
log.debug("Total root menus: {}", menuList.size());
generateMenuGraph(menuList);
CsvTool.csvWriter(
objectFile.getParent(), objectFile.getName(), ';', csvHeaders, fieldDataList);
} catch (IOException e) {
e.printStackTrace();
}
}
private void generateMenuGraph(List<? extends MetaMenu> menuList) {
// log.debug("Checking menu list: {}",menuList);
for (MetaMenu menu : menuList) {
String model = menu.getAction() != null ? menu.getAction().getModel() : null;
// log.debug("Action model: ",model);
if (model != null && !objectList.contains(model)) {
updateFieldData(menu.getAction());
}
// List<? extends MetaMenu> childList = MetaMenu.all().filter("self.parent = ?1 AND self.left
// = true AND ?2 MEMBER OF self.groups", menu,group).order("-priority").order("id").fetch();
List<? extends MetaMenu> childList =
Beans.get(MetaMenuRepository.class)
.all()
.filter("self.parent = ?1 AND self.left = true", menu)
.order("-priority")
.order("id")
.fetch();
generateMenuGraph(childList);
}
}
@SuppressWarnings("unchecked")
private void updateFieldData(MetaAction action) {
String[] objectName = action.getModel().split("\\.");
String objName = objectName[objectName.length - 1];
Map<String, Object> moduleMap = (Map<String, Object>) objectMap.get(objName);
if (moduleMap == null) {
return;
}
boolean addObject = true;
MetaTranslationRepository metaTranslationRepo = Beans.get(MetaTranslationRepository.class);
// log.debug("Adding object: {}",objName);
for (Entry<String, Object> module : moduleMap.entrySet()) {
boolean addModule = true;
for (Map<String, String> field : (List<Map<String, String>>) module.getValue()) {
String[] fields = new String[csvHeaders.length];
fields[0] = "";
if (addObject) {
fields[0] = action.getModel();
fields[8] = getActionUrl(action);
}
fields[1] = "";
if (addModule) {
fields[1] = module.getKey();
}
fields[2] = field.get("name");
fields[3] = field.get("type");
fields[4] = field.get("title");
MetaTranslation mts = metaTranslationRepo.findByKey(field.get("title"), "fr");
if (mts != null) {
fields[5] = mts.getMessage();
}
mts = metaTranslationRepo.findByKey("help:" + objName + "." + field.get("name"), "en");
if (mts != null) {
fields[6] = mts.getMessage().replace(";", "\n");
}
mts = metaTranslationRepo.findByKey("help:" + objName + "." + field.get("name"), "fr");
if (mts != null) {
fields[7] = mts.getMessage().replace(";", "\n");
}
fieldDataList.add(fields);
addObject = false;
addModule = false;
}
}
objectList.add(action.getModel());
}
private String getActionUrl(MetaAction action) {
String url = AppSettings.get().getBaseURL() + "#/ds";
String viewType = getActionViewType(action.getXml());
if (viewType.equals("grid")) {
url = url + "/" + action.getName() + "/list/1";
} else if (viewType.equals("form")) {
url = url + "/" + action.getName() + "/edit";
} else if (viewType.equals("calendar")) {
url = url + "/" + action.getName() + "/calendar";
}
return url;
}
public static String getActionViewType(String xml) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder;
try {
builder = domFactory.newDocumentBuilder();
File tempXml = File.createTempFile("Temp", "xml");
FileWriter fw = new FileWriter(tempXml);
fw.write(xml);
fw.close();
Document doc = builder.parse(new FileInputStream(tempXml));
Node child = doc.getFirstChild();
NodeList chs = child.getChildNodes();
for (Integer i = 0; i < chs.getLength(); i++) {
if (chs.item(i).getNodeName().equals("view")) {
NamedNodeMap attributes = chs.item(i).getAttributes();
return attributes.getNamedItem("type").getNodeValue();
}
}
return "";
} catch (Exception e) {
log.error(e.getMessage());
}
return null;
}
@SuppressWarnings("unchecked")
private void updateObjectMap(List<File> modules, SAXParser parser, XmlHandler xmlHandler)
throws SAXException, IOException {
for (File module : modules) {
String modulePath = module.getAbsolutePath();
File modelDir = new File(modulePath + "/src/main/resources/domains/");
if (!modelDir.exists()) {
continue;
}
// log.debug("Module : {}",modelDir.getAbsolutePath());
for (File objectFile : modelDir.listFiles()) {
// log.debug("Parsing domain : {}",objectFile.getName());
String objectName = objectFile.getName().split("\\.")[0];
parser.parse(new InputSource(new FileInputStream(objectFile)), xmlHandler);
Map<String, Object> moduleMap = (Map<String, Object>) objectMap.get(objectName);
if (moduleMap == null) {
moduleMap = new HashMap<String, Object>();
}
moduleMap.put(
module.getName(),
updateObjectModel(xmlHandler.fieldList, objectName, module.getName()));
objectMap.put(objectName, moduleMap);
}
}
}
private Object updateObjectModel(
List<Map<String, String>> fieldList, String objectName, String moduleName) {
for (Map<String, String> field : fieldList) {
field.put("module", moduleName);
field.put("object", objectName);
}
return fieldList;
}
}
class XmlHandler extends DefaultHandler {
public List<Map<String, String>> fieldList;
private boolean isObject = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
switch (qName) {
case "entity":
{
isObject = true;
fieldList = new ArrayList<Map<String, String>>();
break;
}
default:
{
if (isObject) {
Map<String, String> fieldMap = new HashMap<String, String>();
fieldMap.put("type", qName);
for (String fieldAttr : Arrays.asList(ExportDbObjectService.fieldAttrs)) {
if (attributes.getValue(fieldAttr) != null) {
fieldMap.put(fieldAttr, attributes.getValue(fieldAttr));
}
}
fieldList.add(fieldMap);
}
}
}
super.startElement(uri, localName, qName, attributes);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("entity")) {
isObject = false;
}
super.endElement(uri, localName, qName);
}
}

View File

@ -0,0 +1,126 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.apps.base.db.IndicatorGenerator;
import com.axelor.apps.base.db.IndicatorGeneratorGrouping;
import com.axelor.apps.base.db.repo.IndicatorGeneratorGroupingRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class IndicatorGeneratorGroupingService {
@Inject private IndicatorGeneratorService indicatorGeneratorService;
@Inject private IndicatorGeneratorGroupingRepository iggRepo;
@Transactional(rollbackOn = {Exception.class})
public void run(IndicatorGeneratorGrouping indicatorGeneratorGrouping) throws AxelorException {
String log = "";
String result = "";
for (IndicatorGenerator indicatorGenerator :
indicatorGeneratorGrouping.getIndicatorGeneratorSet()) {
indicatorGeneratorService.run(indicatorGenerator);
result =
result
+ "\n"
+ indicatorGenerator.getCode()
+ " "
+ indicatorGenerator.getName()
+ " : "
+ indicatorGenerator.getResult();
if (indicatorGenerator.getLog() != null && !indicatorGenerator.getLog().isEmpty()) {
log = log + "\n" + indicatorGenerator.getLog();
}
}
indicatorGeneratorGrouping.setResult(result);
indicatorGeneratorGrouping.setLog(log);
iggRepo.save(indicatorGeneratorGrouping);
}
@Transactional(rollbackOn = {Exception.class})
public void export(IndicatorGeneratorGrouping indicatorGeneratorGrouping) throws AxelorException {
String log = "";
if (indicatorGeneratorGrouping.getPath() == null
|| indicatorGeneratorGrouping.getPath().isEmpty()) {
log += "\n" + I18n.get(IExceptionMessage.INDICATOR_GENERATOR_GROUPING_1);
}
if (indicatorGeneratorGrouping.getCode() == null
|| indicatorGeneratorGrouping.getCode().isEmpty()) {
log += "\n" + I18n.get(IExceptionMessage.INDICATOR_GENERATOR_GROUPING_2);
}
List<String[]> resultList = new ArrayList<String[]>();
for (IndicatorGenerator indicatorGenerator :
indicatorGeneratorGrouping.getIndicatorGeneratorSet()) {
String[] result = {
indicatorGenerator.getCode(), indicatorGenerator.getName(), indicatorGenerator.getResult()
};
resultList.add(result);
log = log + "\n" + indicatorGenerator.getLog();
}
try {
CsvTool.csvWriter(
indicatorGeneratorGrouping.getPath(),
indicatorGeneratorGrouping.getCode() + ".csv",
';',
null,
resultList);
} catch (IOException e) {
log += I18n.get(IExceptionMessage.INDICATOR_GENERATOR_GROUPING_3);
}
if (!log.isEmpty() && log.length() != 0) {
String log2 = indicatorGeneratorGrouping.getLog();
log2 += "\n ---------------------------------------------------";
log2 += log;
indicatorGeneratorGrouping.setLog(log2);
}
iggRepo.save(indicatorGeneratorGrouping);
}
}

View File

@ -0,0 +1,102 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.apps.base.db.IndicatorGenerator;
import com.axelor.apps.base.db.repo.IndicatorGeneratorRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigInteger;
import javax.persistence.Query;
public class IndicatorGeneratorService {
@Inject private IndicatorGeneratorRepository indicatorGeneratorRepo;
@Transactional(rollbackOn = {Exception.class})
public String run(IndicatorGenerator indicatorGenerator) throws AxelorException {
String log = "";
int requestType = indicatorGenerator.getRequestLanguage();
String request = indicatorGenerator.getRequest();
if (request == null || request.isEmpty()) {
log =
String.format(
I18n.get(IExceptionMessage.INDICATOR_GENERATOR_1), indicatorGenerator.getCode());
}
String result = "";
try {
if (request != null && !request.isEmpty()) {
if (requestType == 0) {
result = this.runSqlRequest(request);
} else if (requestType == 1) {
result = this.runJpqlRequest(request);
}
}
} catch (Exception e) {
log +=
String.format(
I18n.get(IExceptionMessage.INDICATOR_GENERATOR_2), indicatorGenerator.getCode());
}
indicatorGenerator.setLog(log);
indicatorGenerator.setResult(result);
indicatorGeneratorRepo.save(indicatorGenerator);
return result;
}
public String runSqlRequest(String request) {
String result = "";
Query query = JPA.em().createNativeQuery(request);
BigInteger requestResult = (BigInteger) query.getSingleResult();
result = String.format("%s", requestResult);
return result;
}
public String runJpqlRequest(String request) {
String result = "";
Query query = JPA.em().createQuery(request);
Long requestResult = (Long) query.getSingleResult();
result = String.format("%s", requestResult);
return result;
}
}

View File

@ -0,0 +1,381 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.administration;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.base.db.SequenceLettersTypeSelect;
import com.axelor.apps.base.db.SequenceTypeSelect;
import com.axelor.apps.base.db.SequenceVersion;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.db.repo.SequenceVersionRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.tool.StringTool;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaSelectItem;
import com.axelor.meta.db.repo.MetaSelectItemRepository;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ThreadSafe
@Singleton
public class SequenceService {
private static final String DRAFT_PREFIX = "#";
private static final String PATTERN_FULL_YEAR = "%YYYY",
PATTERN_YEAR = "%YY",
PATTERN_MONTH = "%M",
PATTERN_FULL_MONTH = "%FM",
PATTERN_DAY = "%D",
PATTERN_WEEK = "%WY",
PADDING_STRING = "0";
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private SequenceVersionRepository sequenceVersionRepository;
private AppBaseService appBaseService;
@Inject private SequenceRepository sequenceRepo;
@Inject
public SequenceService(
SequenceVersionRepository sequenceVersionRepository, AppBaseService appBaseService) {
this.sequenceVersionRepository = sequenceVersionRepository;
this.appBaseService = appBaseService;
}
public static boolean isYearValid(Sequence sequence) {
boolean yearlyResetOk = sequence.getYearlyResetOk();
if (!yearlyResetOk) {
return true;
}
String seqPrefixe = StringUtils.defaultString(sequence.getPrefixe(), ""),
seqSuffixe = StringUtils.defaultString(sequence.getSuffixe(), ""),
seq = seqPrefixe + seqSuffixe;
if (yearlyResetOk && !seq.contains(PATTERN_YEAR) && !seq.contains(PATTERN_FULL_YEAR)) {
return false;
}
return true;
}
public static boolean isMonthValid(Sequence sequence) {
boolean monthlyResetOk = sequence.getMonthlyResetOk();
if (!monthlyResetOk) {
return true;
}
String seqPrefixe = StringUtils.defaultString(sequence.getPrefixe(), ""),
seqSuffixe = StringUtils.defaultString(sequence.getSuffixe(), ""),
seq = seqPrefixe + seqSuffixe;
if (monthlyResetOk
&& ((!seq.contains(PATTERN_MONTH) && !seq.contains(PATTERN_FULL_MONTH))
|| (!seq.contains(PATTERN_YEAR) && !seq.contains(PATTERN_FULL_YEAR)))) {
return false;
}
return true;
}
public static boolean isSequenceLengthValid(Sequence sequence) {
String seqPrefixe = StringUtils.defaultString(sequence.getPrefixe(), "").replaceAll("%", "");
String seqSuffixe = StringUtils.defaultString(sequence.getSuffixe(), "").replaceAll("%", "");
return (seqPrefixe.length() + seqSuffixe.length() + sequence.getPadding()) <= 14;
}
/**
* Retourne une sequence en fonction du code, de la sté
*
* @return
*/
public Sequence getSequence(String code, Company company) {
if (code == null) {
return null;
}
if (company == null) {
return sequenceRepo.findByCode(code);
}
return sequenceRepo.find(code, company);
}
/**
* Retourne une sequence en fonction du code, de la sté
*
* @return
*/
public String getSequenceNumber(String code) {
return this.getSequenceNumber(code, null);
}
/**
* Retourne une sequence en fonction du code, de la sté
*
* @return
*/
public String getSequenceNumber(String code, Company company) {
Sequence sequence = getSequence(code, company);
if (sequence == null) {
return null;
}
return this.getSequenceNumber(sequence, appBaseService.getTodayDate());
}
/**
* Retourne une sequence en fonction du code, de la sté
*
* @return
*/
public boolean hasSequence(String code, Company company) {
return getSequence(code, company) != null;
}
public String getSequenceNumber(Sequence sequence) {
return getSequenceNumber(sequence, appBaseService.getTodayDate());
}
/**
* Fonction retournant une numéro de séquence depuis une séquence générique, et une date
*
* @param sequence
* @return
*/
@Transactional
public String getSequenceNumber(Sequence sequence, LocalDate refDate) {
SequenceVersion sequenceVersion = getVersion(sequence, refDate);
String seqPrefixe = StringUtils.defaultString(sequence.getPrefixe(), ""),
seqSuffixe = StringUtils.defaultString(sequence.getSuffixe(), ""),
sequenceValue;
if (sequence.getSequenceTypeSelect() == SequenceTypeSelect.NUMBERS) {
sequenceValue =
StringUtils.leftPad(
sequenceVersion.getNextNum().toString(), sequence.getPadding(), PADDING_STRING);
} else {
sequenceValue = findNextLetterSequence(sequenceVersion);
}
sequenceVersion.setNextNum(sequenceVersion.getNextNum() + sequence.getToBeAdded());
String nextSeq =
(seqPrefixe + sequenceValue + seqSuffixe)
.replaceAll(PATTERN_FULL_YEAR, Integer.toString(refDate.get(ChronoField.YEAR_OF_ERA)))
.replaceAll(PATTERN_YEAR, refDate.format(DateTimeFormatter.ofPattern("yy")))
.replaceAll(PATTERN_MONTH, Integer.toString(refDate.getMonthValue()))
.replaceAll(PATTERN_FULL_MONTH, refDate.format(DateTimeFormatter.ofPattern("MM")))
.replaceAll(PATTERN_DAY, Integer.toString(refDate.getDayOfMonth()))
.replaceAll(
PATTERN_WEEK, Integer.toString(refDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)));
log.debug("nextSeq : : : : {}", nextSeq);
sequenceVersionRepository.save(sequenceVersion);
return nextSeq;
}
private String findNextLetterSequence(SequenceVersion sequenceVersion) {
long n = sequenceVersion.getNextNum();
char[] buf = new char[(int) Math.floor(Math.log(25 * (n + 1)) / Math.log(26))];
for (int i = buf.length - 1; i >= 0; i--) {
n--;
buf[i] = (char) ('A' + n % 26);
n /= 26;
}
if (sequenceVersion.getSequence().getSequenceLettersTypeSelect()
== SequenceLettersTypeSelect.UPPERCASE) {
return new String(buf);
}
return new String(buf).toLowerCase();
}
protected SequenceVersion getVersion(Sequence sequence, LocalDate refDate) {
log.debug("Reference date : : : : {}", refDate);
if (sequence.getMonthlyResetOk()) {
return getVersionByMonth(sequence, refDate);
}
if (sequence.getYearlyResetOk()) {
return getVersionByYear(sequence, refDate);
}
return getVersionByDate(sequence, refDate);
}
protected SequenceVersion getVersionByDate(Sequence sequence, LocalDate refDate) {
SequenceVersion sequenceVersion = sequenceVersionRepository.findByDate(sequence, refDate);
if (sequenceVersion == null) {
sequenceVersion = new SequenceVersion(sequence, refDate, null, 1L);
}
return sequenceVersion;
}
protected SequenceVersion getVersionByMonth(Sequence sequence, LocalDate refDate) {
SequenceVersion sequenceVersion =
sequenceVersionRepository.findByMonth(sequence, refDate.getMonthValue(), refDate.getYear());
if (sequenceVersion == null) {
sequenceVersion =
new SequenceVersion(
sequence,
refDate.withDayOfMonth(1),
refDate.withDayOfMonth(refDate.lengthOfMonth()),
1L);
}
return sequenceVersion;
}
protected SequenceVersion getVersionByYear(Sequence sequence, LocalDate refDate) {
SequenceVersion sequenceVersion =
sequenceVersionRepository.findByYear(sequence, refDate.getYear());
if (sequenceVersion == null) {
sequenceVersion =
new SequenceVersion(
sequence,
refDate.withDayOfMonth(1),
refDate.withDayOfMonth(refDate.lengthOfMonth()),
1L);
}
return sequenceVersion;
}
public String getDefaultTitle(Sequence sequence) {
MetaSelectItem item =
Beans.get(MetaSelectItemRepository.class)
.all()
.filter(
"self.select.name = ? AND self.value = ?",
"sequence.generic.code.select",
sequence.getCode())
.fetchOne();
return item.getTitle();
}
/**
* Get draft sequence number.
*
* @param model
* @return
* @throws AxelorException
*/
public String getDraftSequenceNumber(Model model) throws AxelorException {
if (model.getId() == null) {
throw new AxelorException(
model,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.SEQUENCE_NOT_SAVED_RECORD));
}
return String.format("%s%d", DRAFT_PREFIX, model.getId());
}
/**
* Get draft sequence number with leading zeros.
*
* @param model
* @param padding
* @return
*/
public String getDraftSequenceNumber(Model model, int zeroPadding) throws AxelorException {
if (model.getId() == null) {
throw new AxelorException(
model,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.SEQUENCE_NOT_SAVED_RECORD));
}
return String.format(
"%s%s",
DRAFT_PREFIX, StringTool.fillStringLeft(String.valueOf(model.getId()), '0', zeroPadding));
}
/**
* Check whether a sequence number is empty or draft.
*
* <p>Also consider '*' as draft character for backward compatibility.
*
* @param sequenceNumber
* @return
*/
public boolean isEmptyOrDraftSequenceNumber(String sequenceNumber) {
return Strings.isNullOrEmpty(sequenceNumber)
|| sequenceNumber.matches(String.format("[\\%s\\*]\\d+", DRAFT_PREFIX));
}
/**
* Computes sequence full name
*
* @param sequence Sequence to compute full name
*/
public String computeFullName(Sequence sequence) {
StringBuilder fn = new StringBuilder();
if (sequence.getPrefixe() != null) {
fn.append(sequence.getPrefixe());
}
for (int i = 0; i < sequence.getPadding(); i++) {
fn.append("X");
}
if (sequence.getSuffixe() != null) {
fn.append(sequence.getSuffixe());
}
fn.append(" - ");
fn.append(sequence.getName());
return fn.toString();
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.AdvancedImport;
import com.axelor.apps.base.service.readers.DataReaderService;
import com.axelor.db.mapper.Mapper;
import com.axelor.exception.AxelorException;
public interface AdvancedImportService {
public boolean apply(AdvancedImport advanceImport) throws AxelorException, ClassNotFoundException;
public Mapper getMapper(String modelFullName) throws ClassNotFoundException;
public int getTabConfigRowCount(
String sheet, DataReaderService reader, int totalLines, String[] objectRow);
public boolean resetImport(AdvancedImport advancedImport) throws ClassNotFoundException;
}

View File

@ -0,0 +1,863 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.AdvancedImport;
import com.axelor.apps.base.db.FileField;
import com.axelor.apps.base.db.FileTab;
import com.axelor.apps.base.db.repo.AdvancedImportRepository;
import com.axelor.apps.base.db.repo.FileFieldRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.readers.DataReaderFactory;
import com.axelor.apps.base.service.readers.DataReaderService;
import com.axelor.common.Inflector;
import com.axelor.db.EntityHelper;
import com.axelor.db.JpaRepository;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaFieldRepository;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.axelor.rpc.JsonContext;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AdvancedImportServiceImpl implements AdvancedImportService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String forSelectUseValues = "values";
private static final String forSelectUseTitles = "titles";
private static final String forSelectUseTranslatedTitles = "translated titles";
private Inflector inflector = Inflector.getInstance();
private List<String> searchFieldList;
private boolean isTabWithoutConfig = false;
@Inject private MetaModelRepository metaModelRepo;
@Inject private MetaFieldRepository metaFieldRepo;
@Inject private DataReaderFactory dataReaderFactory;
@Inject private FileFieldService fileFieldService;
@Inject private AdvancedImportRepository advancedImportRepository;
@Inject private FileFieldRepository fileFieldRepository;
@Inject private DataImportService dataImportService;
@Override
public boolean apply(AdvancedImport advancedImport)
throws AxelorException, ClassNotFoundException {
if (advancedImport.getImportFile() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_NO_IMPORT_FILE));
}
String extension = Files.getFileExtension(advancedImport.getImportFile().getFileName());
if (extension == null
|| (!extension.equals("xlsx") && !extension.equals("xls") && !extension.equals("csv"))) {
return false;
}
DataReaderService reader = dataReaderFactory.getDataReader(extension);
reader.initialize(advancedImport.getImportFile(), advancedImport.getFileSeparator());
return this.process(reader, advancedImport);
}
@Transactional
public boolean process(DataReaderService reader, AdvancedImport advancedImport)
throws AxelorException, ClassNotFoundException {
boolean isValid = true;
if (!CollectionUtils.isEmpty(advancedImport.getFileTabList())) {
advancedImport.getFileTabList().stream().forEach(tab -> tab.clearFileFieldList());
advancedImport.clearFileTabList();
}
String[] sheets = reader.getSheetNames();
boolean isConfig = advancedImport.getIsConfigInFile();
boolean isTabConfig = advancedImport.getIsFileTabConfigAdded();
boolean isHeader = advancedImport.getIsHeader();
int linesToIgnore = advancedImport.getNbOfFirstLineIgnore();
int tabConfigRowCount = 0;
int startIndex = isConfig ? 0 : linesToIgnore;
int fileTabSequence = 1;
for (String sheet : sheets) {
int totalLines = reader.getTotalLines(sheet);
if (totalLines == 0) {
continue;
}
FileTab fileTab = new FileTab();
fileTab.setName(sheet);
fileTab.setSequence(fileTabSequence);
fileTabSequence++;
String[] objectRow = reader.read(sheet, startIndex, 0);
if (objectRow == null) {
isValid = false;
break;
}
if (isConfig && isTabConfig) {
tabConfigRowCount = getTabConfigRowCount(sheet, reader, totalLines, objectRow);
}
isValid = this.applyObject(objectRow, fileTab, isConfig, linesToIgnore, isTabConfig);
if (!isValid) {
break;
}
List<FileField> fileFieldList = new ArrayList<>();
List<Integer> ignoreFields = new ArrayList<Integer>();
for (int line = isConfig ? 1 : linesToIgnore; line < totalLines; line++) {
String[] row = reader.read(sheet, line, isConfig ? 0 : objectRow.length);
if (row == null) {
continue;
}
if (isConfig) {
this.applyWithConfig(
row, line, fileFieldList, ignoreFields, fileTab, isTabConfig, tabConfigRowCount);
} else {
this.applyWithoutConfig(row, (line - linesToIgnore), fileFieldList, fileTab, isHeader);
}
}
if (isConfig) {
fileFieldList.removeIf(field -> field.getImportField() == null);
if (!fileTab.getImportType().equals(FileFieldRepository.IMPORT_TYPE_NEW)) {
fileTab = this.setSearchField(fileTab, searchFieldList, fileFieldList);
}
}
advancedImport.addFileTabListItem(fileTab);
advancedImportRepository.save(advancedImport);
}
return isValid;
}
private boolean applyObject(
String[] row, FileTab fileTab, boolean isConfig, int linesToIgnore, boolean isTabConfig)
throws AxelorException {
int rowIndex = isConfig ? (isTabConfig ? 1 : 0) : 0;
if (isTabConfig && row[0] != null) {
rowIndex = 0;
isTabWithoutConfig = true;
}
if (StringUtils.contains(row[rowIndex], "Object") && isConfig) {
this.setFileTabConfig(row, fileTab, rowIndex);
} else if ((StringUtils.containsIgnoreCase(row[0], "Object")
|| (row.length > 1 && StringUtils.contains(row[1], "Object")))
&& !isConfig) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_3));
} else if (isConfig
&& (!StringUtils.containsIgnoreCase(row[0], "Object")
&& (row.length > 1 && !StringUtils.contains(row[1], "Object")))) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_4));
} else if (isConfig) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_NO_OBJECT),
fileTab.getName());
}
if ((StringUtils.isBlank(row[0])
&& (row.length > 1 && StringUtils.isBlank(row[1]))
&& linesToIgnore == 0)) {
return false;
}
return true;
}
private void applyWithConfig(
String[] row,
int line,
List<FileField> fileFieldList,
List<Integer> ignoreFields,
FileTab fileTab,
Boolean isTabConfig,
int tabConfigRowCount)
throws AxelorException, ClassNotFoundException {
Mapper mapper = getMapper(fileTab.getMetaModel().getFullName());
FileField fileField = null;
int index = 0;
for (int i = 0; i < row.length; i++) {
if (Strings.isNullOrEmpty(row[i])) {
continue;
}
index = line;
String value = row[i].trim();
if (line == 1) {
this.readFields(value, i, fileFieldList, ignoreFields, mapper, fileTab);
continue;
}
if (ignoreFields.contains(i)) {
continue;
}
if (fileFieldList.size() < i) {
break;
} else if (!isTabConfig && fileFieldList.size() <= i) {
break;
}
if (row[0] == null) {
fileField = fileFieldList.get(i - 1);
} else if (!isTabConfig && row[0] != null) {
fileField = fileFieldList.get(i);
}
if (isTabWithoutConfig) {
fileField = fileFieldList.get(i);
}
if (line == 2) {
fileField.setColumnTitle(value);
continue;
}
if (isTabConfig && i > 0 && row[0] != null && line >= 3 && line <= 11) {
fileField = fileFieldList.get(i - 1);
this.setFileFieldConfig(row, i, fileField);
}
if (isTabConfig && i > 0 && row[0] == null) {
line += -(tabConfigRowCount + 2);
this.setSampleLines(line, value, fileField);
line = index;
} else if (!isTabConfig) {
line += -2;
this.setSampleLines(line, value, fileField);
line = index;
}
}
}
@Transactional
public void applyWithoutConfig(
String[] row, int line, List<FileField> fileFieldList, FileTab fileTab, boolean isHeader)
throws AxelorException {
int index = 0;
for (int i = 0; i < row.length; i++) {
if (Strings.isNullOrEmpty(row[i])) {
continue;
}
index = line;
String value = row[i].trim();
FileField fileField = null;
if (line == 0) {
fileField = new FileField();
fileField.setIsMatchWithFile(true);
fileFieldList.add(fileField);
fileField.setFileTab(fileTab);
if (isHeader) {
fileField.setColumnTitle(value);
} else {
fileField.setFirstLine(value);
}
fileField.setSequence(i);
fileField.setFullName(fileFieldService.computeFullName(fileField));
fileField = fileFieldRepository.save(fileField);
continue;
}
if (fileFieldList.size() <= i) {
break;
}
if (!isHeader) {
line += 1;
}
if (fileField == null) {
fileField = fileFieldList.get(i);
}
setSampleLines(line, value, fileField);
line = index;
}
}
private void setSampleLines(int line, String value, FileField fileField) {
if (!StringUtils.isBlank(fileField.getTargetType())
&& fileField.getTargetType().equals("String")
&& !StringUtils.isBlank(value)
&& value.length() > 255) {
value = StringUtils.abbreviate(value, 255);
}
switch (line) {
case 1:
fileField.setFirstLine(value);
break;
case 2:
fileField.setSecondLine(value);
break;
case 3:
fileField.setThirdLine(value);
break;
default:
break;
}
}
@Transactional
public void readFields(
String value,
int index,
List<FileField> fileFieldList,
List<Integer> ignoreFields,
Mapper mapper,
FileTab fileTab)
throws AxelorException, ClassNotFoundException {
FileField fileField = new FileField();
fileField.setSequence(index);
if (Strings.isNullOrEmpty(value)) {
return;
}
String importType = StringUtils.substringBetween(value, "(", ")");
if (!Strings.isNullOrEmpty(importType)
&& (importType.equalsIgnoreCase(forSelectUseValues)
|| importType.equalsIgnoreCase(forSelectUseTitles)
|| importType.equalsIgnoreCase(forSelectUseTranslatedTitles))) {
fileField.setForSelectUse(this.getForStatusSelect(importType));
} else {
fileField.setImportType(this.getImportType(value, importType));
}
value = value.split("\\(")[0];
String importField = null;
String subImportField = null;
if (value.contains(".")) {
importField = value.substring(0, value.indexOf("."));
subImportField = value.substring(value.indexOf(".") + 1, value.length());
} else {
importField = value;
}
boolean isValid = this.checkFields(mapper, importField, subImportField);
if (isValid) {
this.setImportFields(mapper, fileField, importField, subImportField);
} else {
ignoreFields.add(index);
}
fileFieldList.add(fileField);
fileField = fileFieldRepository.save(fileField);
fileField.setFileTab(fileTab);
}
private boolean checkFields(Mapper mapper, String importField, String subImportField)
throws AxelorException, ClassNotFoundException {
if (importField != null) {
Property parentProp = mapper.getProperty(importField);
if (parentProp == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.ADVANCED_IMPORT_1),
importField,
mapper.getBeanClass().getSimpleName()));
}
if (parentProp.getType().name().equals("ONE_TO_MANY")) {
return false;
}
if (!Strings.isNullOrEmpty(subImportField)) {
String[] subFields = subImportField.split("\\.");
return this.checkSubFields(
subFields, 0, parentProp, parentProp.getEntity().getSimpleName());
} else if (parentProp.getTarget() != null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.ADVANCED_IMPORT_5),
importField,
mapper.getBeanClass().getSimpleName()));
}
}
return true;
}
private boolean checkSubFields(String[] subFields, int index, Property parentProp, String model)
throws AxelorException, ClassNotFoundException {
boolean isValid = true;
if (index < subFields.length) {
if (parentProp.getTarget() != null) {
Mapper mapper = getMapper(parentProp.getTarget().getName());
Property childProp = mapper.getProperty(subFields[index]);
if (childProp == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.ADVANCED_IMPORT_2),
subFields[index],
parentProp.getName(),
model));
}
if (childProp.getTarget() != null) {
if (childProp.getType().name().equals("ONE_TO_MANY")) {
isValid = false;
return isValid;
}
if (index != subFields.length - 1) {
isValid = this.checkSubFields(subFields, index + 1, childProp, model);
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.ADVANCED_IMPORT_5), subFields[index], model));
}
}
}
}
return isValid;
}
private void setImportFields(
Mapper mapper, FileField fileField, String importField, String subImportField) {
Property prop = mapper.getProperty(importField);
MetaField field =
metaFieldRepo
.all()
.filter(
"self.name = ?1 AND self.metaModel.name = ?2",
prop.getName(),
prop.getEntity().getSimpleName())
.fetchOne();
fileField.setImportField(field);
fileField.setIsMatchWithFile(true);
if (!Strings.isNullOrEmpty(subImportField)) {
fileField.setSubImportField(subImportField);
}
fileField.setFullName(fileFieldService.computeFullName(fileField));
fileField = fileFieldService.fillType(fileField);
if (fileField.getTargetType().equals("MetaFile")) {
fileField.setImportType(FileFieldRepository.IMPORT_TYPE_NEW);
}
}
private int getImportType(String value, String importType) {
if (Strings.isNullOrEmpty(importType)) {
if (value.contains(".")) {
return FileFieldRepository.IMPORT_TYPE_NEW;
}
return FileFieldRepository.IMPORT_TYPE_DIRECT;
}
switch (importType.toLowerCase()) {
case "find":
return FileFieldRepository.IMPORT_TYPE_FIND;
case "findnew":
return FileFieldRepository.IMPORT_TYPE_FIND_NEW;
case "new":
return FileFieldRepository.IMPORT_TYPE_NEW;
case "ignore/empty":
return FileFieldRepository.IMPORT_TYPE_IGNORE_EMPTY;
default:
if (value.contains(".")) {
return FileFieldRepository.IMPORT_TYPE_NEW;
}
return FileFieldRepository.IMPORT_TYPE_DIRECT;
}
}
private int getForStatusSelect(String importType) {
switch (importType.toLowerCase()) {
case forSelectUseTitles:
return FileFieldRepository.SELECT_USE_TITLES;
case forSelectUseValues:
return FileFieldRepository.SELECT_USE_VALUES;
case forSelectUseTranslatedTitles:
return FileFieldRepository.SELECT_USE_TRANSLATED_TITLES;
default:
return FileFieldRepository.SELECT_USE_VALUES;
}
}
protected void setFileTabConfig(String row[], FileTab fileTab, int rowIndex)
throws AxelorException {
searchFieldList = new ArrayList<String>();
String objName = row[rowIndex].split("\\:")[1].trim();
MetaModel model = metaModelRepo.findByName(objName);
fileTab.setMetaModel(model);
if (row.length >= 2 && StringUtils.containsIgnoreCase(row[rowIndex + 1], "importType")) {
String type = row[rowIndex + 1].split("\\:")[1].trim();
if (!type.equals(null)) {
fileTab.setImportType(this.getImportType(null, type));
if (!fileTab.getImportType().equals(FileFieldRepository.IMPORT_TYPE_NEW)) {
if (StringUtils.containsIgnoreCase(row[rowIndex + 2], "searchFieldSet")) {
String searchFieldtype = row[rowIndex + 2].split("\\:")[1].trim();
String[] searchFieldArr = searchFieldtype.split("\\,");
for (String search : searchFieldArr) {
searchFieldList.add(search);
}
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_6),
fileTab.getName());
}
}
}
}
}
protected void setFileFieldConfig(String[] row, Integer i, FileField fileField) {
String fieldName = row[0].trim();
switch (fieldName.toLowerCase()) {
case "forselectuse":
fileField.setForSelectUse(this.getForStatusSelect(row[i]));
break;
case "noimportif":
fileField.setNoImportIf(row[i]);
break;
case "expression":
fileField.setExpression(row[i]);
break;
case "dateformat":
fileField.setDateFormat(row[i]);
break;
case "importtype":
fileField.setImportType(Integer.parseInt(row[i]));
break;
case "subimportfield":
fileField.setSubImportField(row[i]);
break;
case "splitby":
fileField.setSplitBy(row[i]);
break;
case "defaultifnotfound":
fileField.setDefaultIfNotFound(row[i]);
break;
}
}
protected FileTab setSearchField(
FileTab fileTab, List<String> searchFieldList, List<FileField> fileFieldList) {
Set<FileField> searchFieldSet = new HashSet<FileField>();
for (String searchField : searchFieldList) {
for (FileField fileField : fileFieldList) {
if (fileField.getFullName().endsWith("- " + searchField)) {
searchFieldSet.add(fileField);
}
}
}
fileTab.setSearchFieldSet(searchFieldSet);
return fileTab;
}
@Override
public int getTabConfigRowCount(
String sheet, DataReaderService reader, int totalLines, String[] objectRow) {
int tabConfigRowCount = 0;
if (objectRow[0] == null
&& (objectRow.length > 1 && StringUtils.containsIgnoreCase(objectRow[1], "Object"))) {
int linesForTab;
for (linesForTab = 3; linesForTab < totalLines; linesForTab++) {
if (reader.read(sheet, linesForTab, 0)[0] != null) {
tabConfigRowCount++;
} else {
break;
}
}
}
return tabConfigRowCount;
}
@SuppressWarnings("unchecked")
@Override
public Mapper getMapper(String modelFullName) throws ClassNotFoundException {
Class<? extends Model> klass = (Class<? extends Model>) Class.forName(modelFullName);
return Mapper.of(klass);
}
@Override
public boolean resetImport(AdvancedImport advancedImport) throws ClassNotFoundException {
List<FileTab> fileTabList = advancedImport.getFileTabList();
Beans.get(ValidatorService.class).sortFileTabList(fileTabList);
boolean isResetValue = resetRelationalFields(fileTabList);
if (isResetValue) {
removeRecords(fileTabList);
}
return isResetValue;
}
@SuppressWarnings("unchecked")
public boolean resetRelationalFields(List<FileTab> fileTabList) throws ClassNotFoundException {
boolean isResetValue = false;
for (FileTab fileTab : fileTabList) {
Map<String, Object> jsonContextMap = dataImportService.createJsonContext(fileTab);
JsonContext jsonContext = (JsonContext) jsonContextMap.get("jsonContext");
String fieldName = inflector.camelize(fileTab.getMetaModel().getName(), true) + "Set";
List<Object> recordList = (List<Object>) jsonContext.get(fieldName);
if (CollectionUtils.isEmpty(recordList)) {
continue;
}
isResetValue = true;
Class<? extends Model> modelKlass =
(Class<? extends Model>) Class.forName(fileTab.getMetaModel().getFullName());
this.resetPropertyValue(modelKlass, recordList);
this.resetSubPropertyValue(modelKlass, jsonContext);
}
return isResetValue;
}
@Transactional
private void resetPropertyValue(Class<? extends Model> klass, List<Object> recordList)
throws ClassNotFoundException {
JpaRepository<? extends Model> modelRepo = JpaRepository.of(klass);
for (Property prop : Mapper.of(klass).getProperties()) {
if (prop.getTarget() == null || prop.isRequired()) {
continue;
}
for (Object obj : recordList) {
Map<String, Object> recordMap = Mapper.toMap(EntityHelper.getEntity(obj));
Long id = Long.valueOf(recordMap.get("id").toString());
Object bean = modelRepo.find(id);
if (bean != null) {
prop.set(bean, null);
}
}
}
}
@SuppressWarnings("unchecked")
private void resetSubPropertyValue(Class<? extends Model> klass, JsonContext jsonContext)
throws ClassNotFoundException {
for (Property prop : Mapper.of(klass).getProperties()) {
if (prop.getTarget() == null || prop.isRequired()) {
continue;
}
String simpleModelName = StringUtils.substringAfterLast(prop.getTarget().getName(), ".");
String field = inflector.camelize(simpleModelName, true) + "Set";
if (!jsonContext.containsKey(field)) {
continue;
}
List<Object> recordList = (List<Object>) jsonContext.get(field);
Class<? extends Model> modelKlass =
(Class<? extends Model>) Class.forName(prop.getTarget().getName());
this.resetPropertyValue(modelKlass, recordList);
}
}
@SuppressWarnings("unchecked")
public void removeRecords(List<FileTab> fileTabList) throws ClassNotFoundException {
for (FileTab fileTab : fileTabList) {
String targetModelName = fileTab.getMetaModel().getFullName();
Map<String, Object> jsonContextMap = dataImportService.createJsonContext(fileTab);
JsonContext jsonContext = (JsonContext) jsonContextMap.get("jsonContext");
String fieldName = inflector.camelize(fileTab.getMetaModel().getName(), true) + "Set";
List<Object> recordList = (List<Object>) jsonContext.get(fieldName);
if (CollectionUtils.isEmpty(recordList)) {
continue;
}
Class<? extends Model> modelKlass = (Class<? extends Model>) Class.forName(targetModelName);
removeRecord(fileTab, modelKlass, recordList, fileTabList);
removeSubRecords(modelKlass, jsonContext);
}
}
@SuppressWarnings("unchecked")
@Transactional
public void removeRecord(
FileTab fileTab,
Class<? extends Model> modelKlass,
List<Object> recordList,
List<FileTab> fileTabList)
throws ClassNotFoundException {
JpaRepository<? extends Model> modelRepo = JpaRepository.of(modelKlass);
for (FileTab tab : fileTabList) {
Map<String, Object> jsonContextMap = dataImportService.createJsonContext(tab);
JsonContext jsonContext = (JsonContext) jsonContextMap.get("jsonContext");
String fieldName = inflector.camelize(tab.getMetaModel().getName(), true) + "Set";
List<Object> recList = (List<Object>) jsonContext.get(fieldName);
if (CollectionUtils.isEmpty(recList)) {
continue;
}
Class<? extends Model> klass =
(Class<? extends Model>) Class.forName(tab.getMetaModel().getFullName());
Property[] props = Mapper.of(klass).getProperties();
for (Property prop : props) {
if (prop.getTarget() != null && prop.getTarget() == modelKlass && prop.isRequired()) {
removeRecord(tab, klass, recList, fileTabList);
}
}
}
String ids =
recordList
.stream()
.map(
obj -> {
Map<String, Object> recordMap = Mapper.toMap(EntityHelper.getEntity(obj));
return recordMap.get("id").toString();
})
.collect(Collectors.joining(","));
modelRepo.all().filter("self.id IN (" + ids + ")").delete();
fileTab.setAttrs(null);
LOG.debug("Reset imported data : {}", modelKlass.getSimpleName());
}
@SuppressWarnings("unchecked")
@Transactional
public void removeSubRecords(Class<? extends Model> klass, JsonContext jsonContext)
throws ClassNotFoundException {
for (Property prop : Mapper.of(klass).getProperties()) {
if (prop.getTarget() == null || prop.isCollection()) {
continue;
}
String simpleModelName = StringUtils.substringAfterLast(prop.getTarget().getName(), ".");
String field = inflector.camelize(simpleModelName, true) + "Set";
if (!jsonContext.containsKey(field)) {
continue;
}
List<Object> recList = (List<Object>) jsonContext.get(field);
String ids =
recList
.stream()
.map(
obj -> {
Map<String, Object> recordMap = Mapper.toMap(EntityHelper.getEntity(obj));
return recordMap.get("id").toString();
})
.collect(Collectors.joining(","));
JpaRepository<? extends Model> modelRepo =
JpaRepository.of((Class<? extends Model>) Class.forName(prop.getTarget().getName()));
modelRepo.all().filter("self.id IN (" + ids + ")").delete();
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.AdvancedImport;
import com.axelor.apps.base.db.FileTab;
import com.axelor.exception.AxelorException;
import com.axelor.meta.db.MetaFile;
import java.io.IOException;
import java.util.Map;
public interface DataImportService {
public MetaFile importData(AdvancedImport advanceImport)
throws IOException, AxelorException, ClassNotFoundException;
public Map<String, Object> createJsonContext(FileTab fileTab);
}

View File

@ -0,0 +1,913 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.AdvancedImport;
import com.axelor.apps.base.db.FileField;
import com.axelor.apps.base.db.FileTab;
import com.axelor.apps.base.db.repo.FileFieldRepository;
import com.axelor.apps.base.service.imports.listener.ImporterListener;
import com.axelor.apps.base.service.readers.DataReaderFactory;
import com.axelor.apps.base.service.readers.DataReaderService;
import com.axelor.apps.tool.service.TranslationService;
import com.axelor.common.Inflector;
import com.axelor.data.XStreamUtils;
import com.axelor.data.adapter.DataAdapter;
import com.axelor.data.adapter.JavaTimeAdapter;
import com.axelor.data.csv.CSVBind;
import com.axelor.data.csv.CSVConfig;
import com.axelor.data.csv.CSVImporter;
import com.axelor.data.csv.CSVInput;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.MetaSelect;
import com.axelor.meta.db.MetaSelectItem;
import com.axelor.meta.db.repo.MetaSelectItemRepository;
import com.axelor.meta.db.repo.MetaSelectRepository;
import com.axelor.rpc.Context;
import com.axelor.rpc.JsonContext;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.opencsv.CSVWriter;
import com.thoughtworks.xstream.XStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
public class DataImportServiceImpl implements DataImportService {
private static final char CSV_SEPRATOR = ';';
private static final String INPUT_CALLABLE =
"com.axelor.csv.script.ImportAdvancedImport:importGeneral";
private static final String BIND_CALLABLE_DATE =
"call:com.axelor.csv.script.ImportDateTime:importDate";
private static final String BIND_CALLABLE_DATE_TIME =
"call:com.axelor.csv.script.ImportDateTime:importDateTime";
private static final String BIND_CALLABLE_META_FILE =
"call:com.axelor.csv.script.ImportAdvancedImport:importPicture";
private static final String MANY_TO_MANY = "ManyToMany";
private static final String SPLIT = ".split('\\\\";
private static final String AS_LIST = "') as List";
private static final String REPLACE_SYMBOL = "$";
private CSVInput csvInput;
private Map<String, CSVBind> parentBindMap;
private Map<String, CSVBind> subBindMap;
private Map<String, Object> importContext;
private Map<String, Object> fieldMap;
private Map<String, Object> titleMap;
private Map<String, DataAdapter> adapterMap;
private List<String> ifList;
private String fullFieldName;
private String language;
private File dataDir;
private int counter = 1;
private Inflector inflector = Inflector.getInstance();
@Inject DataReaderFactory dataReaderFactory;
@Inject private MetaFiles metaFiles;
@Inject private MetaSelectItemRepository metaSelectItemRepo;
@Inject private TranslationService translationService;
@Inject private ValidatorService validatorService;
@Inject private AdvancedImportService advancedImportService;
@Inject private MetaSelectRepository metaSelectRepo;
@Override
public MetaFile importData(AdvancedImport advancedImport)
throws IOException, AxelorException, ClassNotFoundException {
adapterMap = new HashMap<String, DataAdapter>();
importContext = new HashMap<String, Object>();
language = advancedImport.getLanguageSelect();
dataDir = Files.createTempDir();
String extension = Files.getFileExtension(advancedImport.getImportFile().getFileName());
DataReaderService reader = dataReaderFactory.getDataReader(extension);
reader.initialize(advancedImport.getImportFile(), advancedImport.getFileSeparator());
List<CSVInput> inputs = this.process(reader, advancedImport);
if (advancedImport.getAttachment() != null) {
this.processAttachments(advancedImport.getAttachment());
}
MetaFile logFile = this.importData(inputs);
FileUtils.forceDelete(dataDir);
return logFile;
}
private List<CSVInput> process(DataReaderService reader, AdvancedImport advancedImport)
throws AxelorException, IOException, ClassNotFoundException {
String[] sheets = reader.getSheetNames();
boolean isConfig = advancedImport.getIsConfigInFile();
int linesToIgnore = advancedImport.getNbOfFirstLineIgnore();
boolean isTabConfig = advancedImport.getIsFileTabConfigAdded();
List<CSVInput> inputList = new ArrayList<CSVInput>();
validatorService.sortFileTabList(advancedImport.getFileTabList());
for (FileTab fileTab : advancedImport.getFileTabList()) {
if (!Arrays.stream(sheets).anyMatch(sheet -> sheet.equals(fileTab.getName()))) {
continue;
}
this.initializeVariables();
String fileName = createDataFileName(fileTab);
csvInput = this.createCSVInput(fileTab, fileName);
ifList = new ArrayList<String>();
CSVWriter csvWriter =
new CSVWriter(new FileWriter(new File(dataDir, fileName)), CSV_SEPRATOR);
int totalLines = reader.getTotalLines(fileTab.getName());
if (totalLines == 0) {
continue;
}
Mapper mapper = advancedImportService.getMapper(fileTab.getMetaModel().getFullName());
List<String[]> allLines = new ArrayList<String[]>();
int startIndex = isConfig ? 1 : linesToIgnore;
String[] row = reader.read(fileTab.getName(), startIndex, 0);
String[] headers = this.createHeader(row, fileTab, isConfig, mapper);
allLines.add(headers);
int tabConfigRowCount = 0;
if (isTabConfig) {
String objectRow[] = reader.read(fileTab.getName(), 0, 0);
tabConfigRowCount =
advancedImportService.getTabConfigRowCount(
fileTab.getName(), reader, totalLines, objectRow);
}
startIndex =
isConfig
? tabConfigRowCount + 3
: fileTab.getAdvancedImport().getIsHeader() ? linesToIgnore + 1 : linesToIgnore;
for (int line = startIndex; line < totalLines; line++) {
String[] dataRow = reader.read(fileTab.getName(), line, row.length);
if (dataRow == null) {
continue;
}
String[] data = this.createData(dataRow, fileTab, isConfig, mapper);
allLines.add(data);
}
csvWriter.writeAll(allLines);
csvWriter.flush();
csvWriter.close();
inputList.add(csvInput);
importContext.put("ifConditions" + fileTab.getId(), ifList);
importContext.put("jsonContextValues" + fileTab.getId(), createJsonContext(fileTab));
}
return inputList;
}
private void initializeVariables() {
parentBindMap = new HashMap<>();
subBindMap = new HashMap<>();
fullFieldName = null;
fieldMap = new HashMap<>();
titleMap = new HashMap<>();
}
private String[] createHeader(String[] row, FileTab fileTab, boolean isConfig, Mapper mapper)
throws ClassNotFoundException {
Map<String, Object> map = isConfig ? fieldMap : titleMap;
int rowCount = row.length;
for (int cell = 0; cell < rowCount; cell++) {
if (Strings.isNullOrEmpty(row[cell])) {
continue;
}
String value = row[cell].trim();
map.put(isConfig ? value.contains("(") ? value.split("\\(")[0] : value : value, cell);
}
validatorService.sortFileFieldList(fileTab.getFileFieldList());
List<String> headers = new ArrayList<>();
List<CSVBind> allBindings = new ArrayList<CSVBind>();
int cnt = 0;
Map<String, Object> searchMap = new HashMap<String, Object>();
for (FileField fileField : fileTab.getFileFieldList()) {
if (fileField.getImportType() == FileFieldRepository.IMPORT_TYPE_IGNORE_EMPTY) {
continue;
}
String key = (isConfig) ? validatorService.getField(fileField) : fileField.getColumnTitle();
String column = ("cell" + (cnt + 1));
if (!CollectionUtils.isEmpty(fileTab.getSearchFieldSet())
&& fileTab.getSearchFieldSet().contains(fileField)
&& fileTab.getImportType() != FileFieldRepository.IMPORT_TYPE_NEW) {
searchMap.put(validatorService.getField(fileField), column);
}
if ((map.containsKey(key) || (!isConfig && !fileTab.getAdvancedImport().getIsHeader()))
&& fileField.getIsMatchWithFile()) {
headers.add(column);
}
allBindings = this.createCSVBinding(column, fileField, mapper, allBindings);
cnt++;
}
CSVBind fileTabBind = new CSVBind();
fileTabBind.setField("fileTabId");
fileTabBind.setExpression(fileTab.getId().toString());
allBindings.add(fileTabBind);
for (Entry<String, Object> entry : searchMap.entrySet()) {
if (Strings.isNullOrEmpty(csvInput.getSearch())) {
csvInput.setSearch("self." + entry.getKey() + " = :" + entry.getValue());
} else {
csvInput.setSearch(
csvInput.getSearch() + " AND self." + entry.getKey() + " = :" + entry.getValue());
}
}
csvInput.setBindings(allBindings);
return headers.stream().toArray(String[]::new);
}
private String[] createData(String[] dataRow, FileTab fileTab, boolean isConfig, Mapper mapper)
throws ClassNotFoundException {
Map<String, Object> map = isConfig ? fieldMap : titleMap;
List<String> dataList = new ArrayList<String>();
for (int fieldIndex = 0; fieldIndex < fileTab.getFileFieldList().size(); fieldIndex++) {
FileField fileField = fileTab.getFileFieldList().get(fieldIndex);
String key = null;
if (isConfig) {
key = validatorService.getField(fileField);
} else {
key = fileField.getColumnTitle();
}
if (!fileField.getIsMatchWithFile()
|| fileField.getImportType() == FileFieldRepository.IMPORT_TYPE_IGNORE_EMPTY) {
continue;
}
int cell = 0;
if (map.containsKey(key)) {
cell = (int) map.get(key);
}
cell = (!isConfig && !fileTab.getAdvancedImport().getIsHeader()) ? fieldIndex : cell;
if (Strings.isNullOrEmpty(dataRow[cell])) {
dataList.add(
!Strings.isNullOrEmpty(fileField.getDefaultIfNotFound())
? fileField.getDefaultIfNotFound().trim()
: "");
continue;
}
String dataCell = dataRow[cell].trim();
this.checkAndWriteData(dataCell, fileTab.getMetaModel(), fileField, mapper, dataList);
}
return dataList.stream().toArray(String[]::new);
}
private void checkAndWriteData(
String dataCell, MetaModel model, FileField fileField, Mapper mapper, List<String> dataList)
throws ClassNotFoundException {
Property parentProp = mapper.getProperty(fileField.getImportField().getName());
if (Strings.isNullOrEmpty(fileField.getSubImportField())) {
if (!Strings.isNullOrEmpty(parentProp.getSelection())) {
this.writeSelectionData(
parentProp.getSelection(), dataCell, fileField.getForSelectUse(), dataList);
} else {
dataList.add(dataCell);
}
} else {
String[] subFields = fileField.getSubImportField().split("\\.");
this.checkSubFieldAndWriteData(
subFields, 0, parentProp, dataCell, fileField.getForSelectUse(), dataList);
}
}
private void checkSubFieldAndWriteData(
String[] subFields,
int index,
Property parentProp,
String dataCell,
int forSelectUse,
List<String> dataList)
throws ClassNotFoundException {
if (index < subFields.length) {
if (parentProp.getTarget() != null) {
Mapper mapper = advancedImportService.getMapper(parentProp.getTarget().getName());
Property childProp = mapper.getProperty(subFields[index]);
if (childProp != null && childProp.getTarget() != null) {
this.checkSubFieldAndWriteData(
subFields, index + 1, childProp, dataCell, forSelectUse, dataList);
} else {
if (!Strings.isNullOrEmpty(childProp.getSelection())) {
this.writeSelectionData(childProp.getSelection(), dataCell, forSelectUse, dataList);
} else {
dataList.add(dataCell);
}
}
}
}
}
private void writeSelectionData(
String selection, String dataCell, int forSelectUse, List<String> dataList) {
String value = this.getSelectionValue(selection, dataCell, forSelectUse);
if (value == null) {
return;
}
dataList.add(value);
}
private String getSelectionValue(String selection, String value, int forSelectUse) {
if (forSelectUse != FileFieldRepository.SELECT_USE_VALUES) {
String title = null;
if (forSelectUse == FileFieldRepository.SELECT_USE_TRANSLATED_TITLES) {
title = translationService.getTranslationKey(value, language);
} else {
title = value;
}
MetaSelect metaSelect = metaSelectRepo.findByName(selection);
if (metaSelect == null) {
return null;
}
MetaSelectItem metaSelectItem =
metaSelectItemRepo
.all()
.filter("self.title = ?1 AND self.select.id = ?2", title, metaSelect.getId())
.fetchOne();
if (metaSelectItem != null) {
return metaSelectItem.getValue();
} else {
return value;
}
} else {
return value;
}
}
private CSVInput createCSVInput(FileTab fileTab, String fileName) {
boolean update = false;
int importType = fileTab.getImportType();
if ((importType == FileFieldRepository.IMPORT_TYPE_FIND)
&& !CollectionUtils.isEmpty(fileTab.getSearchFieldSet())) {
update = true;
}
XStream stream = XStreamUtils.createXStream();
stream.processAnnotations(CSVInput.class);
CSVInput input = (CSVInput) stream.fromXML("<input update=\"" + update + "\" />");
input.setFileName(fileName);
input.setSeparator(CSV_SEPRATOR);
input.setTypeName(fileTab.getMetaModel().getFullName());
input.setCallable(INPUT_CALLABLE);
input.setSearch(null);
input.setBindings(new ArrayList<>());
return input;
}
private List<CSVBind> createCSVBinding(
String column, FileField fileField, Mapper mapper, List<CSVBind> allBindings)
throws ClassNotFoundException {
Property prop = mapper.getProperty(fileField.getImportField().getName());
if (prop == null) {
return allBindings;
}
CSVBind dummyBind = null;
if (!fileField.getIsMatchWithFile()) {
dummyBind = this.createCSVBind(null, column, null, null, null, null);
allBindings.add(0, dummyBind);
}
if (Strings.isNullOrEmpty(fileField.getSubImportField())) {
String expression = this.setExpression(column, fileField, prop);
String adapter = null;
String dateFormat = fileField.getDateFormat();
if (Strings.isNullOrEmpty(expression) && !Strings.isNullOrEmpty(dateFormat)) {
adapter = this.getAdapter(prop.getJavaType().getSimpleName(), dateFormat.trim());
}
CSVBind bind = null;
if (!fileField.getIsMatchWithFile()) {
dummyBind.setExpression(expression);
dummyBind.setAdapter(adapter);
bind = this.createCSVBind(column, prop.getName(), null, null, null, null);
this.setImportIf(prop, bind, column);
} else {
bind = this.createCSVBind(column, prop.getName(), null, expression, adapter, null);
this.setImportIf(prop, bind, column);
}
allBindings.add(bind);
this.setSearch(column, prop.getName(), fileField, null);
} else {
CSVBind parentBind = null;
if (parentBindMap.containsKey(prop.getName())) {
parentBind = parentBindMap.get(prop.getName());
} else {
parentBind = this.createCSVBind(null, prop.getName(), null, null, null, true);
parentBind.setBindings(new ArrayList<>());
allBindings.add(parentBind);
parentBindMap.put(prop.getName(), parentBind);
}
fullFieldName = prop.getName();
String[] subFields = fileField.getSubImportField().split("\\.");
this.createCSVSubBinding(subFields, 0, column, prop, fileField, parentBind, dummyBind);
}
if (!Strings.isNullOrEmpty(fileField.getNoImportIf())) {
String importIf =
this.convertExpression(
fileField.getNoImportIf().trim(), fileField.getTargetType(), column);
ifList.add(importIf);
}
return allBindings;
}
private void createCSVSubBinding(
String[] subFields,
int index,
String column,
Property parentProp,
FileField fileField,
CSVBind parentBind,
CSVBind dummyBind)
throws ClassNotFoundException {
if (index < subFields.length) {
if (parentProp.getTarget() == null) {
return;
}
int importType = fileField.getImportType();
String relationship = fileField.getRelationship();
fullFieldName += "." + subFields[index];
Mapper mapper = advancedImportService.getMapper(parentProp.getTarget().getName());
Property childProp = mapper.getProperty(subFields[index]);
if (childProp != null && childProp.getTarget() != null) {
CSVBind subBind = null;
if (subBindMap.containsKey(fullFieldName)) {
subBind = subBindMap.get(fullFieldName);
} else {
subBind = this.createCSVBind(null, childProp.getName(), null, null, null, true);
subBind.setBindings(new ArrayList<>());
parentBind.getBindings().add(subBind);
subBindMap.put(fullFieldName, subBind);
}
this.createCSVSubBinding(
subFields, index + 1, column, childProp, fileField, subBind, dummyBind);
} else {
String expression = this.setExpression(column, fileField, childProp);
String adapter = null;
String dateFormat = fileField.getDateFormat();
if (Strings.isNullOrEmpty(expression) && !Strings.isNullOrEmpty(dateFormat)) {
adapter = this.getAdapter(childProp.getJavaType().getSimpleName(), dateFormat.trim());
}
if (!fileField.getIsMatchWithFile()) {
this.createBindForNotMatchWithFile(
column, importType, dummyBind, expression, adapter, parentBind, childProp);
} else {
this.createBindForMatchWithFile(
column, importType, expression, adapter, relationship, parentBind, childProp);
}
this.setSearch(column, childProp.getName(), fileField, parentBind);
if (importType != FileFieldRepository.IMPORT_TYPE_FIND) {
parentBind.setUpdate(false);
}
}
}
}
private void createBindForNotMatchWithFile(
String column,
int importType,
CSVBind dummyBind,
String expression,
String adapter,
CSVBind parentBind,
Property childProp) {
dummyBind.setExpression(expression);
dummyBind.setAdapter(adapter);
if (importType == FileFieldRepository.IMPORT_TYPE_FIND_NEW
|| importType == FileFieldRepository.IMPORT_TYPE_NEW) {
CSVBind subBind = this.createCSVBind(column, childProp.getName(), null, null, null, null);
this.setImportIf(childProp, subBind, column);
parentBind.getBindings().add(subBind);
}
}
private void createBindForMatchWithFile(
String column,
int importType,
String expression,
String adapter,
String relationship,
CSVBind parentBind,
Property childProp) {
if (importType != FileFieldRepository.IMPORT_TYPE_FIND) {
if (!Strings.isNullOrEmpty(expression)
&& expression.contains(BIND_CALLABLE_META_FILE)
&& importType == FileFieldRepository.IMPORT_TYPE_NEW) {
parentBind.setExpression(expression);
return;
}
CSVBind subBind =
this.createCSVBind(column, childProp.getName(), null, expression, adapter, null);
this.setImportIf(childProp, subBind, column);
parentBind.getBindings().add(subBind);
} else {
if (!Strings.isNullOrEmpty(relationship) && relationship.equals(MANY_TO_MANY)) {
parentBind.setColumn(column);
parentBind.setExpression(expression);
}
}
}
private void setSearch(String column, String field, FileField fileField, CSVBind bind) {
int importType = fileField.getImportType();
String relationship = fileField.getRelationship();
String splitBy = fileField.getSplitBy();
if (importType == FileFieldRepository.IMPORT_TYPE_FIND
|| importType == FileFieldRepository.IMPORT_TYPE_FIND_NEW) {
if (Strings.isNullOrEmpty(bind.getSearch())) {
if (!Strings.isNullOrEmpty(relationship)
&& relationship.equals(MANY_TO_MANY)
&& !Strings.isNullOrEmpty(splitBy)) {
bind.setSearch("self." + field + " in :" + column);
} else {
bind.setSearch("self." + field + " = :" + column);
}
} else {
bind.setSearch(bind.getSearch() + " AND " + "self." + field + " = :" + column);
}
}
}
private String setExpression(String column, FileField fileField, Property prop) {
String expr = fileField.getExpression();
String relationship = fileField.getRelationship();
String splitBy = fileField.getSplitBy();
String targetType = fileField.getTargetType();
String expression = null;
if (!Strings.isNullOrEmpty(expr)) {
expr = expr.trim();
if (expr.contains(REPLACE_SYMBOL) && !Strings.isNullOrEmpty(column)) {
if (!Strings.isNullOrEmpty(relationship) && relationship.equals(MANY_TO_MANY)) {
expression =
!Strings.isNullOrEmpty(splitBy)
? expr.replace(REPLACE_SYMBOL, column) + SPLIT + splitBy + AS_LIST
: expr.replace(REPLACE_SYMBOL, column);
return expression;
}
return this.convertExpression(expr, targetType, column);
} else {
if (!Strings.isNullOrEmpty(relationship) && relationship.equals(MANY_TO_MANY)) {
expression =
!Strings.isNullOrEmpty(splitBy) ? expr + SPLIT + splitBy + AS_LIST : "'" + expr + "'";
return expression;
}
return this.createExpression(expr, targetType, prop, fileField.getForSelectUse());
}
} else if (!Strings.isNullOrEmpty(relationship)
&& relationship.equals(MANY_TO_MANY)
&& !targetType.equals("MetaFile")) {
expression =
(!Strings.isNullOrEmpty(fileField.getSplitBy()))
? column + SPLIT + splitBy + AS_LIST
: null;
return expression;
} else if (!Strings.isNullOrEmpty(targetType) && targetType.equals("MetaFile")) {
expression = BIND_CALLABLE_META_FILE + "(" + column + ", '" + dataDir.toPath() + "')";
return expression;
}
return expression;
}
private String createExpression(String expr, String type, Property prop, int forSelectUse) {
String expression = null;
switch (type) {
case ValidatorService.LOCAL_DATE:
expression = BIND_CALLABLE_DATE + "('" + expr + "')";
break;
case ValidatorService.LOCAL_TIME:
case ValidatorService.LOCAL_DATE_TIME:
case ValidatorService.ZONED_DATE_TIME:
expression = BIND_CALLABLE_DATE_TIME + "('" + expr + "')";
break;
case ValidatorService.INTEGER:
case ValidatorService.BIG_DECIMAL:
case ValidatorService.LONG:
case ValidatorService.STRING:
if (!Strings.isNullOrEmpty(prop.getSelection())) {
expression = this.getSelectionValue(prop.getSelection(), expr, forSelectUse);
if (type.equals(ValidatorService.STRING)) {
expression = "'" + expression + "'";
}
} else {
expression = type.equals(ValidatorService.STRING) ? "'" + expr + "'" : expr;
}
break;
default:
expression = "'" + expr + "'";
break;
}
return expression;
}
private String convertExpression(String expr, String type, String column) {
String expression = null;
switch (type) {
case ValidatorService.INTEGER:
expression = expr.replace(REPLACE_SYMBOL, "Integer.parseInt(" + column + ")");
break;
case ValidatorService.LONG:
expression = expr.replace(REPLACE_SYMBOL, "Long.parseLong(" + column + ")");
break;
case ValidatorService.BIG_DECIMAL:
expression = expr.replace(REPLACE_SYMBOL, "new BigDecimal(" + column + ")");
break;
case ValidatorService.LOCAL_DATE:
expression = BIND_CALLABLE_DATE + "('" + expr.replace(REPLACE_SYMBOL, column) + "')";
break;
case ValidatorService.LOCAL_TIME:
case ValidatorService.LOCAL_DATE_TIME:
case ValidatorService.ZONED_DATE_TIME:
expression = BIND_CALLABLE_DATE_TIME + "('" + expr.replace(REPLACE_SYMBOL, column) + "')";
break;
default:
expression = expr.replace(REPLACE_SYMBOL, column);
break;
}
return expression;
}
private String getAdapter(String type, String dateFormat) {
String adapter = null;
switch (type) {
case "LocalDate":
DataAdapter dateAdapter = this.createAdapter("LocalDate", dateFormat);
adapter = dateAdapter.getName();
break;
case "LocalDateTime":
DataAdapter dateTimeAdapter = this.createAdapter("LocalDateTime", dateFormat);
adapter = dateTimeAdapter.getName();
break;
case "LocalTime":
DataAdapter timeAdapter = this.createAdapter("LocalTime", dateFormat);
adapter = timeAdapter.getName();
break;
case "ZoneDateTime":
DataAdapter zonedDateTimeAdapter = this.createAdapter("ZonedDateTime", dateFormat);
adapter = zonedDateTimeAdapter.getName();
break;
}
return adapter;
}
private DataAdapter createAdapter(String typeName, String format) {
DataAdapter adapter = adapterMap.get(format);
if (adapter == null) {
adapter =
new DataAdapter(
("Adapter" + counter), JavaTimeAdapter.class, "type", typeName, "format", format);
adapterMap.put(format, adapter);
counter++;
}
return adapter;
}
private CSVBind createCSVBind(
String column,
String field,
String search,
String expression,
String adapter,
Boolean update) {
CSVBind bind = new CSVBind();
bind.setColumn(column);
bind.setField(field);
bind.setSearch(search);
bind.setExpression(expression);
bind.setAdapter(adapter);
if (update != null) {
bind.setUpdate(update);
}
return bind;
}
private String createDataFileName(FileTab fileTab) {
String fileName = null;
MetaModel model = fileTab.getMetaModel();
Long fileTabId = fileTab.getId();
try {
Mapper mapper = advancedImportService.getMapper(model.getFullName());
fileName =
inflector.camelize(mapper.getBeanClass().getSimpleName(), true) + fileTabId + ".csv";
} catch (ClassNotFoundException e) {
TraceBackService.trace(e);
}
return fileName;
}
private void processAttachments(MetaFile attachments) throws ZipException, IOException {
if (dataDir.isDirectory() && dataDir.list().length == 0) {
return;
}
File attachmentFile = MetaFiles.getPath(attachments).toFile();
ZipInputStream zis = new ZipInputStream(new FileInputStream(attachmentFile));
ZipEntry ze;
byte[] buffer = new byte[1024];
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
File extractFile = new File(dataDir, fileName);
if (ze.isDirectory()) {
extractFile.mkdirs();
continue;
} else {
extractFile.getParentFile().mkdirs();
extractFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(extractFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
zis.closeEntry();
}
zis.close();
}
private MetaFile importData(List<CSVInput> inputs) throws IOException {
if (CollectionUtils.isEmpty(inputs)) {
return null;
}
CSVConfig config = new CSVConfig();
config.setInputs(inputs);
if (!CollectionUtils.isEmpty(adapterMap.values())) {
config.getAdapters().addAll(adapterMap.values());
}
CSVImporter importer = new CSVImporter(config, dataDir.getAbsolutePath());
ImporterListener listener = new ImporterListener("importData");
importer.addListener(listener);
importer.setContext(importContext);
importer.run();
if (!listener.isImported()) {
MetaFile logFile = this.createImportLogFile(listener);
return logFile;
}
return null;
}
private void setImportIf(Property prop, CSVBind bind, String column) {
if (prop.isRequired()) {
bind.setCondition(column.toString() + "!= null && !" + column.toString() + ".empty");
}
}
private MetaFile createImportLogFile(ImporterListener listener) throws IOException {
MetaFile logMetaFile =
metaFiles.upload(
new ByteArrayInputStream(listener.getImportLog().getBytes()),
"importLog-" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".log");
return logMetaFile;
}
@Override
public Map<String, Object> createJsonContext(FileTab fileTab) {
Class<? extends Model> klass = (Class<? extends Model>) fileTab.getClass();
Context context = new Context(klass);
JsonContext jsonContext =
new JsonContext(context, Mapper.of(klass).getProperty("attrs"), fileTab.getAttrs());
Map<String, Object> _map = new HashMap<String, Object>();
_map.put("context", context);
_map.put("jsonContext", jsonContext);
return _map;
}
}

View File

@ -0,0 +1,105 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.i18n.I18n;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelLogWriter {
private Workbook workbook;
private Sheet sheet;
private File excelFile;
public void initialize(String fileName) throws IOException {
excelFile = File.createTempFile(fileName, ".xlsx");
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(fileName);
}
public void writeHeader(String[] header) {
Row headerRow = sheet.createRow(sheet.getFirstRowNum());
for (int col = 0; col < header.length; col++) {
Cell headerCell = headerRow.createCell(col);
headerCell.setCellValue(I18n.get(header[col]));
}
}
public void writeBody(Map<String, Map<String, List<Integer>>> dataMap) {
CellStyle cellStyle = this.setStyle();
for (Entry<String, Map<String, List<Integer>>> keyEntry : dataMap.entrySet()) {
Row titleRow = sheet.createRow(sheet.getLastRowNum() + 1);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(cellStyle);
titleCell.setCellValue(keyEntry.getKey());
for (Entry<String, List<Integer>> dataEntry : keyEntry.getValue().entrySet()) {
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);
Row dataRow = sheet.createRow(sheet.getLastRowNum() + 1);
Cell dataCell1 = dataRow.createCell(0);
dataCell1.setCellValue(dataEntry.getKey());
Cell dataCell2 = dataRow.createCell(1);
if (!CollectionUtils.isEmpty(dataEntry.getValue())) {
dataCell2.setCellValue(
dataEntry
.getValue()
.stream()
.map(num -> String.valueOf(num))
.collect(Collectors.joining(",")));
}
}
}
}
private CellStyle setStyle() {
CellStyle cellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
cellStyle.setFont(font);
return cellStyle;
}
public void close() throws IOException {
FileOutputStream fout = new FileOutputStream(excelFile);
workbook.write(fout);
fout.close();
}
public File getExcelFile() {
return excelFile;
}
}

View File

@ -0,0 +1,27 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.FileField;
public interface FileFieldService {
public FileField fillType(FileField fileField);
public String computeFullName(FileField fileField);
}

View File

@ -0,0 +1,105 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.FileField;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaFieldRepository;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.google.common.base.Strings;
import com.google.inject.Inject;
public class FileFieldServiceImpl implements FileFieldService {
@Inject private MetaModelRepository metaModelRepo;
@Inject MetaFieldRepository metaFieldRepo;
@Override
public FileField fillType(FileField fileField) {
String targetType = null;
String relationship = null;
if (fileField.getImportField() != null) {
targetType = fileField.getImportField().getTypeName();
relationship = fileField.getImportField().getRelationship();
if (!Strings.isNullOrEmpty(fileField.getSubImportField())) {
String[] subFields = fileField.getSubImportField().split("\\.");
String[] types = this.getSubFieldType(subFields, 0, fileField.getImportField());
targetType = types[0];
relationship = types[1];
}
}
fileField.setTargetType(targetType);
fileField.setRelationship(relationship);
return fileField;
}
private String[] getSubFieldType(String[] subFields, int index, MetaField parentField) {
String[] types = new String[2];
if (index < subFields.length) {
types[0] = parentField.getTypeName();
types[1] = parentField.getRelationship();
MetaModel childModel = metaModelRepo.findByName(parentField.getTypeName());
if (childModel != null) {
MetaField childField = metaFieldRepo.findByModel(subFields[index], childModel);
if (childField == null) {
return types;
}
if (childField.getRelationship() != null) {
if (!types[0].equals("MetaFile")) {
types[0] = childField.getTypeName();
}
types[1] = childField.getRelationship();
types = this.getSubFieldType(subFields, index + 1, childField);
} else {
if (!types[0].equals("MetaFile")) {
types[0] = childField.getTypeName();
}
}
}
} else {
if ((!Strings.isNullOrEmpty(types[0]) && !types[0].equals("MetaFile"))
|| Strings.isNullOrEmpty(types[0])) {
types[0] = parentField.getTypeName();
}
types[1] = parentField.getRelationship();
}
return types;
}
@Override
public String computeFullName(FileField fileField) {
String fullName = fileField.getSequence().toString();
if (!Strings.isNullOrEmpty(fileField.getColumnTitle())) {
fullName += " - " + fileField.getColumnTitle();
}
if (fileField.getImportField() != null) {
fullName += " - " + fileField.getImportField().getName();
}
if (!Strings.isNullOrEmpty(fileField.getSubImportField())) {
fullName += "." + fileField.getSubImportField();
}
return fullName;
}
}

View File

@ -0,0 +1,29 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.FileTab;
public interface FileTabService {
public FileTab updateFields(FileTab fileTab) throws ClassNotFoundException;
public FileTab compute(FileTab fileTab);
public String getShowRecordIds(FileTab fileTab, String field) throws ClassNotFoundException;
}

View File

@ -0,0 +1,150 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.FileField;
import com.axelor.apps.base.db.FileTab;
import com.axelor.apps.base.db.repo.FileFieldRepository;
import com.axelor.db.EntityHelper;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaFieldRepository;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.axelor.rpc.Context;
import com.axelor.rpc.JsonContext;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
public class FileTabServiceImpl implements FileTabService {
@Inject MetaFieldRepository metaFieldRepo;
@Inject FileFieldService fileFieldService;
@Override
public FileTab updateFields(FileTab fileTab) throws ClassNotFoundException {
MetaModel model = fileTab.getMetaModel();
if (model == null || CollectionUtils.isEmpty(fileTab.getFileFieldList())) {
return fileTab;
}
Beans.get(ValidatorService.class).sortFileFieldList(fileTab.getFileFieldList());
for (FileField fileField : fileTab.getFileFieldList()) {
MetaField importField =
metaFieldRepo
.all()
.filter(
"self.label = ?1 AND self.metaModel.id = ?2",
fileField.getColumnTitle(),
model.getId())
.fetchOne();
if (importField != null) {
String relationship = importField.getRelationship();
if (!Strings.isNullOrEmpty(relationship) && relationship.equals("OneToMany")) {
continue;
}
fileField.setImportField(importField);
if (!Strings.isNullOrEmpty(relationship)) {
String subImportField = this.getSubImportField(importField);
fileField.setSubImportField(subImportField);
}
fileField = fileFieldService.fillType(fileField);
if (!Strings.isNullOrEmpty(relationship) && !fileField.getTargetType().equals("MetaFile")) {
fileField.setImportType(FileFieldRepository.IMPORT_TYPE_FIND);
} else {
if (!Strings.isNullOrEmpty(fileField.getTargetType())
&& fileField.getTargetType().equals("MetaFile")) {
fileField.setImportType(FileFieldRepository.IMPORT_TYPE_NEW);
}
}
fileField.setFullName(fileFieldService.computeFullName(fileField));
} else {
fileField.setImportField(null);
fileField.setSubImportField(null);
}
}
return fileTab;
}
private String getSubImportField(MetaField importField) throws ClassNotFoundException {
String modelName = importField.getTypeName();
MetaModel metaModel = Beans.get(MetaModelRepository.class).findByName(modelName);
AdvancedImportService advancedImportService = Beans.get(AdvancedImportService.class);
Mapper mapper = advancedImportService.getMapper(metaModel.getFullName());
return (mapper != null && mapper.getNameField() != null)
? mapper.getNameField().getName()
: null;
}
@Override
public FileTab compute(FileTab fileTab) {
if (CollectionUtils.isEmpty(fileTab.getFileFieldList())) {
return fileTab;
}
for (FileField fileField : fileTab.getFileFieldList()) {
fileField.setFullName(fileFieldService.computeFullName(fileField));
}
return fileTab;
}
@SuppressWarnings("unchecked")
@Override
public String getShowRecordIds(FileTab fileTab, String field) throws ClassNotFoundException {
Context context = new Context(fileTab.getClass());
Class<? extends Model> klass =
(Class<? extends Model>) Class.forName(fileTab.getClass().getName());
JsonContext jsonContext =
new JsonContext(context, Mapper.of(klass).getProperty("attrs"), fileTab.getAttrs());
List<Object> recordList = (List<Object>) jsonContext.get(field);
if (CollectionUtils.isEmpty(recordList)) {
return null;
}
String ids =
recordList
.stream()
.map(
obj -> {
Map<String, Object> recordMap = Mapper.toMap(EntityHelper.getEntity(obj));
return recordMap.get("id").toString();
})
.collect(Collectors.joining(","));
return ids;
}
}

View File

@ -0,0 +1,82 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class LogService {
public static final String COMMON_KEY = "Common log";
public static final String HEADER_COL1 = "Field names";
public static final String HEADER_COL2 = "Row numbers";
private String fileName;
private Map<String, Map<String, List<Integer>>> logMap;
@Inject private ExcelLogWriter logWriter;
public void initialize(String fileName) {
this.fileName = fileName;
logMap = new LinkedHashMap<String, Map<String, List<Integer>>>();
}
public void addLog(String key, String log, Integer rowNumber) {
Map<String, List<Integer>> map;
List<Integer> list;
if (!logMap.containsKey(key)) {
logMap.put(key, new LinkedHashMap<String, List<Integer>>());
}
map = logMap.get(key);
if (!map.containsKey(log)) {
map.put(log, new ArrayList<Integer>());
}
list = map.get(log);
if (rowNumber != null) {
list.add(rowNumber);
}
}
public void write() throws IOException {
logWriter.initialize(fileName);
logWriter.writeHeader(new String[] {HEADER_COL1, HEADER_COL2});
logWriter.writeBody(logMap);
}
public void close() throws IOException {
logWriter.close();
}
public boolean isLogGenerated() {
if (!logMap.isEmpty()) {
return true;
}
return false;
}
public File getLogFile() {
return logWriter.getExcelFile();
}
}

View File

@ -0,0 +1,814 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advanced.imports;
import com.axelor.apps.base.db.AdvancedImport;
import com.axelor.apps.base.db.FileField;
import com.axelor.apps.base.db.FileTab;
import com.axelor.apps.base.db.repo.FileFieldRepository;
import com.axelor.apps.base.db.repo.FileTabRepository;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.readers.DataReaderFactory;
import com.axelor.apps.base.service.readers.DataReaderService;
import com.axelor.common.Inflector;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaJsonField;
import com.axelor.meta.db.repo.MetaJsonFieldRepository;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
public class ValidatorService {
public static final String STRING = "String";
public static final String INTEGER = "Integer";
public static final String LONG = "Long";
public static final String BIG_DECIMAL = "BigDecimal";
public static final String BOOLEAN = "Boolean";
public static final String ZONED_DATE_TIME = "ZonedDateTime";
public static final String LOCAL_DATE_TIME = "LocalDateTime";
public static final String LOCAL_TIME = "LocalTime";
public static final String LOCAL_DATE = "LocalDate";
private Map<String, Object> titleMap;
private Map<String, Object> fieldMap;
@Inject private MetaFiles metaFiles;
@Inject private AdvancedImportService advancedImportService;
@Inject private FileTabRepository fileTabRepo;
@Inject private DataReaderFactory dataReaderFactory;
@Inject private LogService logService;
@Inject private MetaJsonFieldRepository metaJsonFieldRepo;
public boolean validate(AdvancedImport advancedImport)
throws AxelorException, IOException, ClassNotFoundException {
if (advancedImport.getImportFile() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_NO_IMPORT_FILE));
}
if (advancedImport.getAttachment() != null
&& !Files.getFileExtension(advancedImport.getAttachment().getFileName()).equals("zip")) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_ATTACHMENT_FORMAT));
}
String extension = Files.getFileExtension(advancedImport.getImportFile().getFileName());
if (extension == null
|| (!extension.equals("xlsx") && !extension.equals("xls") && !extension.equals("csv"))) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_FILE_FORMAT_INVALID));
}
DataReaderService reader = dataReaderFactory.getDataReader(extension);
reader.initialize(advancedImport.getImportFile(), advancedImport.getFileSeparator());
return validate(reader, advancedImport);
}
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
public boolean validate(DataReaderService reader, AdvancedImport advancedImport)
throws IOException, ClassNotFoundException, AxelorException {
boolean isLog = false;
String[] sheets = reader.getSheetNames();
this.validateTab(sheets, advancedImport);
boolean isConfig = advancedImport.getIsConfigInFile();
boolean isTabConfig = advancedImport.getIsFileTabConfigAdded();
sortFileTabList(advancedImport.getFileTabList());
for (FileTab fileTab : advancedImport.getFileTabList()) {
if (!Arrays.stream(sheets).anyMatch(sheet -> sheet.equals(fileTab.getName()))) {
continue;
}
fieldMap = new HashMap<>();
titleMap = new HashMap<>();
String sheet = fileTab.getName();
logService.initialize(sheet);
this.validateModel(fileTab);
int tabConfigRowCount = 0;
int totalLines = reader.getTotalLines(fileTab.getName());
if (isConfig) {
String[] objectRow = reader.read(sheet, 0, 0);
if (isTabConfig) {
tabConfigRowCount =
advancedImportService.getTabConfigRowCount(sheet, reader, totalLines, objectRow);
}
this.validateObject(objectRow, fileTab, isTabConfig);
}
this.validateSearchFields(fileTab);
this.validateObjectRequiredFields(fileTab);
this.validateFieldAndData(reader, sheet, fileTab, isConfig, isTabConfig, tabConfigRowCount);
if (fileTab.getValidationLog() != null) {
fileTab.setValidationLog(null);
}
if (logService.isLogGenerated()) {
logService.write();
logService.close();
File logFile = logService.getLogFile();
fileTab.setValidationLog(
metaFiles.upload(new FileInputStream(logFile), sheet + "_err.xlsx"));
logFile.delete();
isLog = true;
} else {
createCustomObjectSet(
fileTab.getClass().getName(), fileTab.getMetaModel().getFullName(), 0);
createCustomButton(fileTab.getClass().getName(), fileTab.getMetaModel().getFullName(), 1);
}
fileTabRepo.save(fileTab);
}
return isLog;
}
private void validateTab(String[] sheets, AdvancedImport advancedImport) throws AxelorException {
if (sheets == null) {
return;
}
List<String> sheetList = Arrays.asList(sheets);
List<String> tabList =
advancedImport
.getFileTabList()
.stream()
.map(tab -> tab.getName())
.collect(Collectors.toList());
if (!CollectionUtils.containsAny(tabList, sheetList)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_TAB_ERR));
}
}
private void validateModel(FileTab fileTab) throws IOException, AxelorException {
if (fileTab.getMetaModel() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(I18n.get(IExceptionMessage.ADVANCED_IMPORT_NO_OBJECT), fileTab.getName()));
}
}
private void validateObject(String[] row, FileTab fileTab, Boolean isTabConfig)
throws IOException, AxelorException {
int rowIndex = isTabConfig ? 1 : 0;
if (isTabConfig && row[0] != null) {
rowIndex = 0;
}
if (row == null || (StringUtils.isBlank(row[rowIndex]))) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.ADVANCED_IMPORT_FILE_FORMAT_INVALID));
}
String object = row[rowIndex].trim();
if (StringUtils.containsIgnoreCase(object, "Object")) {
String model = object.split("\\:")[1].trim();
if (fileTab.getMetaModel() != null && !fileTab.getMetaModel().getName().equals(model)) {
logService.addLog(LogService.COMMON_KEY, IExceptionMessage.ADVANCED_IMPORT_LOG_1, rowIndex);
}
}
}
private void validateSearchFields(FileTab fileTab) throws AxelorException {
if ((fileTab.getImportType() == FileFieldRepository.IMPORT_TYPE_FIND
|| fileTab.getImportType() == FileFieldRepository.IMPORT_TYPE_FIND_NEW)
&& CollectionUtils.isEmpty(fileTab.getSearchFieldSet())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(
I18n.get(IExceptionMessage.ADVANCED_IMPORT_6), fileTab.getMetaModel().getName()));
}
}
private void validateObjectRequiredFields(FileTab fileTab)
throws ClassNotFoundException, IOException, AxelorException {
if (CollectionUtils.isEmpty(fileTab.getFileFieldList())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
String.format(I18n.get(IExceptionMessage.ADVANCED_IMPORT_NO_FIELDS), fileTab.getName()));
}
List<String> fieldList = new ArrayList<String>();
for (FileField fileField : fileTab.getFileFieldList()) {
if (fileField.getImportField() != null) {
fieldList.add(fileField.getImportField().getName());
} else {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_2, fileField.getColumnTitle(), null);
}
}
if (fileTab.getImportType() == FileFieldRepository.IMPORT_TYPE_FIND) {
return;
}
if (fileTab.getMetaModel() != null) {
Mapper mapper = advancedImportService.getMapper(fileTab.getMetaModel().getFullName());
Model obj = null;
try {
obj = (Model) Class.forName(fileTab.getMetaModel().getFullName()).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
TraceBackService.trace(e);
}
for (Property prop : mapper.getProperties()) {
if (prop.isRequired() && !fieldList.contains(prop.getName())) {
if (obj != null && mapper.get(obj, prop.getName()) != null) {
continue;
}
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_3, prop.getName(), null);
}
}
}
}
private void validateFieldAndData(
DataReaderService reader,
String sheet,
FileTab fileTab,
boolean isConfig,
boolean isTabConfig,
int tabConfigRowCount)
throws ClassNotFoundException, IOException {
AdvancedImport advancedImport = fileTab.getAdvancedImport();
Map<String, Object> map = isConfig ? fieldMap : titleMap;
int linesToIgnore = advancedImport.getNbOfFirstLineIgnore();
int startIndex = isConfig ? 1 : linesToIgnore;
String[] row = reader.read(sheet, startIndex, 0);
if (row == null) {
return;
}
sortFileFieldList(fileTab.getFileFieldList());
int rowCount = row.length;
for (int cell = 0; cell < rowCount; cell++) {
String value = row[cell];
if (Strings.isNullOrEmpty(value)) {
continue;
}
value = value.trim();
map.put(isConfig ? value.contains("(") ? value.split("\\(")[0] : value : value, cell);
if (cell == row.length - 1) {
this.validateFields(startIndex, isConfig, fileTab);
}
}
if (!advancedImport.getIsValidateValue()) {
return;
}
int totalLines = reader.getTotalLines(sheet);
startIndex =
isConfig
? tabConfigRowCount + 3
: fileTab.getAdvancedImport().getIsHeader() ? linesToIgnore + 1 : linesToIgnore;
for (int line = startIndex; line < totalLines; line++) {
String[] dataRow = reader.read(sheet, line, row.length);
if (dataRow == null) {
continue;
}
this.validateData(dataRow, line, isConfig, fileTab);
}
}
private void validateFields(int line, boolean isConfig, FileTab fileTab)
throws IOException, ClassNotFoundException {
List<String> relationalFieldList =
fileTab
.getFileFieldList()
.stream()
.filter(field -> !Strings.isNullOrEmpty(field.getSubImportField()))
.map(field -> field.getImportField().getName() + "." + field.getSubImportField())
.collect(Collectors.toList());
for (FileField fileField : fileTab.getFileFieldList()) {
MetaField importField = fileField.getImportField();
if (importField != null && Strings.isNullOrEmpty(fileField.getSubImportField())) {
if (importField.getRelationship() != null) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_4, importField.getName(), line);
}
this.validateImportRequiredField(
line,
Class.forName(fileTab.getMetaModel().getFullName()),
importField.getName(),
fileField,
null);
this.validateDateField(line, fileField);
} else if (!Strings.isNullOrEmpty(fileField.getSubImportField())) {
Mapper mapper = advancedImportService.getMapper(importField.getMetaModel().getFullName());
Property parentProp = mapper.getProperty(importField.getName());
if (parentProp == null) {
return;
}
Property subProperty = this.getAndValidateSubField(line, parentProp, fileField, false);
if (subProperty != null) {
this.validateImportRequiredField(
line, subProperty.getEntity(), subProperty.getName(), fileField, relationalFieldList);
this.validateDateField(line, fileField);
}
}
}
}
private void validateDateField(int line, FileField fileField) throws IOException {
String type = fileField.getTargetType();
Integer rowNum = fileField.getIsMatchWithFile() ? line : null;
if (!Strings.isNullOrEmpty(type)
&& (type.equals(LOCAL_DATE)
|| type.equals(LOCAL_DATE_TIME)
|| type.equals(LOCAL_TIME)
|| type.equals(ZONED_DATE_TIME))) {
String field = getField(fileField);
if (Strings.isNullOrEmpty(fileField.getDateFormat())
&& Strings.isNullOrEmpty(fileField.getExpression())) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_6, field, rowNum);
}
}
}
private void validateImportRequiredField(
int line,
Class<?> model,
String fieldName,
FileField fileField,
List<String> relationalFieldList)
throws IOException, ClassNotFoundException {
Mapper mapper = advancedImportService.getMapper(model.getName());
String field = getField(fileField);
Integer rowNum = fileField.getIsMatchWithFile() ? line : null;
int importType = fileField.getImportType();
for (Property prop : mapper.getProperties()) {
if (prop.isRequired()) {
if (prop.getName().equals(fieldName)
&& importType == FileFieldRepository.IMPORT_TYPE_IGNORE_EMPTY) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_5, field, rowNum);
} else if ((importType == FileFieldRepository.IMPORT_TYPE_FIND_NEW
|| importType == FileFieldRepository.IMPORT_TYPE_NEW)
&& field.contains(".")
&& !fileField.getTargetType().equals("MetaFile")) {
String newField = StringUtils.substringBeforeLast(field, ".");
newField = newField + "." + prop.getName();
if (!relationalFieldList.contains(newField)) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_3, newField, null);
}
}
}
}
}
private void validateData(String[] dataRow, int line, boolean isConfig, FileTab fileTab)
throws IOException, ClassNotFoundException {
Map<String, Object> map = isConfig ? fieldMap : titleMap;
for (int fieldIndex = 0; fieldIndex < fileTab.getFileFieldList().size(); fieldIndex++) {
FileField fileField = fileTab.getFileFieldList().get(fieldIndex);
if (!fileField.getIsMatchWithFile() || !Strings.isNullOrEmpty(fileField.getExpression())) {
continue;
}
Mapper mapper = null;
Property property = null;
String key = null;
if (isConfig) {
key = this.getField(fileField);
} else {
key = fileField.getColumnTitle();
}
int cellIndex = 0;
if (map.containsKey(key)) {
cellIndex = (int) map.get(key);
}
cellIndex =
(!isConfig && !fileTab.getAdvancedImport().getIsHeader()) ? fieldIndex : cellIndex;
if (fileField.getImportField() != null) {
mapper =
advancedImportService.getMapper(
fileField.getImportField().getMetaModel().getFullName());
property = mapper.getProperty(fileField.getImportField().getName());
}
if (property != null && Strings.isNullOrEmpty(fileField.getSubImportField())) {
if (this.validateDataRequiredField(
dataRow,
cellIndex,
line,
Class.forName(fileTab.getMetaModel().getFullName()),
property.getName(),
fileField)) {
continue;
}
if (!Strings.isNullOrEmpty(property.getSelection())
&& fileField.getForSelectUse() != FileFieldRepository.SELECT_USE_VALUES) {
continue;
}
this.validateDataType(
dataRow, cellIndex, line, property.getJavaType().getSimpleName(), fileField);
} else if (!Strings.isNullOrEmpty(fileField.getSubImportField())) {
Property subProperty = this.getAndValidateSubField(line, property, fileField, true);
if (subProperty != null) {
if (this.validateDataRequiredField(
dataRow,
cellIndex,
line,
subProperty.getEntity(),
subProperty.getName(),
fileField)) {
continue;
}
if (!Strings.isNullOrEmpty(subProperty.getSelection())
&& fileField.getForSelectUse() != FileFieldRepository.SELECT_USE_VALUES) {
continue;
}
this.validateDataType(
dataRow, cellIndex, line, subProperty.getJavaType().getSimpleName(), fileField);
}
}
}
}
private boolean validateDataRequiredField(
String row[], int cell, int line, Class<?> model, String fieldName, FileField fileField)
throws IOException, ClassNotFoundException {
boolean flag = false;
String field = getField(fileField);
int importType = fileField.getImportType();
Mapper mapper = advancedImportService.getMapper(model.getName());
Property prop = mapper.getProperty(fieldName);
if (prop != null) {
if (prop.isRequired()
&& Strings.isNullOrEmpty(row[cell])
&& importType != FileFieldRepository.IMPORT_TYPE_FIND) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_8, field, line);
} else if (importType == FileFieldRepository.IMPORT_TYPE_IGNORE_EMPTY) {
flag = true;
return flag;
}
}
return flag;
}
private Property getAndValidateSubField(
int line, Property parentProp, FileField fileField, boolean isLog)
throws IOException, ClassNotFoundException {
String field = getField(fileField);
Integer rowNum = fileField.getIsMatchWithFile() ? line : null;
String[] subFields = fileField.getSubImportField().split("\\.");
return this.getAndValidateSubField(subFields, 0, rowNum, parentProp, field, isLog);
}
public Property getAndValidateSubField(
String[] subFields,
int index,
Integer rowNum,
Property parentProp,
String field,
boolean isLog)
throws IOException, ClassNotFoundException {
Property subProperty = null;
if (parentProp.getTarget() != null) {
Mapper mapper = advancedImportService.getMapper(parentProp.getTarget().getName());
Property childProp = mapper.getProperty(subFields[index]);
if (childProp == null) {
if (!isLog) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_7, field, rowNum);
}
}
if (childProp != null && childProp.getTarget() != null) {
if (index != subFields.length - 1) {
subProperty =
this.getAndValidateSubField(subFields, index + 1, rowNum, childProp, field, isLog);
} else {
subProperty = childProp;
if (!isLog) {
logService.addLog(IExceptionMessage.ADVANCED_IMPORT_LOG_4, field, rowNum);
}
}
} else {
subProperty = childProp;
}
}
return subProperty;
}
private void validateDataType(String[] row, int cell, int line, String type, FileField fileField)
throws IOException {
if (Strings.isNullOrEmpty(row[cell])) {
return;
}
String field = getField(fileField);
String value = row[cell].trim();
switch (type) {
case INTEGER:
case LONG:
case BIG_DECIMAL:
this.checkNumeric(value, line, field, type);
break;
case LOCAL_DATE:
case ZONED_DATE_TIME:
case LOCAL_DATE_TIME:
case LOCAL_TIME:
this.checkDateTime(value, line, type, fileField);
break;
case BOOLEAN:
String boolPat = "(true|false|1|0|no|yes|n|y)";
if (!value.matches(boolPat)) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, field + "(" + type + ")", line);
}
break;
default:
if (!type.equals(STRING)) {
try {
new BigInteger(value);
} catch (Exception e) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, field + "(" + type + ")", line);
}
}
break;
}
}
private void checkNumeric(String value, int line, String field, String type) throws IOException {
switch (type) {
case INTEGER:
try {
Integer.parseInt(value);
} catch (NumberFormatException e) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, field + "(" + type + ")", line);
}
break;
case LONG:
try {
Long.parseLong(value);
} catch (NumberFormatException e) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, field + "(" + type + ")", line);
}
break;
case BIG_DECIMAL:
try {
new BigDecimal(value);
} catch (NumberFormatException e) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, field + "(" + type + ")", line);
}
break;
}
}
private void checkDateTime(String value, int line, String type, FileField fileField)
throws IOException {
if (!Strings.isNullOrEmpty(fileField.getDateFormat())
&& Strings.isNullOrEmpty(fileField.getExpression())) {
String pattern = fileField.getDateFormat().trim();
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
switch (type) {
case LOCAL_DATE:
LocalDate.parse(value, formatter);
break;
case LOCAL_TIME:
LocalTime.parse(value, formatter);
break;
case LOCAL_DATE_TIME:
LocalDateTime.parse(value, formatter);
break;
case ZONED_DATE_TIME:
ZonedDateTime.parse(value, formatter);
break;
}
} catch (DateTimeParseException e) {
logService.addLog(
IExceptionMessage.ADVANCED_IMPORT_LOG_9, getField(fileField) + "(" + type + ")", line);
}
}
}
public String getField(FileField fileField) {
String field =
!Strings.isNullOrEmpty(fileField.getSubImportField())
? fileField.getImportField().getName() + "." + fileField.getSubImportField().trim()
: fileField.getImportField().getName();
return field;
}
public void sortFileTabList(List<FileTab> fileTabList) {
fileTabList.sort((tab1, tab2) -> tab1.getSequence().compareTo(tab2.getSequence()));
}
public void sortFileFieldList(List<FileField> fileFieldList) {
fileFieldList.sort((field1, field2) -> field1.getSequence().compareTo(field2.getSequence()));
}
public void addLog(BufferedWriter writer, String errorType, String log) throws IOException {
writer.write(I18n.get(errorType) + ": " + log);
writer.newLine();
}
@Transactional(rollbackOn = {Exception.class})
public void createCustomObjectSet(String modelName, String targetModelName, int sequence) {
String simpleModelName = StringUtils.substringAfterLast(targetModelName, ".");
String fieldName = Inflector.getInstance().camelize(simpleModelName, true) + "Set";
String viewName = Inflector.getInstance().dasherize(simpleModelName);
if (metaJsonFieldRepo
.all()
.filter(
"self.type = ?1 AND self.model = ?2 AND self.targetModel = ?3",
"many-to-many",
modelName,
targetModelName)
.count()
> 0) {
return;
}
MetaJsonField jsonField = new MetaJsonField();
jsonField.setName(fieldName);
jsonField.setType("many-to-many");
jsonField.setTitle(Inflector.getInstance().titleize(simpleModelName));
jsonField.setSequence(sequence);
jsonField.setModel(modelName);
jsonField.setModelField("attrs");
jsonField.setTargetModel(targetModelName);
jsonField.setGridView(viewName + "-grid");
jsonField.setFormView(viewName + "-form");
jsonField.setWidgetAttrs("{\"colSpan\": \"12\"}");
jsonField.setShowIf(fieldName + " != null && $record.advancedImport.statusSelect > 0");
metaJsonFieldRepo.save(jsonField);
}
@Transactional(rollbackOn = {Exception.class})
public void createCustomButton(String modelName, String targetModelName, int sequence) {
String simpleModelName = StringUtils.substringAfterLast(targetModelName, ".");
String fieldName = Inflector.getInstance().camelize(simpleModelName, true) + "Set";
String buttonName = "show" + fieldName + "Btn";
if (metaJsonFieldRepo
.all()
.filter(
"self.name = ?1 AND self.type = ?2 AND self.model = ?3",
buttonName,
"button",
modelName)
.count()
> 0) {
return;
}
MetaJsonField jsonField = new MetaJsonField();
jsonField.setName(buttonName);
jsonField.setType("button");
jsonField.setTitle("Show " + Inflector.getInstance().titleize(simpleModelName));
jsonField.setSequence(sequence);
jsonField.setModel(modelName);
jsonField.setModelField("attrs");
jsonField.setOnClick("action-file-tab-method-show-record,close");
jsonField.setWidgetAttrs("{\"colSpan\": \"4\"}");
jsonField.setShowIf(fieldName + " != null && $record.advancedImport.statusSelect > 0");
metaJsonFieldRepo.save(jsonField);
}
}

View File

@ -0,0 +1,149 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advancedExport;
import com.axelor.apps.base.db.AdvancedExport;
import com.axelor.exception.AxelorException;
import com.itextpdf.text.DocumentException;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AdvancedExportGenerator {
private final Logger log = LoggerFactory.getLogger(AdvancedExportGenerator.class);
private boolean isReachMaxExportLimit;
/**
* This method generate the header of export file.
*
* @throws IOException
* @throws DocumentException
* @throws AxelorException
*/
public abstract void generateHeader() throws AxelorException;
/**
* This method generate the body of export file.
*
* @param dataList
*/
@SuppressWarnings("rawtypes")
public abstract void generateBody(List<List> dataList);
/**
* This method close the object.
*
* @throws AxelorException
* @throws IOException
* @throws DocumentException
*/
public abstract void close() throws AxelorException;
/**
* This method return the object of <i>AdvancedExport</i>.
*
* @return
*/
public abstract AdvancedExport getAdvancedExport();
/**
* Get the export file.
*
* @return
*/
public abstract File getExportFile();
/**
* Get the name of export file.
*
* @return
*/
public abstract String getFileName();
/**
* This method is used to generate the export file.
*
* @param advancedExport
* @param query
* @return
* @throws AxelorException
* @throws IOException
* @throws DocumentException
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public File generateFile(Query query) throws AxelorException {
AdvancedExport advancedExport = getAdvancedExport();
log.debug("Export file : {}", getFileName());
generateHeader();
int startPosition = 0;
int reachLimit = 0;
int maxExportLimit = advancedExport.getMaxExportLimit();
int queryFetchLimit = advancedExport.getQueryFetchSize();
List<List> dataList = new ArrayList<>();
query.setMaxResults(queryFetchLimit);
while (startPosition < maxExportLimit) {
if ((maxExportLimit - startPosition) < queryFetchLimit) {
query.setMaxResults((maxExportLimit - startPosition));
}
query.setFirstResult(startPosition);
dataList = query.getResultList();
if (dataList.isEmpty()) break;
generateBody(dataList);
startPosition = startPosition + queryFetchLimit;
reachLimit += dataList.size();
}
if (maxExportLimit == reachLimit) {
isReachMaxExportLimit = true;
}
close();
return getExportFile();
}
public boolean getIsReachMaxExportLimit() {
return isReachMaxExportLimit;
}
public String getExportFileName() {
return getFileName();
}
/**
* Explicitly convert decimal value with it's scale.
*
* @param value
* @return
*/
public String convertDecimalValue(Object value) {
BigDecimal decimalVal = (BigDecimal) value;
return String.format("%." + decimalVal.scale() + "f", decimalVal);
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.base.service.advancedExport;
import com.axelor.apps.base.db.AdvancedExport;
import com.axelor.exception.AxelorException;
import com.google.inject.Singleton;
@Singleton
public class AdvancedExportGeneratorFactory {
public AdvancedExportGenerator getAdvancedExportGenerator(
AdvancedExport advancedExport, String fileType) throws AxelorException {
AdvancedExportGenerator exportGenerator = null;
switch (fileType) {
case AdvancedExportService.PDF:
exportGenerator = new PdfExportGenerator(advancedExport);
break;
case AdvancedExportService.EXCEL:
exportGenerator = new ExcelExportGenerator(advancedExport);
break;
case AdvancedExportService.CSV:
exportGenerator = new CsvExportGenerator(advancedExport);
break;
default:
break;
}
return exportGenerator;
}
}

Some files were not shown because too many files have changed in this diff Show More