First commit waiting for Budget Alert
This commit is contained in:
26
modules/axelor-open-suite/axelor-base/build.gradle
Normal file
26
modules/axelor-open-suite/axelor-base/build.gradle
Normal 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'
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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 = /*$$(*/
|
||||
"Can’t 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" /*)*/;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
@ -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"; /*)*/
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
// }
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
Reference in New Issue
Block a user