First commit waiting for Budget Alert
16
modules/axelor-open-suite/axelor-stock/build.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
apply plugin: "com.axelor.app-module"
|
||||
|
||||
apply from: "../version.gradle"
|
||||
|
||||
apply {
|
||||
version = openSuiteVersion
|
||||
}
|
||||
|
||||
axelor {
|
||||
title "Axelor Stock"
|
||||
description "Axelor Stock Module"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(":modules:axelor-base")
|
||||
}
|
||||
@ -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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.service.InventoryService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class InventoryManagementRepository extends InventoryRepository {
|
||||
@Override
|
||||
public Inventory copy(Inventory entity, boolean deep) {
|
||||
|
||||
Inventory copy = super.copy(entity, deep);
|
||||
|
||||
copy.setStatusSelect(STATUS_DRAFT);
|
||||
copy.setInventorySeq(null);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory save(Inventory entity) {
|
||||
Inventory inventory = super.save(entity);
|
||||
SequenceService sequenceService = Beans.get(SequenceService.class);
|
||||
|
||||
try {
|
||||
if (Strings.isNullOrEmpty(inventory.getInventorySeq())) {
|
||||
inventory.setInventorySeq(sequenceService.getDraftSequenceNumber(inventory));
|
||||
}
|
||||
} catch (AxelorException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
|
||||
entity.setInventoryTitle(Beans.get(InventoryService.class).computeTitle(entity));
|
||||
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Sequence;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.message.db.Template;
|
||||
import com.axelor.apps.message.service.TemplateMessageService;
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
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.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class LogisticalFormStockRepository extends LogisticalFormRepository {
|
||||
|
||||
@Override
|
||||
public LogisticalForm save(LogisticalForm logisticalForm) {
|
||||
try {
|
||||
|
||||
Company company = logisticalForm.getCompany();
|
||||
|
||||
if (company != null) {
|
||||
if (Strings.isNullOrEmpty(logisticalForm.getDeliveryNumberSeq())) {
|
||||
String sequenceNumber =
|
||||
Beans.get(SequenceService.class)
|
||||
.getSequenceNumber("logisticalForm", logisticalForm.getCompany());
|
||||
if (Strings.isNullOrEmpty(sequenceNumber)) {
|
||||
throw new AxelorException(
|
||||
Sequence.class,
|
||||
TraceBackRepository.CATEGORY_NO_VALUE,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_MISSING_SEQUENCE),
|
||||
logisticalForm.getCompany().getName());
|
||||
}
|
||||
logisticalForm.setDeliveryNumberSeq(sequenceNumber);
|
||||
}
|
||||
|
||||
if (!logisticalForm.getIsEmailSent()) {
|
||||
StockConfig stockConfig = Beans.get(StockConfigService.class).getStockConfig(company);
|
||||
if (stockConfig.getLogisticalFormAutomaticEmail()) {
|
||||
Template template = stockConfig.getLogisticalFormMessageTemplate();
|
||||
if (template == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_MISSING_TEMPLATE),
|
||||
logisticalForm);
|
||||
}
|
||||
|
||||
Beans.get(TemplateMessageService.class)
|
||||
.generateAndSendMessage(logisticalForm, template);
|
||||
|
||||
logisticalForm.setIsEmailSent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.save(logisticalForm);
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
throw new PersistenceException(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductBaseRepository;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.service.StockLocationLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.google.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProductStockRepository extends ProductBaseRepository {
|
||||
|
||||
@Inject private StockMoveService stockMoveService;
|
||||
|
||||
@Inject private StockLocationRepository stockLocationRepo;
|
||||
|
||||
@Inject private StockLocationLineService stockLocationLineService;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
|
||||
|
||||
this.setAvailableQty(json, context);
|
||||
|
||||
if (!context.containsKey("fromStockWizard")) {
|
||||
return json;
|
||||
}
|
||||
try {
|
||||
Long productId = (Long) json.get("id");
|
||||
Long locationId = Long.parseLong(context.get("locationId").toString());
|
||||
LocalDate fromDate = LocalDate.parse(context.get("stockFromDate").toString());
|
||||
LocalDate toDate = LocalDate.parse(context.get("stockToDate").toString());
|
||||
List<Map<String, Object>> stock =
|
||||
stockMoveService.getStockPerDate(locationId, productId, fromDate, toDate);
|
||||
|
||||
if (stock != null && !stock.isEmpty()) {
|
||||
LocalDate minDate = null;
|
||||
LocalDate maxDate = null;
|
||||
BigDecimal minQty = BigDecimal.ZERO;
|
||||
BigDecimal maxQty = BigDecimal.ZERO;
|
||||
for (Map<String, Object> dateStock : stock) {
|
||||
LocalDate date = (LocalDate) dateStock.get("$date");
|
||||
BigDecimal qty = (BigDecimal) dateStock.get("$qty");
|
||||
if (minDate == null
|
||||
|| qty.compareTo(minQty) < 0
|
||||
|| qty.compareTo(minQty) == 0 && date.isAfter(minDate)) {
|
||||
minDate = date;
|
||||
minQty = qty;
|
||||
}
|
||||
if (maxDate == null
|
||||
|| qty.compareTo(maxQty) > 0
|
||||
|| qty.compareTo(maxQty) == 0 && date.isBefore(maxDate)) {
|
||||
maxDate = date;
|
||||
maxQty = qty;
|
||||
}
|
||||
}
|
||||
json.put("$stockMinDate", minDate);
|
||||
json.put("$stockMin", minQty);
|
||||
json.put("$stockMaxDate", maxDate);
|
||||
json.put("$stockMax", maxQty);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void setAvailableQty(Map<String, Object> json, Map<String, Object> context) {
|
||||
try {
|
||||
Long productId = (Long) json.get("id");
|
||||
Product product = find(productId);
|
||||
|
||||
if (context.get("_parent") != null) {
|
||||
Map<String, Object> _parent = (Map<String, Object>) context.get("_parent");
|
||||
|
||||
StockLocation stockLocation = null;
|
||||
if (context.get("_model").toString().equals("com.axelor.apps.stock.db.StockMoveLine")) {
|
||||
if (_parent.get("fromStockLocation") != null) {
|
||||
stockLocation =
|
||||
stockLocationRepo.find(
|
||||
Long.parseLong(((Map) _parent.get("fromStockLocation")).get("id").toString()));
|
||||
}
|
||||
} else {
|
||||
if (_parent.get("stockLocation") != null) {
|
||||
stockLocation =
|
||||
stockLocationRepo.find(
|
||||
Long.parseLong(((Map) _parent.get("stockLocation")).get("id").toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (stockLocation != null) {
|
||||
BigDecimal availableQty =
|
||||
stockLocationLineService.getAvailableQty(stockLocation, product);
|
||||
|
||||
json.put("$availableQty", availableQty);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product copy(Product product, boolean deep) {
|
||||
Product copy = super.copy(product, deep);
|
||||
copy.setAvgPrice(BigDecimal.ZERO);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@ -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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.service.WeightedAveragePriceService;
|
||||
import com.axelor.inject.Beans;
|
||||
|
||||
public class StockLocationLineStockRepository extends StockLocationLineRepository {
|
||||
|
||||
@Override
|
||||
public StockLocationLine save(StockLocationLine entity) {
|
||||
Product product = entity.getProduct();
|
||||
if (entity.getIsAvgPriceChanged()) {
|
||||
Beans.get(WeightedAveragePriceService.class).computeAvgPriceForProduct(product);
|
||||
}
|
||||
return super.save(entity);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.service.StockLocationSaveService;
|
||||
import com.axelor.apps.stock.service.StockLocationService;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.util.Map;
|
||||
|
||||
public class StockLocationStockRepository extends StockLocationRepository {
|
||||
|
||||
/**
|
||||
* Override to remove incompatible stock locations in partners
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public StockLocation save(StockLocation entity) {
|
||||
Beans.get(StockLocationSaveService.class).removeForbiddenDefaultStockLocation(entity);
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
|
||||
Long stocklocationId = (Long) json.get("id");
|
||||
StockLocation stockLocation = find(stocklocationId);
|
||||
|
||||
if (stockLocation.getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL) {
|
||||
return super.populate(json, context);
|
||||
}
|
||||
|
||||
json.put(
|
||||
"stockLocationValue",
|
||||
Beans.get(StockLocationService.class).getStockLocationValue(stockLocation));
|
||||
|
||||
return super.populate(json, context);
|
||||
}
|
||||
}
|
||||
@ -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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.service.StockMoveLineService;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.util.Map;
|
||||
|
||||
public class StockMoveLineStockRepository extends StockMoveLineRepository {
|
||||
|
||||
@Override
|
||||
public StockMoveLine copy(StockMoveLine entity, boolean deep) {
|
||||
StockMoveLine copy = super.copy(entity, deep);
|
||||
copy.setStockMove(null);
|
||||
copy.setPlannedStockMove(null);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
|
||||
Long stockMoveLineId = (Long) json.get("id");
|
||||
StockMoveLine stockMoveLine = find(stockMoveLineId);
|
||||
|
||||
StockMove stockMove = stockMoveLine.getStockMove();
|
||||
|
||||
if (stockMove == null
|
||||
|| (stockMove.getFromStockLocation() != null
|
||||
&& stockMove.getFromStockLocation().getTypeSelect()
|
||||
== StockLocationRepository.TYPE_VIRTUAL)) {
|
||||
|
||||
return super.populate(json, context);
|
||||
}
|
||||
|
||||
if (stockMove.getStatusSelect() < StockMoveRepository.STATUS_REALIZED) {
|
||||
Beans.get(StockMoveLineService.class).setAvailableStatus(stockMoveLine);
|
||||
json.put(
|
||||
"availableStatus",
|
||||
stockMoveLine.getProduct() != null && stockMoveLine.getProduct().getStockManaged()
|
||||
? stockMoveLine.getAvailableStatus()
|
||||
: null);
|
||||
json.put("availableStatusSelect", stockMoveLine.getAvailableStatusSelect());
|
||||
}
|
||||
return super.populate(json, context);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.StockMoveLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveToolService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Map;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class StockMoveManagementRepository extends StockMoveRepository {
|
||||
|
||||
@Override
|
||||
public StockMove copy(StockMove entity, boolean deep) {
|
||||
|
||||
StockMove copy = super.copy(entity, deep);
|
||||
|
||||
copy.setStatusSelect(STATUS_DRAFT);
|
||||
copy.setStockMoveSeq(null);
|
||||
copy.setName(null);
|
||||
copy.setRealDate(null);
|
||||
copy.setPickingEditDate(null);
|
||||
copy.setPickingIsEdited(false);
|
||||
copy.setAvailabilityRequest(false);
|
||||
copy.setSupplierShipmentDate(null);
|
||||
copy.setSupplierShipmentRef(null);
|
||||
copy.setAvailabilityRequest(false);
|
||||
copy.setFullySpreadOverLogisticalFormsFlag(false);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockMove save(StockMove entity) {
|
||||
try {
|
||||
StockMove stockMove = super.save(entity);
|
||||
SequenceService sequenceService = Beans.get(SequenceService.class);
|
||||
|
||||
if (Strings.isNullOrEmpty(stockMove.getStockMoveSeq())) {
|
||||
stockMove.setStockMoveSeq(sequenceService.getDraftSequenceNumber(stockMove));
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(stockMove.getName())
|
||||
|| stockMove.getName().startsWith(stockMove.getStockMoveSeq())) {
|
||||
stockMove.setName(Beans.get(StockMoveToolService.class).computeName(stockMove));
|
||||
}
|
||||
|
||||
return stockMove;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(StockMove entity) {
|
||||
if (entity.getStatusSelect() == STATUS_PLANNED) {
|
||||
throw new PersistenceException(I18n.get(IExceptionMessage.STOCK_MOVE_PLANNED_NOT_DELETED));
|
||||
} else if (entity.getStatusSelect() == STATUS_REALIZED) {
|
||||
throw new PersistenceException(I18n.get(IExceptionMessage.STOCK_MOVE_REALIZED_NOT_DELETED));
|
||||
} else {
|
||||
if (entity.getStockMoveOrigin() != null) {
|
||||
entity.getStockMoveOrigin().setBackorderId(null);
|
||||
}
|
||||
super.remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
|
||||
Long stockMoveId = (Long) json.get("id");
|
||||
StockMove stockMove = find(stockMoveId);
|
||||
|
||||
if (stockMove.getStatusSelect() > STATUS_PLANNED
|
||||
|| stockMove.getStockMoveLineList() == null
|
||||
|| (stockMove.getFromStockLocation() != null
|
||||
&& stockMove.getFromStockLocation().getTypeSelect()
|
||||
== StockLocationRepository.TYPE_VIRTUAL)) {
|
||||
return super.populate(json, context);
|
||||
}
|
||||
|
||||
int available = 0, availableForProduct = 0, missing = 0;
|
||||
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
|
||||
Beans.get(StockMoveLineService.class)
|
||||
.updateAvailableQty(stockMoveLine, stockMove.getFromStockLocation());
|
||||
Product product = stockMoveLine.getProduct();
|
||||
if (stockMoveLine.getAvailableQty().compareTo(stockMoveLine.getRealQty()) >= 0
|
||||
|| product != null && !product.getStockManaged()) {
|
||||
available++;
|
||||
} else if (stockMoveLine.getAvailableQtyForProduct().compareTo(stockMoveLine.getRealQty())
|
||||
>= 0) {
|
||||
availableForProduct++;
|
||||
} else if (stockMoveLine.getAvailableQty().compareTo(stockMoveLine.getRealQty()) < 0
|
||||
&& stockMoveLine.getAvailableQtyForProduct().compareTo(stockMoveLine.getRealQty()) < 0) {
|
||||
missing++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((available > 0 || availableForProduct > 0) && missing == 0) {
|
||||
json.put("availableStatusSelect", StockMoveRepository.STATUS_AVAILABLE);
|
||||
} else if ((available > 0 || availableForProduct > 0) && missing > 0) {
|
||||
json.put("availableStatusSelect", StockMoveRepository.STATUS_PARTIALLY_AVAILABLE);
|
||||
} else if (available == 0 && availableForProduct == 0 && missing > 0) {
|
||||
json.put("availableStatusSelect", StockMoveRepository.STATUS_UNAVAILABLE);
|
||||
}
|
||||
return super.populate(json, context);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.axelor.apps.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class StockProductionRequestManagementRepository extends StockProductionRequestRepository {
|
||||
|
||||
@Override
|
||||
public StockProductionRequest save(StockProductionRequest entity) {
|
||||
try {
|
||||
StockProductionRequest productionRequest = super.save(entity);
|
||||
SequenceService sequenceService = Beans.get(SequenceService.class);
|
||||
|
||||
if (Strings.isNullOrEmpty(productionRequest.getStockProductionRequestSeq())) {
|
||||
productionRequest.setStockProductionRequestSeq(
|
||||
sequenceService.getDraftSequenceNumber(productionRequest));
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(productionRequest.getName())
|
||||
|| productionRequest
|
||||
.getName()
|
||||
.startsWith(productionRequest.getStockProductionRequestSeq())) {
|
||||
productionRequest.setName(productionRequest.getStockProductionRequestSeq());
|
||||
}
|
||||
|
||||
return productionRequest;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.stock.db.repo;
|
||||
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.service.StockLocationLineService;
|
||||
import com.google.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public class TrackingNumberManagementRepository extends TrackingNumberRepository {
|
||||
|
||||
@Inject private StockLocationRepository stockLocationRepo;
|
||||
|
||||
@Inject private StockLocationLineService stockLocationLineService;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
|
||||
try {
|
||||
Long trackingNumberId = (Long) json.get("id");
|
||||
TrackingNumber trackingNumber = find(trackingNumberId);
|
||||
|
||||
if (trackingNumber.getProduct() != null && context.get("_parent") != null) {
|
||||
Map<String, Object> _parent = (Map<String, Object>) context.get("_parent");
|
||||
|
||||
if (_parent.get("fromStockLocation") != null) {
|
||||
StockLocation stockLocation =
|
||||
stockLocationRepo.find(
|
||||
Long.parseLong(((Map) _parent.get("fromStockLocation")).get("id").toString()));
|
||||
|
||||
if (stockLocation != null) {
|
||||
BigDecimal availableQty =
|
||||
stockLocationLineService.getTrackingNumberAvailableQty(
|
||||
stockLocation, trackingNumber);
|
||||
|
||||
json.put("$availableQty", availableQty);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return super.populate(json, context);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.stock.exception;
|
||||
|
||||
/** @author axelor */
|
||||
public interface IExceptionMessage {
|
||||
|
||||
/** Inventory service and controller */
|
||||
static final String INVENTORY_1 = /*$$(*/ "You must select a stock location" /*)*/;
|
||||
|
||||
static final String INVENTORY_2 = /*$$(*/
|
||||
"There's no configured sequence for inventory for company" /*)*/;
|
||||
static final String INVENTORY_3 = /*$$(*/
|
||||
"An error occurred while importing the file data. Please contact your application administrator to check Traceback logs." /*)*/;
|
||||
static final String INVENTORY_4 = /*$$(*/
|
||||
"An error occurred while importing the file data, product not found with code :" /*)*/;
|
||||
static final String INVENTORY_5 = /*$$(*/
|
||||
"There is currently no such file in the specified folder or the folder may not exists." /*)*/;
|
||||
static final String INVENTORY_6 = /*$$(*/ "Company missing for stock location %s" /*)*/;
|
||||
static final String INVENTORY_7 = /*$$(*/ "Incorrect product in inventory line" /*)*/;
|
||||
static final String INVENTORY_8 = /*$$(*/ "File %s successfully imported." /*)*/;
|
||||
static final String INVENTORY_9 = /*$$(*/ "There's no product in stock location." /*)*/;
|
||||
static final String INVENTORY_10 = /*$$(*/ "Inventory's lines' list has been filled." /*)*/;
|
||||
static final String INVENTORY_11 = /*$$(*/ "No inventory lines has been created." /*)*/;
|
||||
static final String INVENTORY_12 = /*$$(*/
|
||||
"An error occurred while importing the file data, there are multiple products with code :" /*)*/;
|
||||
static final String INVENTORY_3_LINE_LENGHT = /*$$(*/ "Line length too big" /*)*/;
|
||||
static final String INVENTORY_3_REAL_QUANTITY = /*$$(*/ "Real quantity problem" /*)*/;
|
||||
static final String INVENTORY_3_CURRENT_QUANTITY = /*$$(*/ "Current quantity problem" /*)*/;
|
||||
static final String INVENTORY_3_DATA_NULL_OR_EMPTY = /*$$(*/ "Data is null or empty" /*)*/;
|
||||
|
||||
/** Stock Location Line Service Impl */
|
||||
static final String LOCATION_LINE_1 = /*$$(*/
|
||||
"Product's stocks %s (%s) are not in sufficient quantity to realize the delivery" /*)*/;
|
||||
|
||||
static final String LOCATION_LINE_2 = /*$$(*/
|
||||
"Product's stocks %s (%s), tracking number %s are not in sufficient quantity to realize the delivery" /*)*/;
|
||||
static final String LOCATION_LINE_3 = /*$$(*/
|
||||
"Product's stocks %s (%s) exceeds maximum stock rules." /*)*/;
|
||||
|
||||
/** Stock Move Service and Controller */
|
||||
static final String STOCK_MOVE_1 = /*$$(*/
|
||||
"There's no configured sequence for stock's intern moves for the company %s" /*)*/;
|
||||
|
||||
static final String STOCK_MOVE_2 = /*$$(*/
|
||||
"There's no configured sequence for stock's receptions for the company %s" /*)*/;
|
||||
static final String STOCK_MOVE_3 = /*$$(*/
|
||||
"There's no configured sequence for stock's delivery for the company %s" /*)*/;
|
||||
static final String STOCK_MOVE_4 = /*$$(*/ "Stock's movement's type undefined" /*)*/;
|
||||
static final String STOCK_MOVE_5 = /*$$(*/
|
||||
"There's no source stock location selected for the stock's movement %s" /*)*/;
|
||||
static final String STOCK_MOVE_6 = /*$$(*/
|
||||
"There's no destination stock location selected for the stock's movement %s" /*)*/;
|
||||
static final String STOCK_MOVE_7 = /*$$(*/ "Partial stock move (From" /*)*/;
|
||||
static final String STOCK_MOVE_8 = /*$$(*/ "Reverse stock move (From" /*)*/;
|
||||
static final String STOCK_MOVE_9 = /*$$(*/ "A partial stock move has been generated (%s)" /*)*/;
|
||||
static final String STOCK_MOVE_10 = /*$$(*/ "Please select the StockMove(s) to print." /*)*/;
|
||||
static final String STOCK_MOVE_11 = /*$$(*/ "Company address is empty." /*)*/;
|
||||
static final String STOCK_MOVE_12 = /*$$(*/
|
||||
"Feature currently not available with Open Street Maps." /*)*/;
|
||||
static final String STOCK_MOVE_13 = /*$$(*/ "<B>%s or %s</B> not found" /*)*/;
|
||||
static final String STOCK_MOVE_14 = /*$$(*/ "No move lines to split" /*)*/;
|
||||
static final String STOCK_MOVE_15 = /*$$(*/ "Please select lines to split" /*)*/;
|
||||
static final String STOCK_MOVE_16 = /*$$(*/ "Please enter a valid split quantity" /*)*/;
|
||||
static final String STOCK_MOVE_17 = /*$$(*/
|
||||
"Must set mass unit in stock configuration for customs." /*)*/;
|
||||
static final String STOCK_MOVE_18 = /*$$(*/
|
||||
"All storable products used in DEB must have net mass and mass unit information for customs." /*)*/;
|
||||
static final String STOCK_MOVE_19 = /*$$(*/
|
||||
"Can't realize this stock move because of the ongoing inventory %s." /*)*/;
|
||||
static final String STOCK_MOVE_PLANNED_NOT_DELETED = /*$$(*/
|
||||
"Can't delete a planned stock move" /*)*/;
|
||||
static final String STOCK_MOVE_REALIZED_NOT_DELETED = /*$$(*/
|
||||
"Can't delete a realized stock move" /*)*/;
|
||||
static final String STOCK_MOVE_SPLIT_NOT_GENERATED = /*$$(*/
|
||||
"No new stock move was generated" /*)*/;
|
||||
static final String STOCK_MOVE_INCOMING_PARTIAL_GENERATED = /*$$(*/
|
||||
"An incoming partial stock move has been generated (%s)" /*)*/;
|
||||
static final String STOCK_MOVE_OUTGOING_PARTIAL_GENERATED = /*$$(*/
|
||||
"An outgoing partial stock move has been generated (%s)" /*)*/;
|
||||
static final String STOCK_MOVE_MISSING_TEMPLATE = /*$$(*/
|
||||
"The template to send message on realization is missing." /*)*/;
|
||||
static final String STOCK_MOVE_QTY_BY_TRACKING = /*$$(*/
|
||||
"The tracking number configuration quantity is equal to zero, it must be at least one." /*)*/;
|
||||
static final String STOCK_MOVE_TOO_MANY_ITERATION = /*$$(*/
|
||||
"Too many iterations while trying to generate stock move line with tracking numbers." /*)*/;
|
||||
static final String STOCK_MOVE_CANNOT_GO_BACK_TO_DRAFT = /*$$(*/
|
||||
"Cannot go back to draft status." /*)*/;
|
||||
|
||||
/*
|
||||
* Stock Move printing
|
||||
*/
|
||||
String STOCK_MOVES_MISSING_PRINTING_SETTINGS = /*$$(*/
|
||||
"Please fill printing settings on following stock moves: %s" /*)*/;
|
||||
|
||||
String STOCK_MOVE_PRINT = /*$$(*/ "Please select the stock move(s) to print" /*)*/;
|
||||
|
||||
/** Tracking Number Service */
|
||||
static final String TRACKING_NUMBER_1 = /*$$(*/
|
||||
"There's no configured sequence for tracking number for the product %s:%s" /*)*/;
|
||||
|
||||
/** Stock Config Service */
|
||||
static final String STOCK_CONFIG_1 = /*$$(*/
|
||||
"You must configure a Stock module for the company %s" /*)*/;
|
||||
|
||||
static final String STOCK_CONFIG_2 = /*$$(*/
|
||||
"You must configure an inventory virtual stock location for the company %s" /*)*/;
|
||||
static final String STOCK_CONFIG_3 = /*$$(*/
|
||||
"You must configure a supplier virtual stock location for the company %s" /*)*/;
|
||||
static final String STOCK_CONFIG_4 = /*$$(*/
|
||||
"You must configure a customer virtual stock location for the company %s" /*)*/;
|
||||
static final String STOCK_CONFIG_RECEIPT = /*$$(*/
|
||||
"You must configure a default receipt stock location for the company %s" /*)*/;
|
||||
static final String STOCK_CONFIG_PICKUP = /*$$(*/
|
||||
"You must configure a default pickup stock location for the company %s" /*)*/;
|
||||
static final String STOCK_CONFIG_NON_COMPLIANT = /*$$(*/
|
||||
"You must configure a default non compliant stock location for the company %s" /*)*/;
|
||||
|
||||
/** Stock Location Controller */
|
||||
static final String LOCATION_1 = /*$$(*/
|
||||
"There's already an existing storage, you must deactivate it first" /*)*/;
|
||||
|
||||
static final String LOCATION_2 = /*$$(*/ "Please select the Stock Location(s) to print." /*)*/;
|
||||
|
||||
static final String STOCK_LOCATION_PRINT_WIZARD_TITLE = /*$$(*/ "Select format to Export" /*)*/;
|
||||
|
||||
/** Stock Move Line Service */
|
||||
static final String STOCK_MOVE_LINE_MUST_FILL_CONFORMITY =
|
||||
/*$$(*/ "Please fill the conformity for the product(s) : %s" /*)*/;
|
||||
|
||||
static final String STOCK_MOVE_LINE_MUST_FILL_TRACKING_NUMBER =
|
||||
/*$$(*/ "Please fill the tracking number for the product(s) : %s" /*)*/;
|
||||
|
||||
static final String STOCK_MOVE_LINE_EXPIRED_PRODUCTS = /*$$(*/ "Expired product(s): %s" /*)*/;
|
||||
|
||||
static final String MISSING_PRODUCT_MASS_UNIT = /*$$(*/
|
||||
"Please configure mass units for this product packing : %s" /*)*/;
|
||||
|
||||
static final String STOCK_CONFIGURATION_MISSING = /*$$(*/
|
||||
"Configuration is missing in stock configuration to see financial data" /*)*/;
|
||||
|
||||
/** Partner Product Quality Rating Service */
|
||||
String PARTNER_PRODUCT_QUALITY_RATING_MISSING_PARTNER = /*$$(*/ "Partner is missing." /*)*/;
|
||||
|
||||
/*
|
||||
* Logistical form
|
||||
*/
|
||||
String LOGISTICAL_FORM_MISSING_SEQUENCE = /*$$(*/
|
||||
"Missing logistical form sequence for company %s" /*)*/;
|
||||
String LOGISTICAL_FORM_PARTNER_MISMATCH = /*$$(*/ "Partner mismatch: %s" /*)*/;
|
||||
String LOGISTICAL_FORM_LINE_INVALID_DIMENSIONS = /*$$(*/
|
||||
"Invalid dimensions on packing line No. %d" /*)*/;
|
||||
String LOGISTICAL_FORM_LINE_REQUIRED_TYPE = /*$$(*/ "Type is required on line %d." /*)*/;
|
||||
String LOGISTICAL_FORM_LINE_REQUIRED_STOCK_MOVE_LINE = /*$$(*/
|
||||
"Stock move line is required on line %d." /*)*/;
|
||||
String LOGISTICAL_FORM_LINE_REQUIRED_QUANTITY = /*$$(*/ "Quantity is required on line %d." /*)*/;
|
||||
String LOGISTICAL_FORM_LINES_INCONSISTENT_QUANTITY = /*$$(*/
|
||||
"Total quantity for %s: %s (expected: %s)" /*)*/;
|
||||
String LOGISTICAL_FORM_LINES_EMPTY_PARCEL = /*$$(*/ "Parcel %d is empty." /*)*/;
|
||||
String LOGISTICAL_FORM_LINES_EMPTY_PALLET = /*$$(*/ "Pallet %d is empty." /*)*/;
|
||||
String LOGISTICAL_FORM_LINES_ORPHAN_DETAIL = /*$$(*/
|
||||
"Detail line(s) not inside a parcel/pallet" /*)*/;
|
||||
String LOGISTICAL_FORM_UNKNOWN_ACCOUNT_SELECTION = /*$$(*/ "Unknown account selection" /*)*/;
|
||||
|
||||
String LOGISTICAL_FORM_MISSING_TEMPLATE = /*$$(*/
|
||||
"The template to send message on save is missing." /*)*/;
|
||||
|
||||
String CANCEL_REASON_MISSING = /*$$(*/ "A cancel reason must be selected" /*)*/;
|
||||
String CANCEL_REASON_BAD_TYPE = /*$$(*/
|
||||
"The type of cancel reason doesn't match with stock move" /*)*/;
|
||||
|
||||
/*
|
||||
* Declaration of exchanges
|
||||
*/
|
||||
String DECLARATION_OF_EXCHANGES_ECONOMIC_AREA_MISSING = /*$$(*/
|
||||
"No economic area is configured for %s." /*)*/;
|
||||
String DECLARATION_OF_EXCHANGES_ECONOMIC_AREA_UNSUPPORTED = /*$$(*/
|
||||
"Declaration of exchanges for %s is not supported." /*)*/;
|
||||
String DECLARATION_OF_EXCHANGES_ECONOMIC_AREA_MISSING_IN_APP_STOCK = /*$$(*/
|
||||
"Please set an economic are in AppStock." /*)*/;
|
||||
|
||||
String TRACK_NUMBER_WIZARD_TITLE = /*$$(*/ "Enter tracking numbers" /*)*/;
|
||||
String TRACK_NUMBER_WIZARD_NO_RECORD_ADDED_ERROR = /*$$(*/ "No Tracking Numbers Added" /*)*/;
|
||||
|
||||
String TRACK_NUMBER_DATE_MISSING = /*$$(*/ "Please filled estimated delivery date" /*)*/;
|
||||
|
||||
/** Stock correction service and controller */
|
||||
public static final String STOCK_CORRECTION_1 = /*$$(*/
|
||||
"Incorrect product for stock correction" /*)*/;
|
||||
|
||||
public static final String STOCK_CORRECTION_2 = /*$$(*/
|
||||
"No stock move generated.Please verify stock correction details." /*)*/;
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.stock.exception;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.LogisticalFormLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
|
||||
public class LogisticalFormError extends AxelorException {
|
||||
|
||||
private static final long serialVersionUID = 354779411257144849L;
|
||||
|
||||
public LogisticalFormError(LogisticalForm logisticalForm, String message) {
|
||||
super(logisticalForm, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, message);
|
||||
}
|
||||
|
||||
public LogisticalFormError(
|
||||
LogisticalFormLine logisticalFormLine, String message, Object... messageArgs) {
|
||||
super(
|
||||
logisticalFormLine,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
String.format(message, messageArgs));
|
||||
}
|
||||
}
|
||||
@ -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.stock.exception;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
|
||||
public class LogisticalFormWarning extends AxelorException {
|
||||
|
||||
private static final long serialVersionUID = 7036277936135855411L;
|
||||
|
||||
public LogisticalFormWarning(LogisticalForm logisticalForm, String message) {
|
||||
super(logisticalForm, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.stock.module;
|
||||
|
||||
import com.axelor.app.AxelorModule;
|
||||
import com.axelor.apps.base.db.repo.PartnerAddressRepository;
|
||||
import com.axelor.apps.base.db.repo.ProductBaseRepository;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.repo.InventoryManagementRepository;
|
||||
import com.axelor.apps.stock.db.repo.InventoryRepository;
|
||||
import com.axelor.apps.stock.db.repo.LogisticalFormRepository;
|
||||
import com.axelor.apps.stock.db.repo.LogisticalFormStockRepository;
|
||||
import com.axelor.apps.stock.db.repo.ProductStockRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineStockRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationStockRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineStockRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveManagementRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberManagementRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.apps.stock.service.AddressServiceStockImpl;
|
||||
import com.axelor.apps.stock.service.LogisticalFormLineService;
|
||||
import com.axelor.apps.stock.service.LogisticalFormLineServiceImpl;
|
||||
import com.axelor.apps.stock.service.LogisticalFormService;
|
||||
import com.axelor.apps.stock.service.LogisticalFormServiceImpl;
|
||||
import com.axelor.apps.stock.service.PartnerProductQualityRatingService;
|
||||
import com.axelor.apps.stock.service.PartnerProductQualityRatingServiceImpl;
|
||||
import com.axelor.apps.stock.service.PartnerStockSettingsService;
|
||||
import com.axelor.apps.stock.service.PartnerStockSettingsServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockCorrectionService;
|
||||
import com.axelor.apps.stock.service.StockCorrectionServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockHistoryService;
|
||||
import com.axelor.apps.stock.service.StockHistoryServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockLocationLineService;
|
||||
import com.axelor.apps.stock.service.StockLocationLineServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockLocationService;
|
||||
import com.axelor.apps.stock.service.StockLocationServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockMoveLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveLineServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.stock.service.StockMoveServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockMoveToolService;
|
||||
import com.axelor.apps.stock.service.StockMoveToolServiceImpl;
|
||||
import com.axelor.apps.stock.service.StockRulesService;
|
||||
import com.axelor.apps.stock.service.StockRulesServiceImpl;
|
||||
import com.axelor.apps.stock.service.WeightedAveragePriceService;
|
||||
import com.axelor.apps.stock.service.WeightedAveragePriceServiceImpl;
|
||||
import com.axelor.apps.stock.service.app.AppStockService;
|
||||
import com.axelor.apps.stock.service.app.AppStockServiceImpl;
|
||||
import com.axelor.apps.stock.service.stockmove.print.ConformityCertificatePrintService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.ConformityCertificatePrintServiceImpl;
|
||||
import com.axelor.apps.stock.service.stockmove.print.PickingStockMovePrintService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.PickingStockMovePrintServiceimpl;
|
||||
import com.axelor.apps.stock.service.stockmove.print.StockMovePrintService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.StockMovePrintServiceImpl;
|
||||
|
||||
public class StockModule extends AxelorModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AddressServiceStockImpl.class);
|
||||
bind(StockRulesService.class).to(StockRulesServiceImpl.class);
|
||||
bind(InventoryRepository.class).to(InventoryManagementRepository.class);
|
||||
bind(StockMoveRepository.class).to(StockMoveManagementRepository.class);
|
||||
bind(StockLocationLineService.class).to(StockLocationLineServiceImpl.class);
|
||||
bind(StockMoveLineService.class).to(StockMoveLineServiceImpl.class);
|
||||
bind(StockMoveService.class).to(StockMoveServiceImpl.class);
|
||||
bind(StockLocationService.class).to(StockLocationServiceImpl.class);
|
||||
bind(ProductBaseRepository.class).to(ProductStockRepository.class);
|
||||
bind(PartnerProductQualityRatingService.class).to(PartnerProductQualityRatingServiceImpl.class);
|
||||
bind(LogisticalFormService.class).to(LogisticalFormServiceImpl.class);
|
||||
bind(LogisticalFormLineService.class).to(LogisticalFormLineServiceImpl.class);
|
||||
bind(LogisticalFormRepository.class).to(LogisticalFormStockRepository.class);
|
||||
bind(StockLocationRepository.class).to(StockLocationStockRepository.class);
|
||||
bind(PartnerStockSettingsService.class).to(PartnerStockSettingsServiceImpl.class);
|
||||
bind(AppStockService.class).to(AppStockServiceImpl.class);
|
||||
bind(StockMoveLineRepository.class).to(StockMoveLineStockRepository.class);
|
||||
PartnerAddressRepository.modelPartnerFieldMap.put(StockMove.class.getName(), "partner");
|
||||
bind(TrackingNumberRepository.class).to(TrackingNumberManagementRepository.class);
|
||||
bind(StockMovePrintService.class).to(StockMovePrintServiceImpl.class);
|
||||
bind(StockMoveToolService.class).to(StockMoveToolServiceImpl.class);
|
||||
bind(PickingStockMovePrintService.class).to(PickingStockMovePrintServiceimpl.class);
|
||||
bind(ConformityCertificatePrintService.class).to(ConformityCertificatePrintServiceImpl.class);
|
||||
bind(StockLocationLineRepository.class).to(StockLocationLineStockRepository.class);
|
||||
bind(StockCorrectionService.class).to(StockCorrectionServiceImpl.class);
|
||||
bind(WeightedAveragePriceService.class).to(WeightedAveragePriceServiceImpl.class);
|
||||
bind(StockHistoryService.class).to(StockHistoryServiceImpl.class);
|
||||
}
|
||||
}
|
||||
@ -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.stock.report;
|
||||
|
||||
public interface IReport {
|
||||
|
||||
public static final String STOCK_MOVE = "StockMove.rptdesign";
|
||||
public static final String PICKING_STOCK_MOVE = "PickingStockMove.rptdesign";
|
||||
public static final String CONFORMITY_CERTIFICATE = "ConformityCertificate.rptdesign";
|
||||
public static final String INVENTORY = "Inventory.rptdesign";
|
||||
public static final String STOCK_LOCATION = "StockLocation.rptdesign";
|
||||
public static final String STOCK_PRODUCTION_REQUEST = "StockProductionRequest.rptdesign";
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.stock.report;
|
||||
|
||||
public interface ITranslation {
|
||||
|
||||
public static final String STOCK_LOCATION_FINACIALDATA = /*$$(*/
|
||||
"StockLocation.locationFinancialData"; /*)*/
|
||||
public static final String STOCK_STOCK_LOCATION_CONTENT = /*$$(*/
|
||||
"StockLocation.stockLocationContent"; /*)*/
|
||||
public static final String STOCK_LOCATION_DATE = /*$$(*/ "StockLocation.date"; /*)*/
|
||||
public static final String STOCK_LOCATION_LOCATIONS = /*$$(*/ "StockLocation.locations"; /*)*/
|
||||
public static final String STOCK_LOCATION_LOCATION = /*$$(*/ "StockLocation.location"; /*)*/
|
||||
public static final String STOCK_LOCATION_TOTAL_SALE_VALUE = /*$$(*/
|
||||
"StockLocation.totalSaleValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_TOTAL_ACCOUNTING_VALUE = /*$$(*/
|
||||
"StockLocation.totalAccountingValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_TOTAL_WAP_VALUE = /*$$(*/
|
||||
"StockLocation.totalWapValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_LOCATION_TOTAL_CONTENT = /*$$(*/
|
||||
"StockLocation.locationTotalContent"; /*)*/
|
||||
public static final String STOCK_LOCATION_PRODUCT_NAME = /*$$(*/
|
||||
"StockLocation.productName"; /*)*/
|
||||
public static final String STOCK_LOCATION_CODE = /*$$(*/ "StockLocation.code"; /*)*/
|
||||
public static final String STOCK_LOCATION_QTY_UNIT = /*$$(*/ "StockLocation.qtyUnit"; /*)*/
|
||||
public static final String STOCK_LOCATION_SALE_VALUE = /*$$(*/ "StockLocation.saleValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_ACCOUNTING_VALUE = /*$$(*/
|
||||
"StockLocation.accountingValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_WAP_VALUE = /*$$(*/ "StockLocation.wapValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_UNIT_VALUE = /*$$(*/ "StockLocation.unitValue"; /*)*/
|
||||
public static final String STOCK_LOCATION_TOTAL = /*$$(*/ "StockLocation.total"; /*)*/
|
||||
public static final String STOCK_LOCATION_DETAILS_BY_STOCK_LOCATION = /*$$(*/
|
||||
"StockLocation.detailsByStockLocation"; /*)*/
|
||||
|
||||
public static final String INVENTORY_TITLE = /*$$(*/ "Inventory.title"; /*)*/
|
||||
public static final String INVENTORY_NAME = /*$$(*/ "Inventory.name"; /*)*/
|
||||
public static final String INVENTORY_BARCODE = /*$$(*/ "Inventory.barcode"; /*)*/
|
||||
public static final String INVENTORY_DATE = /*$$(*/ "Inventory.date"; /*)*/
|
||||
public static final String INVENTORY_STOCK_LOCATION = /*$$(*/ "Inventory.stockLocation"; /*)*/
|
||||
public static final String INVENTORY_CODE = /*$$(*/ "Inventory.code"; /*)*/
|
||||
public static final String INVENTORY_UNIT = /*$$(*/ "Inventory.unit"; /*)*/
|
||||
public static final String INVENTORY_REAL_QTY = /*$$(*/ "Inventory.realQty"; /*)*/
|
||||
public static final String INVENTORY_DESCRIPTION = /*$$(*/ "Inventory.description"; /*)*/
|
||||
public static final String INVENTORY_RACK = /*$$(*/ "Inventory.rack"; /*)*/
|
||||
public static final String INVENTORY_TRACKING_NUMBER = /*$$(*/ "Inventory.trackingNumber"; /*)*/
|
||||
public static final String INVENTORY_COMPANY = /*$$(*/ "Inventory.company"; /*)*/
|
||||
public static final String INVENTORY_PLANNED_START_DATE = /*$$(*/
|
||||
"Inventory.plannedStartDateT"; /*)*/
|
||||
public static final String INVENTORY_PLANNED_END_DATE = /*$$(*/ "Inventory.plannedEndDateT"; /*)*/
|
||||
public static final String INVENTORY_CREATED_ON_DATE = /*$$(*/ "Inventory.createdOn"; /*)*/
|
||||
public static final String INVENTORY_PRODUCT_CATEGORY = /*$$(*/ "Inventory.productCategory"; /*)*/
|
||||
public static final String INVENTORY_LAST_INVENTORY_DATE = /*$$(*/
|
||||
"Inventory.lastInventoryDate"; /*)*/
|
||||
public static final String INVENTORY_PRODUCT = /*$$(*/ "Inventory.product"; /*)*/
|
||||
public static final String INVENTORY_PRODUCT_FAMILY = /*$$(*/ "Inventory.productFamily"; /*)*/
|
||||
public static final String INVENTORY_FROM_RACK = /*$$(*/ "Inventory.fromRack"; /*)*/
|
||||
public static final String INVENTORY_TO_RACK = /*$$(*/ "Inventory.toRack"; /*)*/
|
||||
public static final String INVENTORY_CATEGORY = /*$$(*/ "Inventory.category"; /*)*/
|
||||
|
||||
public static final String STOCK_MOVE_INTERNAL_MOVE = /*$$(*/ "StockMove.internalMove"; /*)*/
|
||||
public static final String STOCK_MOVE_DELIVERY_ORDER = /*$$(*/ "StockMove.deliveryOrder"; /*)*/
|
||||
public static final String STOCK_MOVE_RECEPTION_ORDER = /*$$(*/ "StockMove.receptionOrder"; /*)*/
|
||||
public static final String STOCK_MOVE_DATE = /*$$(*/ "StockMove.date"; /*)*/
|
||||
public static final String STOCK_MOVE_REFERENCE = /*$$(*/ "StockMove.reference"; /*)*/
|
||||
public static final String STOCK_MOVE_PURCHASE_ORDER = /*$$(*/ "StockMove.purchaseOrder"; /*)*/
|
||||
public static final String STOCK_MOVE_MASS = /*$$(*/ "StockMove.mass"; /*)*/
|
||||
public static final String STOCK_MOVE_CUSTOMER = /*$$(*/ "StockMove.customer"; /*)*/
|
||||
public static final String STOCK_MOVE_MOVE_PREPARED_BY = /*$$(*/ "StockMove.movePreparedBy"; /*)*/
|
||||
public static final String STOCK_MOVE_STOCK_PREPARED_BY = /*$$(*/
|
||||
"StockMove.stockPreparedBy"; /*)*/
|
||||
public static final String STOCK_MOVE_RECEIVED_BY = /*$$(*/ "StockMove.receivedBy"; /*)*/
|
||||
public static final String STOCK_MOVE_DELIVERY_ADDRESS = /*$$(*/
|
||||
"StockMove.deliveryAddress"; /*)*/
|
||||
public static final String STOCK_MOVE_DELIVERY_DETAILS = /*$$(*/
|
||||
"StockMove.deliveryDetails"; /*)*/
|
||||
public static final String STOCK_MOVE_VARIANT = /*$$(*/ "StockMove.variant"; /*)*/
|
||||
public static final String STOCK_MOVE_QTY_UNIT = /*$$(*/ "StockMove.qtyUnit"; /*)*/
|
||||
public static final String STOCK_MOVE_LOT_NO_REF = /*$$(*/ "StockMove.lotNoRef"; /*)*/
|
||||
public static final String STOCK_MOVE_PRODUCT = /*$$(*/ "StockMove.product"; /*)*/
|
||||
public static final String STOCK_MOVE_PACKAGE = /*$$(*/ "StockMove.numOfPackages"; /*)*/
|
||||
public static final String STOCK_MOVE_PALETTES = /*$$(*/ "StockMove.numOfPalettes"; /*)*/
|
||||
public static final String STOCK_MOVE_GROSS_MASS = /*$$(*/ "StockMove.grossMass"; /*)*/
|
||||
public static final String STOCK_MOVE_DESCRIPTION = /*$$(*/ "StockMove.description"; /*)*/
|
||||
public static final String STOCK_MOVE_ISPM = /*$$(*/ "StockMove.ispm"; /*)*/
|
||||
public static final String STOCK_MOVE_CUSTOMER_PARTNER_SEQ = /*$$(*/
|
||||
"StockMove.customerPartnerSeq"; /*)*/
|
||||
public static final String STOCK_MOVE_SUPPLIER_PARTNER_SEQ = /*$$(*/
|
||||
"StockMove.supplierPartnerSeq"; /*)*/
|
||||
public static final String STOCK_MOVE_NET_MASS = /*$$(*/ "StockMove.netMass"; /*)*/
|
||||
|
||||
// PickingStockMove
|
||||
public static final String STOCK_MOVE_PICKING_ORDER = /*$$(*/ "StockMove.pickingOrder"; /*)*/
|
||||
public static final String STOCK_MOVE_RACK_NUMBER = /*$$(*/ "StockMove.rackNbr"; /*)*/
|
||||
public static final String STOCK_MOVE_COMMENTS = /*$$(*/ "StockMove.comments"; /*)*/
|
||||
public static final String STOCK_MOVE_BARCODE = /*$$(*/ "StockMove.barcode"; /*)*/
|
||||
public static final String STOCK_MOVE_PRODUCTION_NOTE = /*$$(*/ "StockMove.productionNote"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_LINE_NUMBER = /*$$(*/
|
||||
"PickingStockMove.lineNumber"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_WEEK_CODE = /*$$(*/
|
||||
"PickingStockMove.weekCode"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_SIGNALING = /*$$(*/
|
||||
"PickingStockMove.signaling"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_CODE = /*$$(*/ "PickingStockMove.code"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_MANUAL = /*$$(*/ "PickingStockMove.manual"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_ASPECT = /*$$(*/ "PickingStockMove.aspect"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_MARK = /*$$(*/ "PickingStockMove.mark"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_VISA = /*$$(*/ "PickingStockMove.visa"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_QTY = /*$$(*/ "PickingStockMove.qty"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_WITH_WITHOUT = /*$$(*/
|
||||
"PickingStockMove.withWithout"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_CUSTOMER_CODE = /*$$(*/
|
||||
"PickingStockMove.customerCode"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_EXTERNAL_REFERENCE = /*$$(*/
|
||||
"PickingStockMove.externalReference"; /*)*/
|
||||
public static final String PICKING_STOCK_MOVE_DELIVERY_CONDITION = /*$$(*/
|
||||
"PickingStockMove.deliveryCondition"; /*)*/
|
||||
|
||||
// Certificate of conformity
|
||||
public static final String CONFORMITY_CERTIFICATE_NUMBER = /*$$(*/
|
||||
"ConformityCertificate.certificateNumber"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_CUSTOMER_NAME = /*$$(*/
|
||||
"ConformityCertificate.customerName"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_ORDER_NUMBER = /*$$(*/
|
||||
"ConformityCertificate.orderNumber"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_DATE_OF_ORDER = /*$$(*/
|
||||
"ConformityCertificate.orderDate"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_NUMBER_AND_DATE_DELIVERY = /*$$(*/
|
||||
"ConformityCertificate.numberAndDateDelivery"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_PART_NUMBER = /*$$(*/
|
||||
"ConformityCertificate.partNumber"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_DESIGNATION = /*$$(*/
|
||||
"ConformityCertificate.designation"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_QUANTITY = /*$$(*/
|
||||
"ConformityCertificate.quantity"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_SIGNATURE = /*$$(*/
|
||||
"ConformityCertificate.signature"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_NAME = /*$$(*/
|
||||
"ConformityCertificate.name"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_FUNCTION = /*$$(*/
|
||||
"ConformityCertificate.function"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_QTY_UNIT = /*$$(*/
|
||||
"ConformityCertificate.qtyUnit"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_SEQUENCE = /*$$(*/
|
||||
"ConformityCertificate.sequence"; /*)*/
|
||||
public static final String CONFORMITY_CERTIFICATE_STOCK_MOVE_LINE_ROW_NUM = /*$$(*/
|
||||
"ConformityCertificate.stockMoveLineRowNum"; /*)*/
|
||||
public static final String CONFORMITY_LOT_NO_REF = /*$$(*/ "ConformityCertificate.lotNoRef"; /*)*/
|
||||
public static final String CONFORMITY_EXTERNAL_REFERENCE = /*$$(*/
|
||||
"ConformityCertificate.externalReference"; /*)*/
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import static com.axelor.apps.base.service.administration.AbstractBatch.FETCH_LIMIT;
|
||||
|
||||
import com.axelor.apps.base.db.ABCAnalysis;
|
||||
import com.axelor.apps.base.db.ABCAnalysisLine;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
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.service.ABCAnalysisServiceImpl;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.Query;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ABCAnalysisServiceStockImpl extends ABCAnalysisServiceImpl {
|
||||
|
||||
protected StockLocationService stockLocationService;
|
||||
protected StockLocationLineRepository stockLocationLineRepository;
|
||||
|
||||
private static final String STOCK_MANAGED_TRUE = " AND self.stockManaged = TRUE";
|
||||
|
||||
@Inject
|
||||
public ABCAnalysisServiceStockImpl(
|
||||
ABCAnalysisLineRepository abcAnalysisLineRepository,
|
||||
UnitConversionService unitConversionService,
|
||||
ABCAnalysisRepository abcAnalysisRepository,
|
||||
ProductRepository productRepository,
|
||||
StockLocationService stockLocationService,
|
||||
StockLocationLineRepository stockLocationLineRepository,
|
||||
ABCAnalysisClassRepository abcAnalysisClassRepository,
|
||||
SequenceService sequenceService) {
|
||||
super(
|
||||
abcAnalysisLineRepository,
|
||||
unitConversionService,
|
||||
abcAnalysisRepository,
|
||||
productRepository,
|
||||
abcAnalysisClassRepository,
|
||||
sequenceService);
|
||||
this.stockLocationService = stockLocationService;
|
||||
this.stockLocationLineRepository = stockLocationLineRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<ABCAnalysisLine> createABCAnalysisLine(
|
||||
ABCAnalysis abcAnalysis, Product product) throws AxelorException {
|
||||
ABCAnalysisLine abcAnalysisLine = null;
|
||||
List<StockLocation> stockLocationList =
|
||||
stockLocationService.getAllLocationAndSubLocation(abcAnalysis.getStockLocation(), false);
|
||||
BigDecimal productQty = BigDecimal.ZERO;
|
||||
BigDecimal productWorth = BigDecimal.ZERO;
|
||||
List<StockLocationLine> stockLocationLineList;
|
||||
int offset = 0;
|
||||
|
||||
Query<StockLocationLine> stockLocationLineQuery =
|
||||
stockLocationLineRepository
|
||||
.all()
|
||||
.filter(
|
||||
"self.stockLocation IN :stockLocationList AND self.product.id = :productId AND self.currentQty != 0 ")
|
||||
.bind("stockLocationList", stockLocationList)
|
||||
.bind("productId", product.getId());
|
||||
|
||||
while (!(stockLocationLineList = stockLocationLineQuery.fetch(FETCH_LIMIT, offset)).isEmpty()) {
|
||||
offset += stockLocationLineList.size();
|
||||
abcAnalysis = abcAnalysisRepository.find(abcAnalysis.getId());
|
||||
|
||||
if (abcAnalysisLine == null) {
|
||||
abcAnalysisLine = super.createABCAnalysisLine(abcAnalysis, product).get();
|
||||
}
|
||||
|
||||
for (StockLocationLine stockLocationLine : stockLocationLineList) {
|
||||
BigDecimal convertedQty =
|
||||
unitConversionService.convert(
|
||||
stockLocationLine.getUnit(),
|
||||
product.getUnit(),
|
||||
stockLocationLine.getCurrentQty(),
|
||||
5,
|
||||
product);
|
||||
productQty = productQty.add(convertedQty);
|
||||
productWorth = productWorth.add(stockLocationLine.getAvgPrice());
|
||||
}
|
||||
|
||||
super.incTotalQty(productQty);
|
||||
super.incTotalWorth(productWorth);
|
||||
|
||||
JPA.clear();
|
||||
}
|
||||
|
||||
if (abcAnalysisLine != null) {
|
||||
setQtyWorth(
|
||||
abcAnalysisLineRepository.find(abcAnalysisLine.getId()), productQty, productWorth);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(abcAnalysisLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProductCategoryQuery() {
|
||||
return super.getProductCategoryQuery() + STOCK_MANAGED_TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProductFamilyQuery() {
|
||||
return super.getProductFamilyQuery() + STOCK_MANAGED_TRUE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.service.AddressServiceImpl;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.db.JPA;
|
||||
|
||||
public class AddressServiceStockImpl extends AddressServiceImpl {
|
||||
static {
|
||||
registerCheckUsedFunc(AddressServiceStockImpl::checkAddressUsedStock);
|
||||
}
|
||||
|
||||
private static boolean checkAddressUsedStock(Long addressId) {
|
||||
return JPA.all(StockMove.class)
|
||||
.filter("self.fromAddress.id = ?1 OR self.toAddress.id = ?1", addressId)
|
||||
.fetchOne()
|
||||
!= null
|
||||
|| JPA.all(StockLocation.class).filter("self.address.id = ?1", addressId).fetchOne()
|
||||
!= null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.axelor.apps.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.InternalTrackingNumber;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.InternalTrackingNumberRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockConfigRepository;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class InternalTrackingNumberService {
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public InternalTrackingNumber createInternalTrackingNumber(
|
||||
Product product, Company company, LocalDate date, TrackingNumber trackingNumber)
|
||||
throws AxelorException {
|
||||
|
||||
InternalTrackingNumber internalTrackingNumber = new InternalTrackingNumber();
|
||||
internalTrackingNumber.setProduct(product);
|
||||
internalTrackingNumber.setTrackingNumber(trackingNumber);
|
||||
internalTrackingNumber.setPerishableExpirationDate(
|
||||
trackingNumber.getPerishableExpirationDate());
|
||||
internalTrackingNumber.setFabricationDate(internalTrackingNumber.getFabricationDate());
|
||||
internalTrackingNumber.setReceptionDate(date);
|
||||
|
||||
Long categoryId = product.getFamilleProduit().getId();
|
||||
|
||||
StockConfig stockConfig =
|
||||
Beans.get(StockConfigRepository.class).all().filter("self.company = ?", company).fetchOne();
|
||||
|
||||
String sequence = "";
|
||||
|
||||
switch (categoryId.intValue()) {
|
||||
case 67:
|
||||
sequence = Beans.get(StockMoveToolServiceImpl.class).getInternalSequence(36, company, date);
|
||||
stockConfig.setMpInternalSeq((stockConfig.getMpInternalSeq() + 1));
|
||||
break;
|
||||
case 68:
|
||||
sequence = Beans.get(StockMoveToolServiceImpl.class).getInternalSequence(36, company, date);
|
||||
stockConfig.setMpInternalSeq((stockConfig.getMpInternalSeq() + 1));
|
||||
break;
|
||||
case 59:
|
||||
sequence = Beans.get(StockMoveToolServiceImpl.class).getInternalSequence(35, company, date);
|
||||
stockConfig.setAcInternalSeq((stockConfig.getMpInternalSeq() + 1));
|
||||
break;
|
||||
default:
|
||||
// sequence = "AC" + stockConfig.getAcInternalSeq() + month + formattedYear;
|
||||
// stockConfig.setAcInternalSeq((stockConfig.getMpInternalSeq() + 1));
|
||||
break;
|
||||
}
|
||||
|
||||
Beans.get(StockConfigRepository.class).save(stockConfig);
|
||||
internalTrackingNumber.setTrackingNumberSeq(sequence);
|
||||
|
||||
return internalTrackingNumber;
|
||||
}
|
||||
|
||||
public InternalTrackingNumber getInternalTrackingNumber(
|
||||
Product product, TrackingNumber trackingNumber) {
|
||||
InternalTrackingNumber internalTrackingNumber =
|
||||
Beans.get(InternalTrackingNumberRepository.class)
|
||||
.all()
|
||||
.filter("self.product = ?1 and self.trackingNumber = ?2", product, trackingNumber)
|
||||
.fetchOne();
|
||||
|
||||
return internalTrackingNumber;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.db.InventoryLine;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.InventoryLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.auth.AuthUtils;
|
||||
import com.axelor.db.Query;
|
||||
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.LocalDateTime;
|
||||
|
||||
public class InventoryLineService {
|
||||
|
||||
@Inject private ProductRepository productRepository;
|
||||
private TrackingNumberRepository trackingNumberRepository;
|
||||
private InventoryLineRepository inventoryLineRepository;
|
||||
|
||||
public InventoryLine createInventoryLine(
|
||||
Inventory inventory,
|
||||
Product product,
|
||||
BigDecimal currentQty,
|
||||
String rack,
|
||||
TrackingNumber trackingNumber) {
|
||||
|
||||
InventoryLine inventoryLine = new InventoryLine();
|
||||
inventoryLine.setInventory(inventory);
|
||||
inventoryLine.setProduct(product);
|
||||
inventoryLine.setRack(rack);
|
||||
inventoryLine.setCurrentQty(currentQty);
|
||||
inventoryLine.setTrackingNumber(trackingNumber);
|
||||
this.compute(inventoryLine, inventory);
|
||||
|
||||
return inventoryLine;
|
||||
}
|
||||
|
||||
public InventoryLine updateInventoryLine(InventoryLine inventoryLine, Inventory inventory) {
|
||||
|
||||
StockLocation stockLocation = inventory.getStockLocation();
|
||||
Product product = inventoryLine.getProduct();
|
||||
|
||||
if (product != null) {
|
||||
StockLocationLine stockLocationLine =
|
||||
Beans.get(StockLocationLineService.class)
|
||||
.getOrCreateStockLocationLine(stockLocation, product);
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
inventoryLine.setCurrentQty(stockLocationLine.getCurrentQty());
|
||||
inventoryLine.setRack(stockLocationLine.getRack());
|
||||
} else {
|
||||
inventoryLine.setCurrentQty(null);
|
||||
inventoryLine.setRack(null);
|
||||
}
|
||||
}
|
||||
|
||||
return inventoryLine;
|
||||
}
|
||||
|
||||
public InventoryLine compute(InventoryLine inventoryLine, Inventory inventory) {
|
||||
|
||||
StockLocation stockLocation = inventory.getStockLocation();
|
||||
Product product = inventoryLine.getProduct();
|
||||
|
||||
if (product != null) {
|
||||
StockLocationLine stockLocationLine =
|
||||
Beans.get(StockLocationLineService.class).getStockLocationLine(stockLocation, product);
|
||||
inventoryLine.setUnit(product.getUnit());
|
||||
BigDecimal gap =
|
||||
inventoryLine.getRealQty() != null
|
||||
? inventoryLine
|
||||
.getCurrentQty()
|
||||
.subtract(inventoryLine.getRealQty())
|
||||
.setScale(2, RoundingMode.HALF_EVEN)
|
||||
: BigDecimal.ZERO;
|
||||
inventoryLine.setGap(gap);
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
inventoryLine.setGapValue(
|
||||
stockLocationLine.getAvgPrice().multiply(gap).setScale(2, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
}
|
||||
|
||||
return inventoryLine;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void setInventoryLine(
|
||||
Inventory inventory,
|
||||
Product product,
|
||||
TrackingNumber trackingNumber,
|
||||
int countingType,
|
||||
BigDecimal firstCounting,
|
||||
BigDecimal secondCounting,
|
||||
BigDecimal controlCounting) {
|
||||
InventoryLine line;
|
||||
|
||||
Query<InventoryLine> query = Beans.get(InventoryLineRepository.class).all();
|
||||
|
||||
if (trackingNumber != null) {
|
||||
line =
|
||||
query
|
||||
.filter(
|
||||
"self.product.id = ?1 AND self.trackingNumber.id = ?2 and self.inventory.id = ?3",
|
||||
product.getId(),
|
||||
trackingNumber.getId(),
|
||||
inventory.getId())
|
||||
.fetchOne();
|
||||
} else {
|
||||
line =
|
||||
query
|
||||
.filter(
|
||||
"self.product.id = ?1 AND self.trackingNumber is null and self.inventory.id = ?2",
|
||||
product.getId(),
|
||||
inventory.getId())
|
||||
.fetchOne();
|
||||
}
|
||||
|
||||
if (line == null) {
|
||||
line = this.createInventoryLine(inventory, product, BigDecimal.ZERO, null, trackingNumber);
|
||||
}
|
||||
|
||||
BigDecimal counting = BigDecimal.ZERO;
|
||||
|
||||
switch (countingType) {
|
||||
case 1:
|
||||
counting = line.getFirstCounting() != null ? line.getFirstCounting() : BigDecimal.ZERO;
|
||||
line.setFirstCounting(counting.add(firstCounting));
|
||||
line.setFirstCountingByUser(AuthUtils.getUser());
|
||||
line.setFirstCountingDate(LocalDateTime.now());
|
||||
break;
|
||||
case 2:
|
||||
counting = line.getSecondCounting() != null ? line.getSecondCounting() : BigDecimal.ZERO;
|
||||
line.setSecondCounting(counting.add(secondCounting));
|
||||
line.setSecondCountingByUser(AuthUtils.getUser());
|
||||
line.setSecondCountingDate(LocalDateTime.now());
|
||||
break;
|
||||
case 3:
|
||||
counting = line.getControlCounting() != null ? line.getControlCounting() : BigDecimal.ZERO;
|
||||
line.setControlCounting(counting.add(controlCounting));
|
||||
line.setControlCountingByUser(AuthUtils.getUser());
|
||||
line.setControlCountingDate(LocalDateTime.now());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Beans.get(InventoryLineRepository.class).save(line);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,720 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.app.AppSettings;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.ProductCategory;
|
||||
import com.axelor.apps.base.db.ProductFamily;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.db.repo.SequenceRepository;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.db.InventoryLine;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.InventoryRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
import com.axelor.apps.tool.file.CsvTool;
|
||||
import com.axelor.auth.AuthUtils;
|
||||
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.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InventoryService {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected InventoryLineService inventoryLineService;
|
||||
protected SequenceService sequenceService;
|
||||
protected StockConfigService stockConfigService;
|
||||
protected ProductRepository productRepo;
|
||||
protected InventoryRepository inventoryRepo;
|
||||
protected StockMoveRepository stockMoveRepo;
|
||||
protected StockLocationLineService stockLocationLineService;
|
||||
protected StockMoveService stockMoveService;
|
||||
protected StockMoveLineService stockMoveLineService;
|
||||
protected StockLocationLineRepository stockLocationLineRepository;
|
||||
protected TrackingNumberRepository trackingNumberRepository;
|
||||
protected AppBaseService appBaseService;
|
||||
|
||||
@Inject
|
||||
public InventoryService(
|
||||
InventoryLineService inventoryLineService,
|
||||
SequenceService sequenceService,
|
||||
StockConfigService stockConfigService,
|
||||
ProductRepository productRepo,
|
||||
InventoryRepository inventoryRepo,
|
||||
StockMoveRepository stockMoveRepo,
|
||||
StockLocationLineService stockLocationLineService,
|
||||
StockMoveService stockMoveService,
|
||||
StockMoveLineService stockMoveLineService,
|
||||
StockLocationLineRepository stockLocationLineRepository,
|
||||
TrackingNumberRepository trackingNumberRepository,
|
||||
AppBaseService appBaseService) {
|
||||
this.inventoryLineService = inventoryLineService;
|
||||
this.sequenceService = sequenceService;
|
||||
this.stockConfigService = stockConfigService;
|
||||
this.productRepo = productRepo;
|
||||
this.inventoryRepo = inventoryRepo;
|
||||
this.stockMoveRepo = stockMoveRepo;
|
||||
this.stockLocationLineService = stockLocationLineService;
|
||||
this.stockMoveService = stockMoveService;
|
||||
this.stockMoveLineService = stockMoveLineService;
|
||||
this.stockLocationLineRepository = stockLocationLineRepository;
|
||||
this.trackingNumberRepository = trackingNumberRepository;
|
||||
this.appBaseService = appBaseService;
|
||||
}
|
||||
|
||||
public Inventory createInventory(
|
||||
LocalDate plannedStartDate,
|
||||
LocalDate plannedEndDate,
|
||||
String description,
|
||||
StockLocation stockLocation,
|
||||
boolean excludeOutOfStock,
|
||||
boolean includeObsolete,
|
||||
ProductFamily productFamily,
|
||||
ProductCategory productCategory)
|
||||
throws AxelorException {
|
||||
|
||||
if (stockLocation == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_1));
|
||||
}
|
||||
|
||||
Inventory inventory = new Inventory();
|
||||
|
||||
inventory.setInventorySeq(this.getInventorySequence(stockLocation.getCompany()));
|
||||
|
||||
inventory.setPlannedStartDateT(plannedStartDate.atStartOfDay(ZoneOffset.UTC));
|
||||
|
||||
inventory.setPlannedEndDateT(plannedEndDate.atStartOfDay(ZoneOffset.UTC));
|
||||
|
||||
inventory.setDescription(description);
|
||||
|
||||
inventory.setFormatSelect(InventoryRepository.FORMAT_PDF);
|
||||
|
||||
inventory.setStockLocation(stockLocation);
|
||||
|
||||
inventory.setExcludeOutOfStock(excludeOutOfStock);
|
||||
|
||||
inventory.setIncludeObsolete(includeObsolete);
|
||||
|
||||
inventory.setProductCategory(productCategory);
|
||||
|
||||
inventory.setProductFamily(productFamily);
|
||||
|
||||
inventory.setStatusSelect(InventoryRepository.STATUS_DRAFT);
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public String getInventorySequence(Company company) throws AxelorException {
|
||||
|
||||
String ref = sequenceService.getSequenceNumber(SequenceRepository.INVENTORY, company);
|
||||
if (ref == null)
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_2) + " " + company.getName());
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Path importFile(Inventory inventory) throws AxelorException {
|
||||
|
||||
List<InventoryLine> inventoryLineList = inventory.getInventoryLineList();
|
||||
Path filePath = MetaFiles.getPath(inventory.getImportFile());
|
||||
List<String[]> data = this.getDatas(filePath);
|
||||
|
||||
HashMap<String, InventoryLine> inventoryLineMap = this.getInventoryLines(inventory);
|
||||
|
||||
for (String[] line : data) {
|
||||
if (line.length < 6)
|
||||
throw new AxelorException(
|
||||
new Throwable(I18n.get(IExceptionMessage.INVENTORY_3_LINE_LENGHT)),
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_3));
|
||||
|
||||
String code = line[1].replace("\"", "");
|
||||
String rack = line[2].replace("\"", "");
|
||||
String trackingNumberSeq = line[3].replace("\"", "");
|
||||
|
||||
BigDecimal realQty;
|
||||
try {
|
||||
realQty = new BigDecimal(line[5].replace("\"", ""));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AxelorException(
|
||||
new Throwable(I18n.get(IExceptionMessage.INVENTORY_3_REAL_QUANTITY)),
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_3));
|
||||
}
|
||||
|
||||
String description = line[6].replace("\"", "");
|
||||
|
||||
if (inventoryLineMap.containsKey(code)) {
|
||||
inventoryLineMap.get(code).setRealQty(realQty);
|
||||
inventoryLineMap.get(code).setDescription(description);
|
||||
} else {
|
||||
BigDecimal currentQty;
|
||||
try {
|
||||
currentQty = new BigDecimal(line[4].replace("\"", ""));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AxelorException(
|
||||
new Throwable(I18n.get(IExceptionMessage.INVENTORY_3_CURRENT_QUANTITY)),
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_3));
|
||||
}
|
||||
|
||||
InventoryLine inventoryLine = new InventoryLine();
|
||||
List<Product> productList =
|
||||
productRepo.all().filter("self.code = :code").bind("code", code).fetch();
|
||||
if (productList != null && !productList.isEmpty()) {
|
||||
if (productList.size() > 1) {
|
||||
throw new AxelorException(
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_12) + " " + code);
|
||||
}
|
||||
}
|
||||
Product product = productList.get(0);
|
||||
if (product == null
|
||||
|| !product.getProductTypeSelect().equals(ProductRepository.PRODUCT_TYPE_STORABLE))
|
||||
throw new AxelorException(
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_4) + " " + code);
|
||||
inventoryLine.setProduct(product);
|
||||
inventoryLine.setInventory(inventory);
|
||||
inventoryLine.setRack(rack);
|
||||
inventoryLine.setCurrentQty(currentQty);
|
||||
inventoryLine.setRealQty(realQty);
|
||||
inventoryLine.setDescription(description);
|
||||
inventoryLine.setTrackingNumber(this.getTrackingNumber(trackingNumberSeq));
|
||||
inventoryLineList.add(inventoryLine);
|
||||
}
|
||||
}
|
||||
inventory.setInventoryLineList(inventoryLineList);
|
||||
|
||||
inventoryRepo.save(inventory);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public List<String[]> getDatas(Path filePath) throws AxelorException {
|
||||
|
||||
List<String[]> data = null;
|
||||
char separator = ';';
|
||||
try {
|
||||
data = CsvTool.cSVFileReader(filePath.toString(), separator);
|
||||
} catch (Exception e) {
|
||||
throw new AxelorException(
|
||||
e.getCause(),
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_5));
|
||||
}
|
||||
|
||||
if (data == null || data.isEmpty()) {
|
||||
throw new AxelorException(
|
||||
new Throwable(I18n.get(IExceptionMessage.INVENTORY_3_DATA_NULL_OR_EMPTY)),
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_3));
|
||||
}
|
||||
|
||||
data.remove(0); /* Skip headers */
|
||||
return data;
|
||||
}
|
||||
|
||||
public HashMap<String, InventoryLine> getInventoryLines(Inventory inventory) {
|
||||
HashMap<String, InventoryLine> inventoryLineMap = new HashMap<>();
|
||||
|
||||
for (InventoryLine line : inventory.getInventoryLineList()) {
|
||||
String key = "";
|
||||
if (line.getProduct() != null) {
|
||||
key += line.getProduct().getCode();
|
||||
}
|
||||
if (line.getTrackingNumber() != null) {
|
||||
key += line.getTrackingNumber().getTrackingNumberSeq();
|
||||
}
|
||||
|
||||
inventoryLineMap.put(key, line);
|
||||
}
|
||||
|
||||
return inventoryLineMap;
|
||||
}
|
||||
|
||||
public TrackingNumber getTrackingNumber(String sequence) {
|
||||
|
||||
if (sequence != null && !sequence.isEmpty()) {
|
||||
return trackingNumberRepository.findBySeq(sequence);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void validateInventory(Inventory inventory) throws AxelorException {
|
||||
|
||||
inventory.setValidatedOn(appBaseService.getTodayDate());
|
||||
inventory.setStatusSelect(InventoryRepository.STATUS_VALIDATED);
|
||||
inventory.setValidatedBy(AuthUtils.getUser());
|
||||
generateStockMove(inventory, true);
|
||||
generateStockMove(inventory, false);
|
||||
storeLastInventoryData(inventory);
|
||||
}
|
||||
|
||||
private void storeLastInventoryData(Inventory inventory) {
|
||||
Map<Pair<Product, TrackingNumber>, BigDecimal> realQties = new HashMap<>();
|
||||
Map<Product, BigDecimal> consolidatedRealQties = new HashMap<>();
|
||||
Map<Product, String> realRacks = new HashMap<>();
|
||||
|
||||
List<InventoryLine> inventoryLineList = inventory.getInventoryLineList();
|
||||
|
||||
if (inventoryLineList != null) {
|
||||
for (InventoryLine inventoryLine : inventoryLineList) {
|
||||
Product product = inventoryLine.getProduct();
|
||||
TrackingNumber trackingNumber = inventoryLine.getTrackingNumber();
|
||||
|
||||
realQties.put(Pair.of(product, trackingNumber), inventoryLine.getRealQty());
|
||||
|
||||
BigDecimal realQty = consolidatedRealQties.getOrDefault(product, BigDecimal.ZERO);
|
||||
realQty = realQty.add(inventoryLine.getRealQty());
|
||||
consolidatedRealQties.put(product, realQty);
|
||||
|
||||
realRacks.put(product, inventoryLine.getRack());
|
||||
}
|
||||
}
|
||||
|
||||
List<StockLocationLine> stockLocationLineList =
|
||||
inventory.getStockLocation().getStockLocationLineList();
|
||||
|
||||
if (stockLocationLineList != null) {
|
||||
for (StockLocationLine stockLocationLine : stockLocationLineList) {
|
||||
Product product = stockLocationLine.getProduct();
|
||||
BigDecimal realQty = consolidatedRealQties.get(product);
|
||||
if (realQty != null) {
|
||||
stockLocationLine.setLastInventoryRealQty(realQty);
|
||||
stockLocationLine.setLastInventoryDateT(
|
||||
inventory.getValidatedOn().atStartOfDay().atZone(ZoneOffset.UTC));
|
||||
}
|
||||
|
||||
String rack = realRacks.get(product);
|
||||
if (rack != null) {
|
||||
stockLocationLine.setRack(rack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<StockLocationLine> detailsStockLocationLineList =
|
||||
inventory.getStockLocation().getDetailsStockLocationLineList();
|
||||
|
||||
if (detailsStockLocationLineList != null) {
|
||||
for (StockLocationLine detailsStockLocationLine : detailsStockLocationLineList) {
|
||||
Product product = detailsStockLocationLine.getProduct();
|
||||
TrackingNumber trackingNumber = detailsStockLocationLine.getTrackingNumber();
|
||||
BigDecimal realQty = realQties.get(Pair.of(product, trackingNumber));
|
||||
if (realQty != null) {
|
||||
detailsStockLocationLine.setLastInventoryRealQty(realQty);
|
||||
detailsStockLocationLine.setLastInventoryDateT(
|
||||
inventory.getValidatedOn().atStartOfDay().atZone(ZoneOffset.UTC));
|
||||
}
|
||||
|
||||
String rack = realRacks.get(product);
|
||||
if (rack != null) {
|
||||
detailsStockLocationLine.setRack(rack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a stock move from an inventory.
|
||||
*
|
||||
* @param inventory a realized inventory.
|
||||
* @param isEnteringStock whether we want to create incoming or upcoming stock move of this
|
||||
* inventory.
|
||||
* @return the generated stock move.
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public StockMove generateStockMove(Inventory inventory, boolean isEnteringStock)
|
||||
throws AxelorException {
|
||||
|
||||
StockLocation toStockLocation;
|
||||
StockLocation fromStockLocation;
|
||||
Company company = inventory.getCompany();
|
||||
if (isEnteringStock) {
|
||||
toStockLocation = inventory.getStockLocation();
|
||||
fromStockLocation =
|
||||
stockConfigService.getInventoryVirtualStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
} else {
|
||||
toStockLocation =
|
||||
stockConfigService.getInventoryVirtualStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
fromStockLocation = inventory.getStockLocation();
|
||||
}
|
||||
|
||||
String inventorySeq = inventory.getInventorySeq();
|
||||
|
||||
LocalDate inventoryDate = inventory.getPlannedStartDateT().toLocalDate();
|
||||
LocalDate realDate = inventory.getValidatedOn();
|
||||
StockMove stockMove =
|
||||
stockMoveService.createStockMove(
|
||||
null,
|
||||
null,
|
||||
company,
|
||||
fromStockLocation,
|
||||
toStockLocation,
|
||||
realDate,
|
||||
inventoryDate,
|
||||
null,
|
||||
StockMoveRepository.TYPE_INTERNAL);
|
||||
|
||||
stockMove.setName(inventorySeq);
|
||||
|
||||
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_INVENTORY);
|
||||
stockMove.setOriginId(inventory.getId());
|
||||
stockMove.setOrigin(inventorySeq);
|
||||
|
||||
for (InventoryLine inventoryLine : inventory.getInventoryLineList()) {
|
||||
generateStockMoveLines(inventoryLine, stockMove, isEnteringStock);
|
||||
}
|
||||
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
|
||||
|
||||
stockMoveService.plan(stockMove);
|
||||
stockMoveService.copyQtyToRealQty(stockMove);
|
||||
// stockMoveService.realize(stockMove, false);
|
||||
}
|
||||
return stockMove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate lines for the given stock move. Depending if we are creating an incoming or outgoing
|
||||
* stock move, we only create stock move line with positive quantity.
|
||||
*
|
||||
* @param inventoryLine an inventory line
|
||||
* @param stockMove a stock move being created
|
||||
* @param isEnteringStock whether we are creating an incoming or outgoing stock move.
|
||||
* @throws AxelorException
|
||||
*/
|
||||
protected void generateStockMoveLines(
|
||||
InventoryLine inventoryLine, StockMove stockMove, boolean isEnteringStock)
|
||||
throws AxelorException {
|
||||
Product product = inventoryLine.getProduct();
|
||||
TrackingNumber trackingNumber = inventoryLine.getTrackingNumber();
|
||||
BigDecimal diff = inventoryLine.getRealQty().subtract(inventoryLine.getCurrentQty());
|
||||
if (!isEnteringStock) {
|
||||
diff = diff.negate();
|
||||
}
|
||||
if (diff.signum() > 0) {
|
||||
BigDecimal avgPrice;
|
||||
StockLocationLine stockLocationLine =
|
||||
stockLocationLineService.getStockLocationLine(stockMove.getToStockLocation(), product);
|
||||
if (stockLocationLine != null) {
|
||||
avgPrice = stockLocationLine.getAvgPrice();
|
||||
} else {
|
||||
avgPrice = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
StockMoveLine stockMoveLine =
|
||||
stockMoveLineService.createStockMoveLine(
|
||||
product,
|
||||
product.getName(),
|
||||
product.getDescription(),
|
||||
diff,
|
||||
avgPrice,
|
||||
avgPrice,
|
||||
product.getUnit(),
|
||||
stockMove,
|
||||
StockMoveLineService.TYPE_NULL,
|
||||
false,
|
||||
BigDecimal.ZERO);
|
||||
if (stockMoveLine == null) {
|
||||
throw new AxelorException(
|
||||
inventoryLine.getInventory(),
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_7)
|
||||
+ " "
|
||||
+ inventoryLine.getInventory().getInventorySeq());
|
||||
}
|
||||
if (trackingNumber != null && stockMoveLine.getTrackingNumber() == null) {
|
||||
stockMoveLine.setTrackingNumber(trackingNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void cancel(Inventory inventory) throws AxelorException {
|
||||
List<StockMove> stockMoveList =
|
||||
stockMoveRepo
|
||||
.all()
|
||||
.filter("self.originTypeSelect = :originTypeSelect AND self.originId = :originId")
|
||||
.bind("originTypeSelect", StockMoveRepository.ORIGIN_INVENTORY)
|
||||
.bind("originId", inventory.getId())
|
||||
.fetch();
|
||||
|
||||
for (StockMove stockMove : stockMoveList) {
|
||||
stockMoveService.cancel(stockMove);
|
||||
}
|
||||
|
||||
inventory.setStatusSelect(InventoryRepository.STATUS_CANCELED);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Boolean fillInventoryLineList(Inventory inventory) throws AxelorException {
|
||||
|
||||
if (inventory.getStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
inventory,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.INVENTORY_1));
|
||||
}
|
||||
|
||||
this.initInventoryLines(inventory);
|
||||
|
||||
List<? extends StockLocationLine> stockLocationLineList = this.getStockLocationLines(inventory);
|
||||
|
||||
if (stockLocationLineList != null) {
|
||||
Boolean succeed = false;
|
||||
for (StockLocationLine stockLocationLine : stockLocationLineList) {
|
||||
if (stockLocationLine.getTrackingNumber()
|
||||
== null) { // if no tracking number on stockLocationLine, check if there is a tracking
|
||||
// number on the product
|
||||
long numberOfTrackingNumberOnAProduct =
|
||||
stockLocationLineRepository
|
||||
.all()
|
||||
.filter(
|
||||
"self.product = ?1 AND self.trackingNumber IS NOT null AND self.detailsStockLocation = ?2",
|
||||
stockLocationLine.getProduct(),
|
||||
inventory.getStockLocation())
|
||||
.count();
|
||||
|
||||
if (numberOfTrackingNumberOnAProduct != 0) { // there is a tracking number on the product
|
||||
continue;
|
||||
}
|
||||
}
|
||||
inventory.addInventoryLineListItem(this.createInventoryLine(inventory, stockLocationLine));
|
||||
succeed = true;
|
||||
}
|
||||
inventoryRepo.save(inventory);
|
||||
return succeed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<? extends StockLocationLine> getStockLocationLines(Inventory inventory) {
|
||||
|
||||
String query = "(self.stockLocation = ? OR self.detailsStockLocation = ?)";
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
params.add(inventory.getStockLocation());
|
||||
params.add(inventory.getStockLocation());
|
||||
|
||||
if (inventory.getExcludeOutOfStock()) {
|
||||
query += " and self.currentQty > 0";
|
||||
}
|
||||
|
||||
if (!inventory.getIncludeObsolete()) {
|
||||
query += " and (self.product.endDate > ? or self.product.endDate is null)";
|
||||
params.add(inventory.getPlannedEndDateT().toLocalDate());
|
||||
}
|
||||
|
||||
if (inventory.getProductFamily() != null) {
|
||||
query += " and self.product.productFamily = ?";
|
||||
params.add(inventory.getProductFamily());
|
||||
}
|
||||
|
||||
if (inventory.getProductCategory() != null) {
|
||||
query += " and self.product.productCategory = ?";
|
||||
params.add(inventory.getProductCategory());
|
||||
}
|
||||
|
||||
if (inventory.getProduct() != null) {
|
||||
query += " and self.product = ?";
|
||||
params.add(inventory.getProduct());
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(inventory.getFromRack())) {
|
||||
query += " and self.rack >= ?";
|
||||
params.add(inventory.getFromRack());
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(inventory.getToRack())) {
|
||||
query += " and self.rack <= ?";
|
||||
params.add(inventory.getToRack());
|
||||
}
|
||||
|
||||
return stockLocationLineRepository.all().filter(query, params.toArray()).fetch();
|
||||
}
|
||||
|
||||
public InventoryLine createInventoryLine(
|
||||
Inventory inventory, StockLocationLine stockLocationLine) {
|
||||
|
||||
return inventoryLineService.createInventoryLine(
|
||||
inventory,
|
||||
stockLocationLine.getProduct(),
|
||||
stockLocationLine.getCurrentQty(),
|
||||
stockLocationLine.getRack(),
|
||||
stockLocationLine.getTrackingNumber());
|
||||
}
|
||||
|
||||
public void initInventoryLines(Inventory inventory) {
|
||||
|
||||
if (inventory.getInventoryLineList() == null) {
|
||||
inventory.setInventoryLineList(new ArrayList<InventoryLine>());
|
||||
} else {
|
||||
inventory.getInventoryLineList().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public MetaFile exportInventoryAsCSV(Inventory inventory) throws IOException {
|
||||
|
||||
List<String[]> list = new ArrayList<>();
|
||||
|
||||
for (InventoryLine inventoryLine : inventory.getInventoryLineList()) {
|
||||
String[] item = new String[9];
|
||||
String realQty = "";
|
||||
|
||||
item[0] = (inventoryLine.getProduct() == null) ? "" : inventoryLine.getProduct().getName();
|
||||
item[1] = (inventoryLine.getProduct() == null) ? "" : inventoryLine.getProduct().getCode();
|
||||
item[2] =
|
||||
(inventoryLine.getProduct() == null)
|
||||
? ""
|
||||
: ((inventoryLine.getProduct().getProductCategory() == null)
|
||||
? ""
|
||||
: inventoryLine.getProduct().getProductCategory().getName());
|
||||
item[3] = (inventoryLine.getRack() == null) ? "" : inventoryLine.getRack();
|
||||
item[4] =
|
||||
(inventoryLine.getTrackingNumber() == null)
|
||||
? ""
|
||||
: inventoryLine.getTrackingNumber().getTrackingNumberSeq();
|
||||
item[5] = inventoryLine.getCurrentQty().toString();
|
||||
if (inventoryLine.getRealQty() != null
|
||||
&& inventory.getStatusSelect() != InventoryRepository.STATUS_DRAFT
|
||||
&& inventory.getStatusSelect() != InventoryRepository.STATUS_PLANNED) {
|
||||
realQty = inventoryLine.getRealQty().toString();
|
||||
}
|
||||
item[6] = realQty;
|
||||
item[7] = (inventoryLine.getDescription() == null) ? "" : inventoryLine.getDescription();
|
||||
|
||||
String lastInventoryDateTString = "";
|
||||
StockLocationLine stockLocationLine =
|
||||
stockLocationLineService.getStockLocationLine(
|
||||
inventory.getStockLocation(), inventoryLine.getProduct());
|
||||
if (stockLocationLine != null) {
|
||||
ZonedDateTime lastInventoryDateT = stockLocationLine.getLastInventoryDateT();
|
||||
lastInventoryDateTString =
|
||||
lastInventoryDateT == null
|
||||
? ""
|
||||
: lastInventoryDateT.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
||||
}
|
||||
item[8] = lastInventoryDateTString;
|
||||
list.add(item);
|
||||
}
|
||||
|
||||
Collections.sort(
|
||||
list,
|
||||
new Comparator<String[]>() { // sort the list by code product
|
||||
@Override
|
||||
public int compare(String[] strings, String[] otherStrings) {
|
||||
return strings[1].compareTo(otherStrings[1]);
|
||||
}
|
||||
});
|
||||
|
||||
String fileName = I18n.get("Inventory") + "_" + inventory.getInventorySeq() + ".csv";
|
||||
String filePath = AppSettings.get().get("file.upload.dir");
|
||||
Path path = Paths.get(filePath, fileName);
|
||||
File file = path.toFile();
|
||||
|
||||
log.debug("File Located at: {}", path);
|
||||
|
||||
String[] headers = {
|
||||
I18n.get("Product Name"),
|
||||
I18n.get("Product Code"),
|
||||
I18n.get("Product category"),
|
||||
I18n.get("Rack"),
|
||||
I18n.get("Tracking Number"),
|
||||
I18n.get("Current Quantity"),
|
||||
I18n.get("Real Quantity"),
|
||||
I18n.get("Description"),
|
||||
I18n.get("Last Inventory date")
|
||||
};
|
||||
CsvTool.csvWriter(filePath, fileName, ';', '"', headers, list);
|
||||
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
return Beans.get(MetaFiles.class).upload(is, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
public List<StockMove> findStockMoves(Inventory inventory) {
|
||||
return stockMoveRepo
|
||||
.all()
|
||||
.filter(
|
||||
"self.originTypeSelect = ?1 AND self.originId = ?2",
|
||||
StockMoveRepository.ORIGIN_INVENTORY,
|
||||
inventory.getId())
|
||||
.fetch();
|
||||
}
|
||||
|
||||
public String computeTitle(Inventory entity) {
|
||||
return entity.getStockLocation().getName()
|
||||
+ (!Strings.isNullOrEmpty(entity.getDescription())
|
||||
? "-" + StringUtils.abbreviate(entity.getDescription(), 10)
|
||||
: "");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalFormLine;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormError;
|
||||
import com.axelor.script.ScriptHelper;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface LogisticalFormLineService {
|
||||
|
||||
/**
|
||||
* Get domain for stockMoveLine.
|
||||
*
|
||||
* @param logisticalFormLine
|
||||
* @return
|
||||
*/
|
||||
String getStockMoveLineDomain(LogisticalFormLine logisticalFormLine);
|
||||
|
||||
/**
|
||||
* Get unspread quantity.
|
||||
*
|
||||
* @param logisticalFormLine
|
||||
* @return
|
||||
*/
|
||||
BigDecimal getUnspreadQty(LogisticalFormLine logisticalFormLine);
|
||||
|
||||
/**
|
||||
* Validate dimensions
|
||||
*
|
||||
* @param logisticalFormLine
|
||||
* @throws LogisticalFormError
|
||||
*/
|
||||
void validateDimensions(LogisticalFormLine logisticalFormLine) throws LogisticalFormError;
|
||||
|
||||
/**
|
||||
* Evaluate volume.
|
||||
*
|
||||
* @param logisticalFormLine
|
||||
* @param scriptHelper
|
||||
* @return
|
||||
* @throws LogisticalFormError
|
||||
*/
|
||||
BigDecimal evalVolume(LogisticalFormLine logisticalFormLine, ScriptHelper scriptHelper)
|
||||
throws LogisticalFormError;
|
||||
|
||||
/**
|
||||
* Initialize parcel/pallet line.
|
||||
*
|
||||
* @param logisticalFormLine
|
||||
*/
|
||||
void initParcelPallet(LogisticalFormLine logisticalFormLine);
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.LogisticalFormLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormError;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.script.ScriptHelper;
|
||||
import com.google.common.base.Strings;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LogisticalFormLineServiceImpl implements LogisticalFormLineService {
|
||||
|
||||
private static final Pattern DIMENSIONS_PATTERN =
|
||||
Pattern.compile(
|
||||
"\\s*\\d+(\\.\\d*)?\\s*[x\\*]\\s*\\d+(\\.\\d*)?\\s*[x\\*]\\s*\\d+(\\.\\d*)?\\s*");
|
||||
|
||||
@Override
|
||||
public BigDecimal getUnspreadQty(LogisticalFormLine logisticalFormLine) {
|
||||
StockMoveLine stockMoveLine = logisticalFormLine.getStockMoveLine();
|
||||
return Beans.get(StockMoveLineService.class)
|
||||
.computeSpreadableQtyOverLogisticalFormLines(
|
||||
stockMoveLine, logisticalFormLine.getLogisticalForm());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStockMoveLineDomain(LogisticalFormLine logisticalFormLine) {
|
||||
long partnerId = 0;
|
||||
List<String> domainList = new ArrayList<>();
|
||||
LogisticalForm logisticalForm = logisticalFormLine.getLogisticalForm();
|
||||
|
||||
if (logisticalForm != null) {
|
||||
Partner deliverToCustomerPartner = logisticalForm.getDeliverToCustomerPartner();
|
||||
|
||||
if (deliverToCustomerPartner != null) {
|
||||
partnerId = deliverToCustomerPartner.getId();
|
||||
}
|
||||
}
|
||||
|
||||
domainList.add(String.format("self.stockMove.partner.id = %d", partnerId));
|
||||
domainList.add(
|
||||
String.format("self.stockMove.typeSelect = %d", StockMoveRepository.TYPE_OUTGOING));
|
||||
domainList.add(
|
||||
String.format(
|
||||
"self.stockMove.statusSelect in (%d, %d)",
|
||||
StockMoveRepository.STATUS_PLANNED, StockMoveRepository.STATUS_REALIZED));
|
||||
domainList.add("self.realQty > 0");
|
||||
domainList.add("COALESCE(self.stockMove.fullySpreadOverLogisticalFormsFlag, FALSE) = FALSE");
|
||||
|
||||
if (logisticalForm.getStockLocation() != null) {
|
||||
domainList.add("self.stockMove.fromStockLocation = :stockLocation");
|
||||
}
|
||||
|
||||
List<StockMoveLine> fullySpreadStockMoveLineList =
|
||||
Beans.get(LogisticalFormService.class).getFullySpreadStockMoveLineList(logisticalForm);
|
||||
|
||||
if (!fullySpreadStockMoveLineList.isEmpty()) {
|
||||
String idListString = StringTool.getIdListString(fullySpreadStockMoveLineList);
|
||||
domainList.add(String.format("self.id NOT IN (%s)", idListString));
|
||||
}
|
||||
|
||||
return domainList
|
||||
.stream()
|
||||
.map(domain -> String.format("(%s)", domain))
|
||||
.collect(Collectors.joining(" AND "));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDimensions(LogisticalFormLine logisticalFormLine) throws LogisticalFormError {
|
||||
String dimensions = logisticalFormLine.getDimensions();
|
||||
if (!Strings.isNullOrEmpty(dimensions) && !DIMENSIONS_PATTERN.matcher(dimensions).matches()) {
|
||||
throw new LogisticalFormError(
|
||||
logisticalFormLine,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINE_INVALID_DIMENSIONS),
|
||||
logisticalFormLine.getSequence() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal evalVolume(LogisticalFormLine logisticalFormLine, ScriptHelper scriptHelper)
|
||||
throws LogisticalFormError {
|
||||
validateDimensions(logisticalFormLine);
|
||||
String script = logisticalFormLine.getDimensions();
|
||||
|
||||
if (Strings.isNullOrEmpty(script)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
return (BigDecimal)
|
||||
scriptHelper.eval(String.format("new BigDecimal(%s)", script.replaceAll("x", "*")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initParcelPallet(LogisticalFormLine logisticalFormLine) {
|
||||
LogisticalFormService logisticalFormService = Beans.get(LogisticalFormService.class);
|
||||
logisticalFormLine.setParcelPalletNumber(
|
||||
logisticalFormService.getNextParcelPalletNumber(
|
||||
logisticalFormLine.getLogisticalForm(), logisticalFormLine.getTypeSelect()));
|
||||
logisticalFormLine.setSequence(
|
||||
logisticalFormService.getNextLineSequence(logisticalFormLine.getLogisticalForm()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormError;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormWarning;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.meta.CallMethod;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/** @author axelor */
|
||||
public interface LogisticalFormService {
|
||||
|
||||
/**
|
||||
* Add detail lines from the stock move. If there were no lines, add a parcel line first.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @param stockMove
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void addDetailLines(LogisticalForm logisticalForm, StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Add parcel or pallet line.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @param typeSelect
|
||||
*/
|
||||
void addParcelPalletLine(LogisticalForm logisticalForm, int typeSelect);
|
||||
|
||||
/**
|
||||
* Compute totals.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @throws LogisticalFormError
|
||||
*/
|
||||
void computeTotals(LogisticalForm logisticalForm) throws LogisticalFormError;
|
||||
|
||||
/**
|
||||
* Check lines.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @throws LogisticalFormWarning
|
||||
* @throws LogisticalFormError
|
||||
*/
|
||||
void checkLines(LogisticalForm logisticalForm) throws LogisticalFormWarning, LogisticalFormError;
|
||||
|
||||
/**
|
||||
* Get list of full spread stock move lines.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
*/
|
||||
List<StockMoveLine> getFullySpreadStockMoveLineList(LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Get map of spreadable quantity for each stock move line.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
*/
|
||||
Map<StockMoveLine, BigDecimal> getSpreadableQtyMap(LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Get map of spread quantity for each stock move line.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
*/
|
||||
Map<StockMoveLine, BigDecimal> getSpreadQtyMap(LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Get domain for stock move.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
String getStockMoveDomain(LogisticalForm logisticalForm) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Get next parcel/pallet number.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @param typeSelect
|
||||
* @return
|
||||
*/
|
||||
int getNextParcelPalletNumber(LogisticalForm logisticalForm, int typeSelect);
|
||||
|
||||
/**
|
||||
* Get next line sequence.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
*/
|
||||
int getNextLineSequence(LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Sort lines by sequence.
|
||||
*
|
||||
* @param logisticalForm
|
||||
*/
|
||||
void sortLines(LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Get the list of logistical form IDs for the given stock move.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
@CallMethod
|
||||
List<Long> getIdList(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Process collected parcels/pallets.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void processCollected(LogisticalForm logisticalForm) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Get customer account number to carrier.
|
||||
*
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
Optional<String> getCustomerAccountNumberToCarrier(LogisticalForm logisticalForm)
|
||||
throws AxelorException;
|
||||
|
||||
void updateProductNetMass(LogisticalForm logisticalForm) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,690 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.user.UserService;
|
||||
import com.axelor.apps.stock.db.FreightCarrierCustomerAccountNumber;
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.LogisticalFormLine;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.repo.LogisticalFormLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.LogisticalFormRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormError;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormWarning;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
import com.axelor.apps.tool.QueryBuilder;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
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.rpc.Context;
|
||||
import com.axelor.rpc.ContextEntity;
|
||||
import com.axelor.script.GroovyScriptHelper;
|
||||
import com.axelor.script.ScriptHelper;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.TypedQuery;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public class LogisticalFormServiceImpl implements LogisticalFormService {
|
||||
|
||||
@Inject ProductRepository productRepository;
|
||||
|
||||
@Override
|
||||
public void addDetailLines(LogisticalForm logisticalForm, StockMove stockMove)
|
||||
throws AxelorException {
|
||||
Objects.requireNonNull(logisticalForm);
|
||||
Objects.requireNonNull(stockMove);
|
||||
|
||||
if (logisticalForm.getDeliverToCustomerPartner() != null
|
||||
&& !logisticalForm.getDeliverToCustomerPartner().equals(stockMove.getPartner())) {
|
||||
throw new AxelorException(
|
||||
logisticalForm,
|
||||
TraceBackRepository.CATEGORY_INCONSISTENCY,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_PARTNER_MISMATCH),
|
||||
logisticalForm.getDeliverToCustomerPartner().getName());
|
||||
}
|
||||
|
||||
if (stockMove.getStockMoveLineList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StockMoveLineService stockMoveLineService = Beans.get(StockMoveLineService.class);
|
||||
List<Pair<StockMoveLine, BigDecimal>> toAddList = new ArrayList<>();
|
||||
|
||||
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
|
||||
BigDecimal spreadableQty =
|
||||
stockMoveLineService.computeSpreadableQtyOverLogisticalFormLines(
|
||||
stockMoveLine, logisticalForm);
|
||||
|
||||
if (spreadableQty.signum() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testForDetailLine(stockMoveLine)) {
|
||||
toAddList.add(Pair.of(stockMoveLine, spreadableQty));
|
||||
}
|
||||
}
|
||||
|
||||
if (!toAddList.isEmpty()) {
|
||||
if (logisticalForm.getLogisticalFormLineList() == null
|
||||
|| logisticalForm.getLogisticalFormLineList().isEmpty()) {
|
||||
addParcelPalletLine(logisticalForm, LogisticalFormLineRepository.TYPE_PARCEL);
|
||||
}
|
||||
|
||||
toAddList.forEach(item -> addDetailLine(logisticalForm, item.getLeft(), item.getRight()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for detail line (to be overridden).
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
protected boolean testForDetailLine(StockMoveLine stockMoveLine) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkLines(LogisticalForm logisticalForm)
|
||||
throws LogisticalFormWarning, LogisticalFormError {
|
||||
List<String> warningMessageList = new ArrayList<>();
|
||||
|
||||
checkRequiredLineFields(logisticalForm);
|
||||
checkInvalidLineDimensions(logisticalForm);
|
||||
checkEmptyParcelPalletLines(logisticalForm, warningMessageList);
|
||||
checkInconsistentQties(logisticalForm, warningMessageList);
|
||||
|
||||
if (!warningMessageList.isEmpty()) {
|
||||
String errorMessage =
|
||||
String.format(
|
||||
"<ul>%s</ul>",
|
||||
warningMessageList
|
||||
.stream()
|
||||
.map(message -> String.format("<li>%s</li>", message))
|
||||
.collect(Collectors.joining("\n")));
|
||||
throw new LogisticalFormWarning(logisticalForm, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkRequiredLineFields(LogisticalForm logisticalForm) throws LogisticalFormError {
|
||||
if (logisticalForm.getLogisticalFormLineList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
if (logisticalFormLine.getTypeSelect() == 0) {
|
||||
throw new LogisticalFormError(
|
||||
logisticalFormLine,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINE_REQUIRED_TYPE),
|
||||
logisticalFormLine.getSequence() + 1);
|
||||
}
|
||||
|
||||
if (logisticalFormLine.getTypeSelect() == LogisticalFormLineRepository.TYPE_DETAIL) {
|
||||
if (logisticalFormLine.getStockMoveLine() == null) {
|
||||
throw new LogisticalFormError(
|
||||
logisticalFormLine,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINE_REQUIRED_STOCK_MOVE_LINE),
|
||||
logisticalFormLine.getSequence() + 1);
|
||||
}
|
||||
if (logisticalFormLine.getQty() == null || logisticalFormLine.getQty().signum() <= 0) {
|
||||
throw new LogisticalFormError(
|
||||
logisticalFormLine,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINE_REQUIRED_QUANTITY),
|
||||
logisticalFormLine.getSequence() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkInconsistentQties(
|
||||
LogisticalForm logisticalForm, List<String> errorMessageList) {
|
||||
Map<StockMoveLine, BigDecimal> spreadableQtyMap = getSpreadableQtyMap(logisticalForm);
|
||||
Map<StockMoveLine, BigDecimal> spreadQtyMap = getSpreadQtyMap(logisticalForm);
|
||||
Locale locale = new Locale(Beans.get(UserService.class).getLanguage());
|
||||
NumberFormat nf = NumberFormat.getInstance(locale);
|
||||
|
||||
for (Entry<StockMoveLine, BigDecimal> entry : spreadableQtyMap.entrySet()) {
|
||||
StockMoveLine stockMoveLine = entry.getKey();
|
||||
BigDecimal spreadableQty = entry.getValue();
|
||||
|
||||
if (spreadableQty.signum() != 0) {
|
||||
BigDecimal spreadQty = spreadQtyMap.getOrDefault(stockMoveLine, BigDecimal.ZERO);
|
||||
BigDecimal expectedQty = spreadQty.add(spreadableQty);
|
||||
String errorMessage =
|
||||
String.format(
|
||||
locale,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINES_INCONSISTENT_QUANTITY),
|
||||
String.format(
|
||||
"%s (%s)",
|
||||
stockMoveLine.getProductName(), stockMoveLine.getStockMove().getStockMoveSeq()),
|
||||
nf.format(spreadQty),
|
||||
nf.format(expectedQty));
|
||||
errorMessageList.add(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkEmptyParcelPalletLines(
|
||||
LogisticalForm logisticalForm, List<String> errorMessageList) throws LogisticalFormError {
|
||||
if (logisticalForm.getLogisticalFormLineList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<LogisticalFormLine, BigDecimal> qtyMap = new HashMap<>();
|
||||
LogisticalFormLine currentLine = null;
|
||||
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
if (logisticalFormLine.getTypeSelect() != LogisticalFormLineRepository.TYPE_DETAIL) {
|
||||
currentLine = logisticalFormLine;
|
||||
qtyMap.put(currentLine, BigDecimal.ZERO);
|
||||
} else {
|
||||
if (currentLine == null) {
|
||||
throw new LogisticalFormError(
|
||||
logisticalForm, I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINES_ORPHAN_DETAIL));
|
||||
}
|
||||
qtyMap.merge(currentLine, logisticalFormLine.getQty(), BigDecimal::add);
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<LogisticalFormLine, BigDecimal> entry : qtyMap.entrySet()) {
|
||||
LogisticalFormLine logisticalFormLine = entry.getKey();
|
||||
|
||||
BigDecimal qty = entry.getValue();
|
||||
|
||||
if (qty.signum() <= 0) {
|
||||
String msg;
|
||||
|
||||
if (logisticalFormLine.getTypeSelect() == LogisticalFormLineRepository.TYPE_PARCEL) {
|
||||
msg = I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINES_EMPTY_PARCEL);
|
||||
} else {
|
||||
msg = I18n.get(IExceptionMessage.LOGISTICAL_FORM_LINES_EMPTY_PALLET);
|
||||
}
|
||||
|
||||
String errorMessage = String.format(msg, logisticalFormLine.getParcelPalletNumber());
|
||||
errorMessageList.add(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkInvalidLineDimensions(LogisticalForm logisticalForm)
|
||||
throws LogisticalFormError {
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
LogisticalFormLineService logisticalFormLineService =
|
||||
Beans.get(LogisticalFormLineService.class);
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
logisticalFormLineService.validateDimensions(logisticalFormLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockMoveLine> getFullySpreadStockMoveLineList(LogisticalForm logisticalForm) {
|
||||
List<StockMoveLine> stockMoveLineList = new ArrayList<>();
|
||||
Map<StockMoveLine, BigDecimal> spreadableQtyMap = new HashMap<>();
|
||||
|
||||
for (LogisticalForm item : findPendingLogisticalForms(logisticalForm)) {
|
||||
spreadableQtyMap.putAll(getSpreadableQtyMap(item));
|
||||
}
|
||||
|
||||
for (Entry<StockMoveLine, BigDecimal> entry : spreadableQtyMap.entrySet()) {
|
||||
StockMoveLine stockMoveLine = entry.getKey();
|
||||
BigDecimal spreadableQty = entry.getValue();
|
||||
|
||||
if (spreadableQty.signum() <= 0) {
|
||||
stockMoveLineList.add(stockMoveLine);
|
||||
}
|
||||
}
|
||||
|
||||
return stockMoveLineList;
|
||||
}
|
||||
|
||||
private List<LogisticalForm> findPendingLogisticalForms(LogisticalForm logisticalForm) {
|
||||
Preconditions.checkNotNull(logisticalForm);
|
||||
Preconditions.checkNotNull(logisticalForm.getDeliverToCustomerPartner());
|
||||
|
||||
QueryBuilder<LogisticalForm> queryBuilder = QueryBuilder.of(LogisticalForm.class);
|
||||
queryBuilder.add("self.deliverToCustomerPartner = :deliverToCustomerPartner");
|
||||
queryBuilder.bind("deliverToCustomerPartner", logisticalForm.getDeliverToCustomerPartner());
|
||||
queryBuilder.add("self.statusSelect < :statusSelect");
|
||||
queryBuilder.bind("statusSelect", LogisticalFormRepository.STATUS_COLLECTED);
|
||||
|
||||
if (logisticalForm.getId() != null) {
|
||||
queryBuilder.add("self.id != :id");
|
||||
queryBuilder.bind("id", logisticalForm.getId());
|
||||
}
|
||||
|
||||
List<LogisticalForm> logisticalFormList = queryBuilder.build().fetch();
|
||||
logisticalFormList.add(logisticalForm);
|
||||
|
||||
return logisticalFormList;
|
||||
}
|
||||
|
||||
protected List<StockMove> getFullySpreadStockMoveList(LogisticalForm logisticalForm) {
|
||||
List<StockMove> fullySpreadStockMoveList = new ArrayList<>();
|
||||
List<StockMoveLine> fullySpreadStockMoveLineList =
|
||||
getFullySpreadStockMoveLineList(logisticalForm);
|
||||
|
||||
Set<StockMove> stockMoveSet = new HashSet<>();
|
||||
|
||||
for (StockMoveLine stockMoveLine : fullySpreadStockMoveLineList) {
|
||||
stockMoveSet.add(stockMoveLine.getStockMove());
|
||||
}
|
||||
|
||||
for (StockMove stockMove : stockMoveSet) {
|
||||
if (fullySpreadStockMoveLineList.containsAll(stockMove.getStockMoveLineList())) {
|
||||
fullySpreadStockMoveList.add(stockMove);
|
||||
}
|
||||
}
|
||||
|
||||
return fullySpreadStockMoveList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<StockMoveLine, BigDecimal> getSpreadableQtyMap(LogisticalForm logisticalForm) {
|
||||
Set<StockMove> stockMoveSet = new LinkedHashSet<>();
|
||||
Map<StockMoveLine, BigDecimal> spreadableQtyMap = new LinkedHashMap<>();
|
||||
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
StockMoveLineService stockMoveLineService = Beans.get(StockMoveLineService.class);
|
||||
|
||||
logisticalForm
|
||||
.getLogisticalFormLineList()
|
||||
.stream()
|
||||
.filter(
|
||||
logisticalFormLine ->
|
||||
logisticalFormLine.getTypeSelect() == LogisticalFormLineRepository.TYPE_DETAIL
|
||||
&& logisticalFormLine.getStockMoveLine() != null
|
||||
&& logisticalFormLine.getStockMoveLine().getStockMove() != null)
|
||||
.forEach(
|
||||
logisticalFormLine ->
|
||||
stockMoveSet.add(logisticalFormLine.getStockMoveLine().getStockMove()));
|
||||
|
||||
for (StockMove stockMove : stockMoveSet) {
|
||||
if (stockMove.getStockMoveLineList() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
|
||||
BigDecimal spreadableQty =
|
||||
stockMoveLineService.computeSpreadableQtyOverLogisticalFormLines(
|
||||
stockMoveLine, logisticalForm);
|
||||
spreadableQtyMap.put(stockMoveLine, spreadableQty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spreadableQtyMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<StockMoveLine, BigDecimal> getSpreadQtyMap(LogisticalForm logisticalForm) {
|
||||
Map<StockMoveLine, BigDecimal> spreadQtyMap = new LinkedHashMap<>();
|
||||
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
logisticalForm
|
||||
.getLogisticalFormLineList()
|
||||
.stream()
|
||||
.filter(
|
||||
logisticalFormLine ->
|
||||
logisticalFormLine.getTypeSelect() == LogisticalFormLineRepository.TYPE_DETAIL)
|
||||
.forEach(
|
||||
logisticalFormLine -> {
|
||||
StockMoveLine stockMoveLine = logisticalFormLine.getStockMoveLine();
|
||||
if (stockMoveLine != null && logisticalFormLine.getQty() != null) {
|
||||
spreadQtyMap.merge(stockMoveLine, logisticalFormLine.getQty(), BigDecimal::add);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return spreadQtyMap;
|
||||
}
|
||||
|
||||
protected void addDetailLine(
|
||||
LogisticalForm logisticalForm, StockMoveLine stockMoveLine, BigDecimal qty) {
|
||||
Preconditions.checkNotNull(logisticalForm);
|
||||
Preconditions.checkNotNull(stockMoveLine);
|
||||
|
||||
LogisticalFormLine logisticalFormLine =
|
||||
createLogisticalFormLine(logisticalForm, stockMoveLine, qty);
|
||||
addLogisticalFormLineListItem(logisticalForm, logisticalFormLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addParcelPalletLine(LogisticalForm logisticalForm, int typeSelect) {
|
||||
LogisticalFormLine logisticalFormLine = new LogisticalFormLine();
|
||||
logisticalFormLine.setTypeSelect(typeSelect);
|
||||
logisticalFormLine.setParcelPalletNumber(getNextParcelPalletNumber(logisticalForm, typeSelect));
|
||||
logisticalFormLine.setSequence(getNextLineSequence(logisticalForm));
|
||||
addLogisticalFormLineListItem(logisticalForm, logisticalFormLine);
|
||||
}
|
||||
|
||||
// Workaround for #9759
|
||||
protected void addLogisticalFormLineListItem(
|
||||
LogisticalForm logisticalForm, LogisticalFormLine logisticalFormLine) {
|
||||
if (logisticalForm instanceof ContextEntity) {
|
||||
List<LogisticalFormLine> logisticalFormLineList = logisticalForm.getLogisticalFormLineList();
|
||||
if (logisticalFormLineList == null) {
|
||||
logisticalFormLineList = new ArrayList<>();
|
||||
logisticalForm.setLogisticalFormLineList(logisticalFormLineList);
|
||||
}
|
||||
logisticalFormLineList.add(logisticalFormLine);
|
||||
} else {
|
||||
logisticalForm.addLogisticalFormLineListItem(logisticalFormLine);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextParcelPalletNumber(LogisticalForm logisticalForm, int typeSelect) {
|
||||
int highest = 0;
|
||||
Set<Integer> parcelPalletNumberSet = new HashSet<>();
|
||||
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
if (logisticalFormLine.getTypeSelect() == typeSelect
|
||||
&& logisticalFormLine.getParcelPalletNumber() != null) {
|
||||
parcelPalletNumberSet.add(logisticalFormLine.getParcelPalletNumber());
|
||||
|
||||
if (logisticalFormLine.getParcelPalletNumber() > highest) {
|
||||
highest = logisticalFormLine.getParcelPalletNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < highest; ++i) {
|
||||
if (!parcelPalletNumberSet.contains(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return highest + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextLineSequence(LogisticalForm logisticalForm) {
|
||||
if (logisticalForm.getLogisticalFormLineList() == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OptionalInt max =
|
||||
logisticalForm
|
||||
.getLogisticalFormLineList()
|
||||
.stream()
|
||||
.mapToInt(LogisticalFormLine::getSequence)
|
||||
.max();
|
||||
|
||||
return max.isPresent() ? max.getAsInt() + 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeTotals(LogisticalForm logisticalForm) throws LogisticalFormError {
|
||||
BigDecimal totalNetMass = BigDecimal.ZERO;
|
||||
BigDecimal totalGrossMass = BigDecimal.ZERO;
|
||||
BigDecimal totalVolume = BigDecimal.ZERO;
|
||||
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
ScriptHelper scriptHelper = getScriptHelper(logisticalForm);
|
||||
LogisticalFormLineService logisticalFormLineService =
|
||||
Beans.get(LogisticalFormLineService.class);
|
||||
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
StockMoveLine stockMoveLine = logisticalFormLine.getStockMoveLine();
|
||||
|
||||
if (logisticalFormLine.getTypeSelect() != LogisticalFormLineRepository.TYPE_DETAIL) {
|
||||
if (logisticalFormLine.getGrossMass() != null) {
|
||||
totalGrossMass = totalGrossMass.add(logisticalFormLine.getGrossMass());
|
||||
}
|
||||
|
||||
totalVolume =
|
||||
totalVolume.add(
|
||||
logisticalFormLineService.evalVolume(logisticalFormLine, scriptHelper));
|
||||
} else if (stockMoveLine != null) {
|
||||
totalNetMass =
|
||||
totalNetMass.add(logisticalFormLine.getQty().multiply(stockMoveLine.getNetMass()));
|
||||
}
|
||||
}
|
||||
|
||||
totalVolume = totalVolume.divide(new BigDecimal(1_000_000), 10, RoundingMode.HALF_UP);
|
||||
logisticalForm.setTotalNetMass(totalNetMass.setScale(3, RoundingMode.HALF_EVEN));
|
||||
logisticalForm.setTotalGrossMass(totalGrossMass.setScale(3, RoundingMode.HALF_EVEN));
|
||||
logisticalForm.setTotalVolume(totalVolume.setScale(3, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
}
|
||||
|
||||
protected ScriptHelper getScriptHelper(LogisticalForm logisticalForm) {
|
||||
Context scriptContext = new Context(Mapper.toMap(logisticalForm), logisticalForm.getClass());
|
||||
return new GroovyScriptHelper(scriptContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStockMoveDomain(LogisticalForm logisticalForm) throws AxelorException {
|
||||
|
||||
if (logisticalForm.getDeliverToCustomerPartner() == null) {
|
||||
return "self IS NULL";
|
||||
}
|
||||
|
||||
List<String> domainList = new ArrayList<>();
|
||||
|
||||
domainList.add("self.partner = :deliverToCustomerPartner");
|
||||
domainList.add(String.format("self.typeSelect = %d", StockMoveRepository.TYPE_OUTGOING));
|
||||
domainList.add(
|
||||
String.format(
|
||||
"self.statusSelect in (%d, %d)",
|
||||
StockMoveRepository.STATUS_PLANNED, StockMoveRepository.STATUS_REALIZED));
|
||||
domainList.add("COALESCE(self.fullySpreadOverLogisticalFormsFlag, FALSE) = FALSE");
|
||||
if (logisticalForm.getStockLocation() != null) {
|
||||
domainList.add("self.fromStockLocation = :stockLocation");
|
||||
}
|
||||
List<StockMove> fullySpreadStockMoveList = getFullySpreadStockMoveList(logisticalForm);
|
||||
|
||||
if (!fullySpreadStockMoveList.isEmpty()) {
|
||||
String idListString = StringTool.getIdListString(fullySpreadStockMoveList);
|
||||
domainList.add(String.format("self.id NOT IN (%s)", idListString));
|
||||
}
|
||||
|
||||
return domainList
|
||||
.stream()
|
||||
.map(domain -> String.format("(%s)", domain))
|
||||
.collect(Collectors.joining(" AND "));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sortLines(LogisticalForm logisticalForm) {
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
logisticalForm
|
||||
.getLogisticalFormLineList()
|
||||
.sort(Comparator.comparing(LogisticalFormLine::getSequence));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getIdList(StockMove stockMove) throws AxelorException {
|
||||
if (stockMove.getId() == null) {
|
||||
throw new AxelorException(
|
||||
StockMove.class,
|
||||
TraceBackRepository.CATEGORY_NO_VALUE,
|
||||
I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.RECORD_UNSAVED));
|
||||
}
|
||||
|
||||
TypedQuery<LogisticalForm> query =
|
||||
JPA.em()
|
||||
.createQuery(
|
||||
"SELECT DISTINCT self FROM LogisticalForm self "
|
||||
+ "JOIN self.logisticalFormLineList logisticalFormLine "
|
||||
+ "WHERE logisticalFormLine.stockMoveLine.stockMove.id = :stockMoveId",
|
||||
LogisticalForm.class);
|
||||
query.setParameter("stockMoveId", stockMove.getId());
|
||||
List<LogisticalForm> resultList = query.getResultList();
|
||||
|
||||
return resultList.isEmpty()
|
||||
? Lists.newArrayList(0L)
|
||||
: resultList.stream().map(LogisticalForm::getId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void processCollected(LogisticalForm logisticalForm) throws AxelorException {
|
||||
if (logisticalForm.getLogisticalFormLineList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<StockMove> stockMoveSet = new HashSet<>();
|
||||
|
||||
logisticalForm
|
||||
.getLogisticalFormLineList()
|
||||
.stream()
|
||||
.filter(
|
||||
logisticalFormLine ->
|
||||
logisticalFormLine.getTypeSelect() == LogisticalFormLineRepository.TYPE_DETAIL
|
||||
&& logisticalFormLine.getStockMoveLine() != null
|
||||
&& logisticalFormLine.getStockMoveLine().getStockMove() != null)
|
||||
.forEach(
|
||||
logisticalFormLine ->
|
||||
stockMoveSet.add(logisticalFormLine.getStockMoveLine().getStockMove()));
|
||||
|
||||
StockMoveService stockMoveService = Beans.get(StockMoveService.class);
|
||||
|
||||
stockMoveSet.forEach(stockMoveService::updateFullySpreadOverLogisticalFormsFlag);
|
||||
|
||||
StockConfigService stockConfigService = Beans.get(StockConfigService.class);
|
||||
StockConfig stockConfig = stockConfigService.getStockConfig(logisticalForm.getCompany());
|
||||
|
||||
if (stockConfig.getRealizeStockMovesUponParcelPalletCollection()) {
|
||||
for (StockMove stockMove : stockMoveSet) {
|
||||
if (stockMove.getFullySpreadOverLogisticalFormsFlag()) {
|
||||
stockMoveService.realize(stockMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logisticalForm.setStatusSelect(LogisticalFormRepository.STATUS_COLLECTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getCustomerAccountNumberToCarrier(LogisticalForm logisticalForm)
|
||||
throws AxelorException {
|
||||
Preconditions.checkNotNull(logisticalForm);
|
||||
List<FreightCarrierCustomerAccountNumber> freightCarrierCustomerAccountNumberList = null;
|
||||
|
||||
switch (logisticalForm.getAccountSelectionToCarrierSelect()) {
|
||||
case LogisticalFormRepository.ACCOUNT_COMPANY:
|
||||
if (logisticalForm.getCompany() != null
|
||||
&& logisticalForm.getCompany().getStockConfig() != null) {
|
||||
freightCarrierCustomerAccountNumberList =
|
||||
logisticalForm
|
||||
.getCompany()
|
||||
.getStockConfig()
|
||||
.getFreightCarrierCustomerAccountNumberList();
|
||||
}
|
||||
break;
|
||||
case LogisticalFormRepository.ACCOUNT_CUSTOMER:
|
||||
if (logisticalForm.getDeliverToCustomerPartner() != null) {
|
||||
freightCarrierCustomerAccountNumberList =
|
||||
logisticalForm
|
||||
.getDeliverToCustomerPartner()
|
||||
.getFreightCarrierCustomerAccountNumberList();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new AxelorException(
|
||||
logisticalForm,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOGISTICAL_FORM_UNKNOWN_ACCOUNT_SELECTION));
|
||||
}
|
||||
|
||||
if (freightCarrierCustomerAccountNumberList != null) {
|
||||
Optional<FreightCarrierCustomerAccountNumber> freightCarrierCustomerAccountNumber =
|
||||
freightCarrierCustomerAccountNumberList
|
||||
.stream()
|
||||
.filter(it -> it.getCarrierPartner().equals(logisticalForm.getCarrierPartner()))
|
||||
.findFirst();
|
||||
|
||||
if (freightCarrierCustomerAccountNumber.isPresent()) {
|
||||
return Optional.ofNullable(
|
||||
freightCarrierCustomerAccountNumber.get().getCustomerAccountNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
protected LogisticalFormLine createLogisticalFormLine(
|
||||
LogisticalForm logisticalForm, StockMoveLine stockMoveLine, BigDecimal qty) {
|
||||
|
||||
LogisticalFormLine logisticalFormLine = new LogisticalFormLine();
|
||||
logisticalFormLine.setTypeSelect(LogisticalFormLineRepository.TYPE_DETAIL);
|
||||
logisticalFormLine.setStockMoveLine(stockMoveLine);
|
||||
logisticalFormLine.setQty(qty);
|
||||
logisticalFormLine.setSequence(getNextLineSequence(logisticalForm));
|
||||
logisticalFormLine.setUnitNetMass(stockMoveLine.getNetMass());
|
||||
|
||||
return logisticalFormLine;
|
||||
}
|
||||
|
||||
public void updateProductNetMass(LogisticalForm logisticalForm) {
|
||||
BigDecimal totalNetMass = BigDecimal.ZERO;
|
||||
if (logisticalForm.getLogisticalFormLineList() != null) {
|
||||
for (LogisticalFormLine logisticalFormLine : logisticalForm.getLogisticalFormLineList()) {
|
||||
if (logisticalFormLine.getStockMoveLine() != null
|
||||
&& logisticalFormLine.getStockMoveLine().getProduct() != null
|
||||
&& logisticalFormLine
|
||||
.getTypeSelect()
|
||||
.equals(LogisticalFormLineRepository.TYPE_DETAIL)) {
|
||||
Product product =
|
||||
productRepository.find(logisticalFormLine.getStockMoveLine().getProduct().getId());
|
||||
logisticalFormLine.setUnitNetMass(product.getNetMass());
|
||||
totalNetMass =
|
||||
totalNetMass.add(logisticalFormLine.getQty().multiply(product.getNetMass()));
|
||||
}
|
||||
}
|
||||
logisticalForm.setTotalNetMass(totalNetMass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.exception.AxelorException;
|
||||
|
||||
public interface PartnerProductQualityRatingService {
|
||||
|
||||
/**
|
||||
* Calculate quality rating.
|
||||
*
|
||||
* @param stockMove
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void calculate(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Undo calculation of the quality rating.
|
||||
*
|
||||
* @param stockMove
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void undoCalculation(StockMove stockMove) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.PartnerProductQualityRating;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.repo.PartnerProductQualityRatingRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PartnerProductQualityRatingServiceImpl implements PartnerProductQualityRatingService {
|
||||
|
||||
public static final BigDecimal MAX_QUALITY_RATING = new BigDecimal(5);
|
||||
|
||||
private PartnerProductQualityRatingRepository partnerProductQualityRatingRepo;
|
||||
|
||||
@Inject
|
||||
public PartnerProductQualityRatingServiceImpl(
|
||||
PartnerProductQualityRatingRepository partnerProductQualityRatingRepo) {
|
||||
this.partnerProductQualityRatingRepo = partnerProductQualityRatingRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void calculate(StockMove stockMove) throws AxelorException {
|
||||
Partner partner = stockMove.getPartner();
|
||||
|
||||
if (partner == null || !partner.getIsSupplier()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<StockMoveLine> stockMoveLines = stockMove.getStockMoveLineList();
|
||||
|
||||
if (stockMoveLines != null) {
|
||||
for (StockMoveLine stockMoveLine : stockMoveLines) {
|
||||
Product product = stockMoveLine.getProduct();
|
||||
PartnerProductQualityRating partnerProductQualityRating =
|
||||
searchPartnerProductQualityRating(partner, product)
|
||||
.orElseGet(() -> createPartnerProductQualityRating(partner, product));
|
||||
updatePartnerProductQualityRating(partnerProductQualityRating, stockMoveLine);
|
||||
}
|
||||
}
|
||||
|
||||
updateSupplier(partner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void undoCalculation(StockMove stockMove) throws AxelorException {
|
||||
Partner partner = stockMove.getPartner();
|
||||
|
||||
if (partner == null || !partner.getIsSupplier()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<StockMoveLine> stockMoveLines = stockMove.getStockMoveLineList();
|
||||
|
||||
if (stockMoveLines != null) {
|
||||
for (StockMoveLine stockMoveLine : stockMoveLines) {
|
||||
Product product = stockMoveLine.getProduct();
|
||||
Optional<PartnerProductQualityRating> optional =
|
||||
searchPartnerProductQualityRating(partner, product);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
PartnerProductQualityRating partnerProductQualityRating = optional.get();
|
||||
updatePartnerProductQualityRating(partnerProductQualityRating, stockMoveLine, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateSupplier(partner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for partner product quality rating.
|
||||
*
|
||||
* @param partner
|
||||
* @param product
|
||||
* @return
|
||||
*/
|
||||
private Optional<PartnerProductQualityRating> searchPartnerProductQualityRating(
|
||||
Partner partner, Product product) {
|
||||
List<PartnerProductQualityRating> partnerProductQualityRatingList =
|
||||
partner.getPartnerProductQualityRatingList();
|
||||
|
||||
if (partnerProductQualityRatingList == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<PartnerProductQualityRating> productQualityRating =
|
||||
partnerProductQualityRatingList
|
||||
.stream()
|
||||
.filter(
|
||||
PartnerProductQualityRating ->
|
||||
PartnerProductQualityRating.getProduct() != null
|
||||
&& PartnerProductQualityRating.getProduct().equals(product))
|
||||
.findFirst();
|
||||
|
||||
if (productQualityRating == null) {
|
||||
productQualityRating = Optional.empty();
|
||||
}
|
||||
|
||||
return productQualityRating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create partner product quality rating.
|
||||
*
|
||||
* @param partner
|
||||
* @param product
|
||||
* @return
|
||||
*/
|
||||
@Transactional
|
||||
protected PartnerProductQualityRating createPartnerProductQualityRating(
|
||||
Partner partner, Product product) {
|
||||
PartnerProductQualityRating partnerProductQualityRating =
|
||||
new PartnerProductQualityRating(product);
|
||||
partner.addPartnerProductQualityRatingListItem(partnerProductQualityRating);
|
||||
partnerProductQualityRatingRepo.persist(partnerProductQualityRating);
|
||||
|
||||
return partnerProductQualityRating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update partner product quality rating.
|
||||
*
|
||||
* @param partnerProductQualityRating
|
||||
* @param stockMoveLine
|
||||
*/
|
||||
private void updatePartnerProductQualityRating(
|
||||
PartnerProductQualityRating partnerProductQualityRating, StockMoveLine stockMoveLine) {
|
||||
updatePartnerProductQualityRating(partnerProductQualityRating, stockMoveLine, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update partner product quality rating.
|
||||
*
|
||||
* @param partnerProductQualityRating
|
||||
* @param stockMoveLine
|
||||
* @param undo
|
||||
*/
|
||||
private void updatePartnerProductQualityRating(
|
||||
PartnerProductQualityRating partnerProductQualityRating,
|
||||
StockMoveLine stockMoveLine,
|
||||
boolean undo) {
|
||||
|
||||
BigDecimal qty = !undo ? stockMoveLine.getRealQty() : stockMoveLine.getRealQty().negate();
|
||||
BigDecimal compliantArrivalProductQty =
|
||||
partnerProductQualityRating.getCompliantArrivalProductQty();
|
||||
|
||||
if (stockMoveLine.getConformitySelect() == StockMoveLineRepository.CONFORMITY_COMPLIANT) {
|
||||
compliantArrivalProductQty = compliantArrivalProductQty.add(qty);
|
||||
partnerProductQualityRating.setCompliantArrivalProductQty(compliantArrivalProductQty);
|
||||
}
|
||||
|
||||
BigDecimal arrivalProductQty = partnerProductQualityRating.getArrivalProductQty().add(qty);
|
||||
partnerProductQualityRating.setArrivalProductQty(arrivalProductQty);
|
||||
|
||||
if (arrivalProductQty.signum() > 0) {
|
||||
BigDecimal qualityRating =
|
||||
computeQualityRating(compliantArrivalProductQty, arrivalProductQty);
|
||||
partnerProductQualityRating.setQualityRating(qualityRating);
|
||||
partnerProductQualityRating.setQualityRatingSelect(computeQualityRatingSelect(qualityRating));
|
||||
} else {
|
||||
partnerProductQualityRating
|
||||
.getPartner()
|
||||
.removePartnerProductQualityRatingListItem(partnerProductQualityRating);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update supplier's quality rating and arrival product quantity.
|
||||
*
|
||||
* @param partner
|
||||
*/
|
||||
private void updateSupplier(Partner partner) {
|
||||
BigDecimal supplierQualityRating = BigDecimal.ZERO;
|
||||
BigDecimal supplierArrivalProductQty = BigDecimal.ZERO;
|
||||
List<PartnerProductQualityRating> partnerProductQualityRatingList =
|
||||
partner.getPartnerProductQualityRatingList();
|
||||
|
||||
if (partnerProductQualityRatingList != null) {
|
||||
for (PartnerProductQualityRating partnerProductQualityRating :
|
||||
partnerProductQualityRatingList) {
|
||||
BigDecimal qualityRating = partnerProductQualityRating.getQualityRating();
|
||||
BigDecimal arrivalProductQty = partnerProductQualityRating.getArrivalProductQty();
|
||||
supplierQualityRating =
|
||||
supplierQualityRating.add(qualityRating.multiply(arrivalProductQty));
|
||||
supplierArrivalProductQty = supplierArrivalProductQty.add(arrivalProductQty);
|
||||
}
|
||||
|
||||
if (supplierArrivalProductQty.signum() > 0) {
|
||||
supplierQualityRating =
|
||||
supplierQualityRating.divide(supplierArrivalProductQty, 2, RoundingMode.HALF_UP);
|
||||
} else {
|
||||
supplierQualityRating = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
partner.setSupplierQualityRating(supplierQualityRating);
|
||||
partner.setSupplierQualityRatingSelect(computeQualityRatingSelect(supplierQualityRating));
|
||||
partner.setSupplierArrivalProductQty(supplierArrivalProductQty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute quality rating.
|
||||
*
|
||||
* @param compliantArrivalProductQty
|
||||
* @param arrivalProductQty
|
||||
* @return
|
||||
*/
|
||||
private BigDecimal computeQualityRating(
|
||||
BigDecimal compliantArrivalProductQty, BigDecimal arrivalProductQty) {
|
||||
return compliantArrivalProductQty
|
||||
.multiply(MAX_QUALITY_RATING)
|
||||
.divide(arrivalProductQty, 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute quality rating selection value (rounding to the nearest half).
|
||||
*
|
||||
* @param qualityRating
|
||||
* @return
|
||||
*/
|
||||
private BigDecimal computeQualityRatingSelect(BigDecimal qualityRating) {
|
||||
final BigDecimal two = new BigDecimal(2);
|
||||
return qualityRating
|
||||
.multiply(two)
|
||||
.setScale(0, RoundingMode.HALF_UP)
|
||||
.divide(two, 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.PartnerStockSettings;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.meta.CallMethod;
|
||||
|
||||
public interface PartnerStockSettingsService {
|
||||
|
||||
/**
|
||||
* get the mail settings from the partner and the company, create if not found
|
||||
*
|
||||
* @param partner
|
||||
* @param company
|
||||
*/
|
||||
PartnerStockSettings getOrCreateMailSettings(Partner partner, Company company)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Create PartnerStockSettings in the given partner
|
||||
*
|
||||
* @param partner
|
||||
* @param company
|
||||
*/
|
||||
PartnerStockSettings createMailSettings(Partner partner, Company company) throws AxelorException;
|
||||
|
||||
/**
|
||||
* get default stock location for given partner and company
|
||||
*
|
||||
* @param partner
|
||||
* @param company
|
||||
*/
|
||||
@CallMethod
|
||||
public StockLocation getDefaultStockLocation(Partner partner, Company company);
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.PartnerStockSettings;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.repo.PartnerStockSettingsRepository;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PartnerStockSettingsServiceImpl implements PartnerStockSettingsService {
|
||||
|
||||
@Override
|
||||
public PartnerStockSettings getOrCreateMailSettings(Partner partner, Company company)
|
||||
throws AxelorException {
|
||||
List<PartnerStockSettings> mailSettingsList = partner.getPartnerStockSettingsList();
|
||||
if (mailSettingsList == null || mailSettingsList.isEmpty()) {
|
||||
return createMailSettings(partner, company);
|
||||
}
|
||||
Optional<PartnerStockSettings> partnerStockSettings =
|
||||
mailSettingsList
|
||||
.stream()
|
||||
.filter(stockSettings -> company.equals(stockSettings.getCompany()))
|
||||
.findAny();
|
||||
return partnerStockSettings.isPresent()
|
||||
? partnerStockSettings.get()
|
||||
: createMailSettings(partner, company);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public PartnerStockSettings createMailSettings(Partner partner, Company company)
|
||||
throws AxelorException {
|
||||
PartnerStockSettings mailSettings = new PartnerStockSettings();
|
||||
mailSettings.setCompany(company);
|
||||
StockConfig stockConfig = Beans.get(StockConfigService.class).getStockConfig(company);
|
||||
mailSettings.setPlannedStockMoveAutomaticMail(stockConfig.getPlannedStockMoveAutomaticMail());
|
||||
mailSettings.setPlannedStockMoveMessageTemplate(
|
||||
stockConfig.getPlannedStockMoveMessageTemplate());
|
||||
mailSettings.setRealStockMoveAutomaticMail(stockConfig.getRealStockMoveAutomaticMail());
|
||||
mailSettings.setRealStockMoveMessageTemplate(stockConfig.getRealStockMoveMessageTemplate());
|
||||
partner.addPartnerStockSettingsListItem(mailSettings);
|
||||
return Beans.get(PartnerStockSettingsRepository.class).save(mailSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocation getDefaultStockLocation(Partner partner, Company company) {
|
||||
|
||||
if (partner != null && company != null) {
|
||||
PartnerStockSettings partnerStockSettings =
|
||||
Beans.get(PartnerStockSettingsRepository.class)
|
||||
.all()
|
||||
.filter("self.partner = ? AND self.company = ?", partner, company)
|
||||
.fetchOne();
|
||||
|
||||
if (partnerStockSettings != null) {
|
||||
return partnerStockSettings.getDefaultStockLocation();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.stock.db.StockCorrection;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.util.Map;
|
||||
|
||||
public interface StockCorrectionService {
|
||||
|
||||
public Map<String, Object> fillDefaultValues(StockLocationLine stockLocationLine);
|
||||
|
||||
public Map<String, Object> fillDeafultQtys(StockCorrection stockCorrection);
|
||||
|
||||
public void getDefaultQtys(
|
||||
StockLocationLine stockLocationLine, Map<String, Object> stockCorrectionQtys);
|
||||
|
||||
public boolean validate(StockCorrection stockCorrection) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.StockCorrection;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.StockCorrectionRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class StockCorrectionServiceImpl implements StockCorrectionService {
|
||||
|
||||
@Inject private StockConfigService stockConfigService;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> fillDefaultValues(StockLocationLine stockLocationLine) {
|
||||
|
||||
Map<String, Object> stockCorrectionInformation = new HashMap<>();
|
||||
|
||||
stockCorrectionInformation.put(
|
||||
"stockLocation",
|
||||
stockLocationLine.getStockLocation() != null
|
||||
? stockLocationLine.getStockLocation()
|
||||
: stockLocationLine.getDetailsStockLocation());
|
||||
stockCorrectionInformation.put("product", stockLocationLine.getProduct());
|
||||
stockCorrectionInformation.put("trackingNumber", stockLocationLine.getTrackingNumber());
|
||||
getDefaultQtys(stockLocationLine, stockCorrectionInformation);
|
||||
|
||||
return stockCorrectionInformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> fillDeafultQtys(StockCorrection stockCorrection) {
|
||||
|
||||
Map<String, Object> stockCorrectionQtys = new HashMap<>();
|
||||
|
||||
StockLocationLineService stockLocationLineService = Beans.get(StockLocationLineService.class);
|
||||
StockLocationLine stockLocationLine;
|
||||
|
||||
if (stockCorrection.getTrackingNumber() == null) {
|
||||
stockLocationLine =
|
||||
stockLocationLineService.getStockLocationLine(
|
||||
stockCorrection.getStockLocation(), stockCorrection.getProduct());
|
||||
} else {
|
||||
stockLocationLine =
|
||||
stockLocationLineService.getDetailLocationLine(
|
||||
stockCorrection.getStockLocation(),
|
||||
stockCorrection.getProduct(),
|
||||
stockCorrection.getTrackingNumber());
|
||||
}
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
getDefaultQtys(stockLocationLine, stockCorrectionQtys);
|
||||
}
|
||||
return stockCorrectionQtys;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public boolean validate(StockCorrection stockCorrection) throws AxelorException {
|
||||
AppBaseService baseService = Beans.get(AppBaseService.class);
|
||||
StockMove stockMove = generateStockMove(stockCorrection);
|
||||
if (stockMove != null) {
|
||||
stockCorrection.setStatusSelect(StockCorrectionRepository.STATUS_VALIDATED);
|
||||
stockCorrection.setValidationDateT(baseService.getTodayDateTime().toLocalDateTime());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public StockMove generateStockMove(StockCorrection stockCorrection) throws AxelorException {
|
||||
StockLocation toStockLocation = stockCorrection.getStockLocation();
|
||||
|
||||
Company company = toStockLocation.getCompany();
|
||||
StockLocation fromStockLocation =
|
||||
stockConfigService.getInventoryVirtualStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
StockMoveService stockMoveService = Beans.get(StockMoveService.class);
|
||||
StockMoveLineService stockMoveLineService = Beans.get(StockMoveLineService.class);
|
||||
|
||||
StockLocationLine stockLocationLine = null;
|
||||
StockLocationLineService stockLocationLineService = Beans.get(StockLocationLineService.class);
|
||||
|
||||
if (stockCorrection.getTrackingNumber() == null) {
|
||||
stockLocationLine =
|
||||
stockLocationLineService.getStockLocationLine(
|
||||
stockCorrection.getStockLocation(), stockCorrection.getProduct());
|
||||
} else {
|
||||
stockLocationLine =
|
||||
stockLocationLineService.getDetailLocationLine(
|
||||
stockCorrection.getStockLocation(),
|
||||
stockCorrection.getProduct(),
|
||||
stockCorrection.getTrackingNumber());
|
||||
}
|
||||
|
||||
BigDecimal realQty = stockCorrection.getRealQty();
|
||||
Product product = stockCorrection.getProduct();
|
||||
TrackingNumber trackingNumber = stockCorrection.getTrackingNumber();
|
||||
BigDecimal diff = realQty.subtract(stockLocationLine.getCurrentQty());
|
||||
|
||||
StockMove stockMove = null;
|
||||
|
||||
if (diff.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return null;
|
||||
} else if (diff.compareTo(BigDecimal.ZERO) > 0) {
|
||||
stockMove = this.createStockMoveHeader(company, fromStockLocation, toStockLocation);
|
||||
} else {
|
||||
stockMove = this.createStockMoveHeader(company, toStockLocation, fromStockLocation);
|
||||
}
|
||||
|
||||
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_STOCK_CORRECTION);
|
||||
stockMove.setOriginId(stockCorrection.getId());
|
||||
stockMove.setStockCorrectionReason(stockCorrection.getStockCorrectionReason());
|
||||
|
||||
StockMoveLine stockMoveLine =
|
||||
stockMoveLineService.createStockMoveLine(
|
||||
product,
|
||||
product.getName(),
|
||||
product.getDescription(),
|
||||
diff.abs(),
|
||||
product.getCostPrice(),
|
||||
product.getCostPrice(),
|
||||
product.getUnit(),
|
||||
stockMove,
|
||||
StockMoveLineService.TYPE_NULL,
|
||||
false,
|
||||
BigDecimal.ZERO);
|
||||
|
||||
if (stockMoveLine == null) {
|
||||
throw new AxelorException(
|
||||
stockCorrection,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CORRECTION_1));
|
||||
}
|
||||
if (trackingNumber != null && stockMoveLine.getTrackingNumber() == null) {
|
||||
stockMoveLine.setTrackingNumber(trackingNumber);
|
||||
}
|
||||
|
||||
stockMoveService.plan(stockMove);
|
||||
stockMoveService.copyQtyToRealQty(stockMove);
|
||||
stockMoveService.realize(stockMove, false);
|
||||
|
||||
return stockMove;
|
||||
}
|
||||
|
||||
public StockMove createStockMoveHeader(
|
||||
Company company, StockLocation fromStockLocation, StockLocation toStockLocation)
|
||||
throws AxelorException {
|
||||
StockMove stockMove =
|
||||
Beans.get(StockMoveService.class)
|
||||
.createStockMove(
|
||||
null,
|
||||
null,
|
||||
company,
|
||||
fromStockLocation,
|
||||
toStockLocation,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
StockMoveRepository.TYPE_INTERNAL);
|
||||
return stockMove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDefaultQtys(
|
||||
StockLocationLine stockLocationLine, Map<String, Object> stockCorrectionQtys) {
|
||||
stockCorrectionQtys.put("realQty", stockLocationLine.getCurrentQty());
|
||||
stockCorrectionQtys.put("futureQty", stockLocationLine.getFutureQty());
|
||||
}
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.stock.db.StockHistoryLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface StockHistoryService {
|
||||
|
||||
/**
|
||||
* Compute lines for stock history. Compute one line per month between beginDate and endDate and
|
||||
* add two lines for average and total.
|
||||
*
|
||||
* @param productId id of the queried product, cannot be null.
|
||||
* @param companyId id of the company used as filter, cannot be null.
|
||||
* @param stockLocationId id of the stock location used as filter, cannot be null.
|
||||
* @param beginDate mandatory date used for the generation.
|
||||
* @param endDate mandatory date used for the generation.
|
||||
* @return the computed lines.
|
||||
*/
|
||||
List<StockHistoryLine> computeStockHistoryLineList(
|
||||
Long productId, Long companyId, Long stockLocationId, LocalDate beginDate, LocalDate endDate)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Compute lines for stock history. Compute one line per month between beginDate and endDate and
|
||||
* add two lines for average and total.
|
||||
*
|
||||
* @param productId id of the queried product, cannot be null.
|
||||
* @param companyId id of the company used as filter, cannot be null.
|
||||
* @param stockLocationId id of the stock location used as filter, cannot be null.
|
||||
* @param beginDate mandatory date used for the generation.
|
||||
* @param endDate mandatory date used for the generation.
|
||||
* @return the computed lines.
|
||||
*/
|
||||
List<StockHistoryLine> compuHistoryLinesPerDate
|
||||
(Long productId, Long companyId, Long stockLocationId, LocalDate beginDate, LocalDate endDate,Long categoryId,Long trackingNumberId,Integer searchTypeSelect)
|
||||
throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,809 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.app.AppSettings;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.CompanyRepository;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.stock.db.StockHistoryLine;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.apps.tool.file.CsvTool;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.exception.AxelorException;
|
||||
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.io.Files;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StockHistoryServiceImpl implements StockHistoryService {
|
||||
|
||||
protected StockMoveLineRepository stockMoveLineRepository;
|
||||
protected UnitConversionService unitConversionService;
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Inject
|
||||
public StockHistoryServiceImpl(
|
||||
StockMoveLineRepository stockMoveLineRepository,
|
||||
UnitConversionService unitConversionService) {
|
||||
this.stockMoveLineRepository = stockMoveLineRepository;
|
||||
this.unitConversionService = unitConversionService;
|
||||
}
|
||||
|
||||
public List<StockHistoryLine> computeStockHistoryLineList(
|
||||
Long productId, Long companyId, Long stockLocationId, LocalDate beginDate, LocalDate endDate)
|
||||
throws AxelorException {
|
||||
List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();
|
||||
|
||||
// one line per month
|
||||
for (LocalDate periodBeginDate = beginDate.withDayOfMonth(1);
|
||||
periodBeginDate.isBefore(endDate);
|
||||
periodBeginDate = periodBeginDate.plusMonths(1)) {
|
||||
LocalDate periodEndDate = periodBeginDate.plusMonths(1);
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(periodBeginDate.toString());
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
periodBeginDate,
|
||||
periodEndDate,
|
||||
true,
|
||||
false,
|
||||
null);
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
periodBeginDate,
|
||||
periodEndDate,
|
||||
false,
|
||||
false,
|
||||
null);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
}
|
||||
StockHistoryLine totalStockHistoryLine = createStockHistoryTotalLine(stockHistoryLineList);
|
||||
StockHistoryLine avgStockHistoryLine =
|
||||
createStockHistoryAvgLine(stockHistoryLineList, totalStockHistoryLine);
|
||||
stockHistoryLineList.add(totalStockHistoryLine);
|
||||
stockHistoryLineList.add(avgStockHistoryLine);
|
||||
|
||||
// result lines
|
||||
return stockHistoryLineList;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
protected void fetchAndFillResultForStockHistoryQuery(
|
||||
StockHistoryLine stockHistoryLine,
|
||||
Long productId,
|
||||
Long companyId,
|
||||
Long stockLocationId,
|
||||
LocalDate periodBeginDate,
|
||||
LocalDate periodEndDate,
|
||||
boolean incoming,
|
||||
boolean allLocations,
|
||||
TrackingNumber trackingNumber)
|
||||
throws AxelorException {
|
||||
Long trackingNumberId = trackingNumber.getId();
|
||||
String filter =
|
||||
"self.product.id = :productId "
|
||||
+ "AND self.stockMove.statusSelect = :realized "
|
||||
+ "AND self.stockMove.company.id = :companyId "
|
||||
+ "AND self.stockMove.realDate >= :beginDate "
|
||||
+ "AND self.stockMove.realDate < :endDate ";
|
||||
|
||||
if (incoming) {
|
||||
if (allLocations == true) {
|
||||
filter += "AND self.stockMove.fromStockLocation.typeSelect = :typeSelect ";
|
||||
} else {
|
||||
filter += "AND self.stockMove.toStockLocation.id = :stockLocationId ";
|
||||
}
|
||||
} else {
|
||||
if (allLocations == true) {
|
||||
filter += "AND self.stockMove.toStockLocation.typeSelect = :typeSelect ";
|
||||
} else {
|
||||
filter += "AND self.stockMove.fromStockLocation.id = :stockLocationId ";
|
||||
}
|
||||
}
|
||||
|
||||
if (trackingNumberId != null) {
|
||||
filter += " AND self.trackingNumber.id = :trackingNumberId";
|
||||
}
|
||||
|
||||
System.out.println("*************query filter************************");
|
||||
System.out.println(filter);
|
||||
System.out.println("*************query************************");
|
||||
|
||||
List<StockMoveLine> stockMoveLineList =
|
||||
stockMoveLineRepository
|
||||
.all()
|
||||
.filter(filter)
|
||||
.bind("productId", productId)
|
||||
.bind("companyId", companyId)
|
||||
.bind("stockLocationId", stockLocationId)
|
||||
.bind("realized", StockMoveRepository.STATUS_REALIZED)
|
||||
.bind("beginDate", periodBeginDate)
|
||||
.bind("endDate", periodEndDate)
|
||||
.bind("typeSelect", StockLocationRepository.TYPE_VIRTUAL)
|
||||
.bind("trackingNumberId", trackingNumberId)
|
||||
.fetch();
|
||||
|
||||
System.out.println("stockMoveLineList size******************************************");
|
||||
System.out.println(stockMoveLineList.size());
|
||||
System.out.println("stockMoveLineList size******************************************");
|
||||
|
||||
if (incoming) {
|
||||
fillIncomingStockHistoryLineFields(stockHistoryLine, stockMoveLineList);
|
||||
} else {
|
||||
fillOutgoingStockHistoryLineFields(stockHistoryLine, stockMoveLineList);
|
||||
}
|
||||
JPA.clear();
|
||||
}
|
||||
|
||||
protected void fillIncomingStockHistoryLineFields(
|
||||
StockHistoryLine stockHistoryLine, List<StockMoveLine> stockMoveLineList)
|
||||
throws AxelorException {
|
||||
|
||||
stockHistoryLine.setCountIncMvtStockPeriod(
|
||||
Math.toIntExact(
|
||||
stockMoveLineList.stream().map(StockMoveLine::getStockMove).distinct().count()));
|
||||
BigDecimal sumIncQtyPeriod = BigDecimal.ZERO;
|
||||
for (StockMoveLine stockMoveLine : stockMoveLineList) {
|
||||
// quantity in product unit
|
||||
BigDecimal qtyConverted =
|
||||
unitConversionService.convert(
|
||||
stockMoveLine.getUnit(),
|
||||
stockMoveLine.getProduct().getUnit(),
|
||||
stockMoveLine.getRealQty(),
|
||||
stockMoveLine.getRealQty().scale(),
|
||||
stockMoveLine.getProduct());
|
||||
sumIncQtyPeriod = sumIncQtyPeriod.add(qtyConverted);
|
||||
}
|
||||
stockHistoryLine.setSumIncQtyPeriod(sumIncQtyPeriod);
|
||||
stockHistoryLine.setPriceIncStockMovePeriod(
|
||||
stockMoveLineList
|
||||
.stream()
|
||||
.map(StockMoveLine::getCompanyUnitPriceUntaxed)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
protected void fillOutgoingStockHistoryLineFields(
|
||||
StockHistoryLine stockHistoryLine, List<StockMoveLine> stockMoveLineList)
|
||||
throws AxelorException {
|
||||
|
||||
stockHistoryLine.setCountOutMvtStockPeriod(
|
||||
Math.toIntExact(
|
||||
stockMoveLineList.stream().map(StockMoveLine::getStockMove).distinct().count()));
|
||||
BigDecimal sumOutQtyPeriod = BigDecimal.ZERO;
|
||||
for (StockMoveLine stockMoveLine : stockMoveLineList) {
|
||||
// quantity in product unit
|
||||
BigDecimal qtyConverted =
|
||||
unitConversionService.convert(
|
||||
stockMoveLine.getUnit(),
|
||||
stockMoveLine.getProduct().getUnit(),
|
||||
stockMoveLine.getRealQty(),
|
||||
stockMoveLine.getRealQty().scale(),
|
||||
stockMoveLine.getProduct());
|
||||
sumOutQtyPeriod = sumOutQtyPeriod.add(qtyConverted);
|
||||
}
|
||||
stockHistoryLine.setSumOutQtyPeriod(sumOutQtyPeriod);
|
||||
stockHistoryLine.setPriceOutStockMovePeriod(
|
||||
stockMoveLineList
|
||||
.stream()
|
||||
.map(StockMoveLine::getCompanyUnitPriceUntaxed)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a line labelled "Total", summing each field in the table.
|
||||
*
|
||||
* @param stockHistoryLineList
|
||||
* @return the created line.
|
||||
*/
|
||||
protected StockHistoryLine createStockHistoryTotalLine(
|
||||
List<StockHistoryLine> stockHistoryLineList) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(I18n.get("Total"));
|
||||
|
||||
Integer countIncMvtStock =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getCountIncMvtStockPeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(Integer::intValue)
|
||||
.sum();
|
||||
stockHistoryLine.setCountIncMvtStockPeriod(countIncMvtStock);
|
||||
|
||||
BigDecimal sumIncQtyPeriod =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getSumIncQtyPeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
stockHistoryLine.setSumIncQtyPeriod(sumIncQtyPeriod);
|
||||
|
||||
BigDecimal priceIncStockMove =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getPriceIncStockMovePeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
stockHistoryLine.setPriceIncStockMovePeriod(priceIncStockMove);
|
||||
|
||||
Integer countOutMvtStock =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getCountOutMvtStockPeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(Integer::intValue)
|
||||
.sum();
|
||||
stockHistoryLine.setCountOutMvtStockPeriod(countOutMvtStock);
|
||||
|
||||
BigDecimal sumOutQtyPeriod =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getSumOutQtyPeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
stockHistoryLine.setSumOutQtyPeriod(sumOutQtyPeriod);
|
||||
|
||||
BigDecimal priceOutStockMove =
|
||||
stockHistoryLineList
|
||||
.stream()
|
||||
.map(StockHistoryLine::getPriceOutStockMovePeriod)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
stockHistoryLine.setPriceOutStockMovePeriod(priceOutStockMove);
|
||||
|
||||
return stockHistoryLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a line labelled "Average", using a list of stockHistory and a line containing totals.
|
||||
*
|
||||
* @param stockHistoryLineList
|
||||
* @param totalStockHistoryLine
|
||||
* @return the created line.
|
||||
*/
|
||||
protected StockHistoryLine createStockHistoryAvgLine(
|
||||
List<StockHistoryLine> stockHistoryLineList, StockHistoryLine totalStockHistoryLine) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(I18n.get("Average"));
|
||||
|
||||
int sizeOfList = stockHistoryLineList.size();
|
||||
if (sizeOfList == 0) {
|
||||
return stockHistoryLine;
|
||||
}
|
||||
|
||||
stockHistoryLine.setCountIncMvtStockPeriod(
|
||||
totalStockHistoryLine.getCountIncMvtStockPeriod() / sizeOfList);
|
||||
|
||||
stockHistoryLine.setSumIncQtyPeriod(
|
||||
totalStockHistoryLine
|
||||
.getSumIncQtyPeriod()
|
||||
.divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));
|
||||
|
||||
stockHistoryLine.setPriceIncStockMovePeriod(
|
||||
totalStockHistoryLine
|
||||
.getPriceIncStockMovePeriod()
|
||||
.divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));
|
||||
|
||||
stockHistoryLine.setCountOutMvtStockPeriod(
|
||||
totalStockHistoryLine.getCountOutMvtStockPeriod() / sizeOfList);
|
||||
|
||||
stockHistoryLine.setSumOutQtyPeriod(
|
||||
totalStockHistoryLine
|
||||
.getSumOutQtyPeriod()
|
||||
.divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));
|
||||
|
||||
stockHistoryLine.setPriceOutStockMovePeriod(
|
||||
totalStockHistoryLine
|
||||
.getPriceOutStockMovePeriod()
|
||||
.divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));
|
||||
|
||||
return stockHistoryLine;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<StockHistoryLine> compuHistoryLinesPerDate(
|
||||
Long productId,
|
||||
Long companyId,
|
||||
Long stockLocationId,
|
||||
LocalDate beginDate,
|
||||
LocalDate endDate,
|
||||
Long categoryId,
|
||||
Long trackingNumberId,
|
||||
Integer searchTypeSelect)
|
||||
throws AxelorException {
|
||||
List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();
|
||||
Company company = Beans.get(CompanyRepository.class).find(companyId);
|
||||
|
||||
// ALL
|
||||
if (searchTypeSelect == 1) {
|
||||
|
||||
List<Product> products =
|
||||
Beans.get(StockLocationLineRepository.class)
|
||||
.all()
|
||||
.fetchStream()
|
||||
.map(StockLocationLine::getProduct)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
for (Product product : products) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(product.getFullName().toString());
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
true,
|
||||
true,
|
||||
null);
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
false,
|
||||
true,
|
||||
null);
|
||||
|
||||
BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, true, null);
|
||||
BigDecimal realQty =
|
||||
totalQty
|
||||
.add(stockHistoryLine.getSumOutQtyPeriod())
|
||||
.subtract(stockHistoryLine.getSumIncQtyPeriod());
|
||||
stockHistoryLine.setRealQty(realQty);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
}
|
||||
|
||||
} else if (searchTypeSelect == 3) {
|
||||
List<Product> products =
|
||||
Beans.get(ProductRepository.class)
|
||||
.all()
|
||||
.filter("self.familleProduit.id in (" + String.valueOf(categoryId) + ")")
|
||||
.fetch();
|
||||
for (Product product : products) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(product.getFullName().toString());
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
true,
|
||||
true,
|
||||
null);
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
false,
|
||||
true,
|
||||
null);
|
||||
|
||||
BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, true, null);
|
||||
BigDecimal realQty =
|
||||
totalQty
|
||||
.add(stockHistoryLine.getSumOutQtyPeriod())
|
||||
.subtract(stockHistoryLine.getSumIncQtyPeriod());
|
||||
stockHistoryLine.setRealQty(realQty);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
}
|
||||
|
||||
} else if (searchTypeSelect == 2) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(beginDate.toString());
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
true,
|
||||
false,
|
||||
null);
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
false,
|
||||
false,
|
||||
null);
|
||||
|
||||
Product product = Beans.get(ProductRepository.class).find(productId);
|
||||
BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, false, null);
|
||||
BigDecimal realQty =
|
||||
totalQty
|
||||
.add(stockHistoryLine.getSumOutQtyPeriod())
|
||||
.subtract(stockHistoryLine.getSumIncQtyPeriod());
|
||||
stockHistoryLine.setRealQty(realQty);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
} else if (searchTypeSelect == 4) {
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
TrackingNumber trackingNumber =
|
||||
Beans.get(TrackingNumberRepository.class).find(trackingNumberId);
|
||||
Product product = Beans.get(ProductRepository.class).find(productId);
|
||||
|
||||
stockHistoryLine.setLabel(
|
||||
beginDate.toString()
|
||||
+ " "
|
||||
+ product.getName()
|
||||
+ " "
|
||||
+ trackingNumber.getTrackingNumberSeq());
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
true,
|
||||
true,
|
||||
trackingNumber);
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
false,
|
||||
true,
|
||||
trackingNumber);
|
||||
|
||||
BigDecimal totalQty =
|
||||
this.getTotalQty(product, company, stockLocationId, true, trackingNumberId);
|
||||
System.out.println("***************totalQty*******************");
|
||||
System.out.println(trackingNumberId);
|
||||
System.out.println(totalQty);
|
||||
System.out.println("***************totalQty*******************");
|
||||
BigDecimal realQty =
|
||||
totalQty
|
||||
.add(stockHistoryLine.getSumOutQtyPeriod())
|
||||
.subtract(stockHistoryLine.getSumIncQtyPeriod());
|
||||
stockHistoryLine.setRealQty(realQty);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
} else if (searchTypeSelect == 5) {
|
||||
List<StockLocationLine> stockLocationLines =
|
||||
Beans.get(StockLocationLineRepository.class)
|
||||
.all()
|
||||
.filter(
|
||||
"self.detailsStockLocation.typeSelect != "
|
||||
+ StockLocationRepository.TYPE_VIRTUAL
|
||||
+ " AND self.detailsStockLocation != null AND self.product.familleProduit.id in ("
|
||||
+ String.valueOf(categoryId)
|
||||
+ ")")
|
||||
.fetch();
|
||||
|
||||
log.debug("stockLocationLines : {}", stockLocationLines);
|
||||
log.debug("stockLocationLines size : {}", stockLocationLines.size());
|
||||
log.debug("categoryId : {}", categoryId);
|
||||
log.debug("categoryId : {}", categoryId);
|
||||
|
||||
int sizeWithoutTraccking =
|
||||
stockLocationLines
|
||||
.stream()
|
||||
.filter(t -> t.getTrackingNumber() == null)
|
||||
.collect(Collectors.toList())
|
||||
.size();
|
||||
|
||||
log.debug("sizeWithoutTraccking : {}", sizeWithoutTraccking);
|
||||
|
||||
List<TrackingNumber> trackingNumbers =
|
||||
Beans.get(TrackingNumberRepository.class).all().fetch();
|
||||
List<Product> products = Beans.get(ProductRepository.class).all().fetch();
|
||||
|
||||
this.sumGroupByStocklocation(null, null, beginDate, null);
|
||||
|
||||
for (StockLocationLine line : stockLocationLines) {
|
||||
|
||||
TrackingNumber trackingNumber =
|
||||
trackingNumbers
|
||||
.stream()
|
||||
.filter(t -> t.getId() == line.getTrackingNumber().getId())
|
||||
.findFirst()
|
||||
.get();
|
||||
Product product =
|
||||
products.stream().filter(t -> t.getId() == line.getProduct().getId()).findFirst().get();
|
||||
|
||||
Long trackingNumberId2 = trackingNumber.getId();
|
||||
|
||||
if (trackingNumber != null) {
|
||||
StockLocation location = line.getDetailsStockLocation();
|
||||
log.debug("trackingNumber >>>>>> : {}", trackingNumber);
|
||||
log.debug("product >>>>>> : {}", product);
|
||||
|
||||
StockHistoryLine stockHistoryLine = new StockHistoryLine();
|
||||
stockHistoryLine.setLabel(
|
||||
product.getName()
|
||||
+ "*"
|
||||
+ trackingNumber.getTrackingNumberSeq()
|
||||
+ "*"
|
||||
+ location.getName());
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
location.getId(),
|
||||
beginDate,
|
||||
endDate,
|
||||
true,
|
||||
false,
|
||||
trackingNumber);
|
||||
|
||||
fetchAndFillResultForStockHistoryQuery(
|
||||
stockHistoryLine,
|
||||
product.getId(),
|
||||
companyId,
|
||||
location.getId(),
|
||||
beginDate,
|
||||
endDate,
|
||||
false,
|
||||
false,
|
||||
trackingNumber);
|
||||
|
||||
BigDecimal totalQty =
|
||||
this.getTotalQty(product, company, location.getId(), false, trackingNumberId2);
|
||||
BigDecimal realQty =
|
||||
totalQty
|
||||
.add(stockHistoryLine.getSumOutQtyPeriod())
|
||||
.subtract(stockHistoryLine.getSumIncQtyPeriod());
|
||||
|
||||
log.debug(
|
||||
"*** trackingNumber || totalQty || realQty : {} {} {}",
|
||||
trackingNumber,
|
||||
totalQty,
|
||||
realQty);
|
||||
|
||||
stockHistoryLine.setRealQty(realQty);
|
||||
stockHistoryLineList.add(stockHistoryLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stockHistoryLineList;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public BigDecimal getTotalQty(
|
||||
Product product,
|
||||
Company company,
|
||||
Long StockLocationId,
|
||||
boolean allLocations,
|
||||
Long trackingNumberId) {
|
||||
Long productId = product.getId();
|
||||
String query = "";
|
||||
|
||||
query =
|
||||
"SELECT new list(self.id, self.avgPrice, self.currentQty) FROM StockLocationLine as self "
|
||||
+ "WHERE self.product.id = "
|
||||
+ productId;
|
||||
|
||||
if (trackingNumberId == null) {
|
||||
query += " AND self.stockLocation.typeSelect != " + StockLocationRepository.TYPE_VIRTUAL;
|
||||
} else {
|
||||
query +=
|
||||
" AND self.detailsStockLocation.typeSelect != "
|
||||
+ StockLocationRepository.TYPE_VIRTUAL
|
||||
+ " AND self.trackingNumber.id = "
|
||||
+ trackingNumberId;
|
||||
}
|
||||
|
||||
if (!allLocations) {
|
||||
if (trackingNumberId == null) {
|
||||
query += " AND self.stockLocation.id in (" + StockLocationId + ")";
|
||||
} else {
|
||||
query += " AND self.detailsStockLocation.id in (" + StockLocationId + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (company != null) {
|
||||
if (trackingNumberId == null) {
|
||||
query += " AND self.stockLocation.company = " + company.getId();
|
||||
} else {
|
||||
query += " AND self.detailsStockLocation.company = " + company.getId();
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal qtyTot = BigDecimal.ZERO;
|
||||
List<List<Object>> results = JPA.em().createQuery(query).getResultList();
|
||||
if (results.isEmpty()) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
for (List<Object> result : results) {
|
||||
BigDecimal qty = (BigDecimal) result.get(2);
|
||||
qtyTot = qtyTot.add(qty);
|
||||
}
|
||||
if (qtyTot.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return qtyTot;
|
||||
}
|
||||
|
||||
public MetaFile exportToCSV(List<HashMap<String, Object>> historyLines)
|
||||
throws AxelorException, IOException {
|
||||
List<String[]> allMoveLineData = new ArrayList<>();
|
||||
|
||||
System.out.println("***************************************");
|
||||
System.out.println(historyLines.size());
|
||||
System.out.println("***************************************");
|
||||
|
||||
for (HashMap<String, Object> historyLine : historyLines) {
|
||||
String[] items = new String[6];
|
||||
int inc =
|
||||
historyLine.get("countIncMvtStockPeriod") != null
|
||||
? Integer.parseInt(historyLine.get("countIncMvtStockPeriod").toString())
|
||||
: 0;
|
||||
int out =
|
||||
historyLine.get("countOutMvtStockPeriod") != null
|
||||
? Integer.parseInt(historyLine.get("countOutMvtStockPeriod").toString())
|
||||
: 0;
|
||||
String label = historyLine.get("label") != null ? historyLine.get("label").toString() : "";
|
||||
BigDecimal sumInc =
|
||||
historyLine.get("sumIncQtyPeriod") != null
|
||||
? new BigDecimal(historyLine.get("sumIncQtyPeriod").toString())
|
||||
: BigDecimal.ZERO;
|
||||
BigDecimal sumOut =
|
||||
historyLine.get("sumOutQtyPeriod") != null
|
||||
? new BigDecimal(historyLine.get("sumOutQtyPeriod").toString())
|
||||
: BigDecimal.ZERO;
|
||||
BigDecimal realQty =
|
||||
historyLine.get("realQty") != null
|
||||
? new BigDecimal(historyLine.get("realQty").toString())
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
items[0] = label;
|
||||
items[1] = String.valueOf(inc);
|
||||
items[2] = String.valueOf(sumInc);
|
||||
items[3] = String.valueOf(out);
|
||||
items[4] = String.valueOf(sumOut);
|
||||
items[5] = String.valueOf(realQty);
|
||||
allMoveLineData.add(items);
|
||||
}
|
||||
|
||||
String filePath = Files.createTempDir().getAbsolutePath();
|
||||
|
||||
if (filePath == null) {
|
||||
filePath = AppSettings.get().get("file.upload.dir");
|
||||
} else {
|
||||
new File(filePath).mkdirs();
|
||||
}
|
||||
|
||||
String fileName = I18n.get("StockPerDate") + ".csv";
|
||||
Path path = Paths.get(filePath, fileName);
|
||||
File file = path.toFile();
|
||||
|
||||
String[] headers = {
|
||||
I18n.get("Product Name"),
|
||||
I18n.get("Inc"),
|
||||
I18n.get("SumInc"),
|
||||
I18n.get("Out"),
|
||||
I18n.get("SumOut"),
|
||||
I18n.get("Real Quantity"),
|
||||
};
|
||||
|
||||
CsvTool.csvWriter(filePath, fileName, ';', headers, allMoveLineData);
|
||||
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
return Beans.get(MetaFiles.class).upload(is, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
public void sumGroupByStocklocation(
|
||||
Long detailsStockLocationId, Long productId, LocalDate date, Long trackingNumberId) {
|
||||
Query q =
|
||||
JPA.em()
|
||||
.createNativeQuery(
|
||||
"select coalesce(m2.sum,0) inn,coalesce(m1.sum,0) outt from"
|
||||
+ " (SELECT product,line.tracking_number,from_stock_location,sum(real_qty)"
|
||||
+ " FROM STOCK_STOCK_MOVE_LINE LINE left join stock_stock_move move on move.id = line.stock_move"
|
||||
+ " where (line.archived = false OR line.archived is null) and move.estimated_date >= ?1 "
|
||||
+ " group by product,line.tracking_number,from_stock_location) m1"
|
||||
+ " full outer join"
|
||||
+ " (SELECT product,line.tracking_number,to_stock_location,sum(real_qty)"
|
||||
+ " FROM STOCK_STOCK_MOVE_LINE LINE left join stock_stock_move move on move.id = line.stock_move"
|
||||
+ " where (line.archived = false OR line.archived is null) and move.estimated_date >= ?2 "
|
||||
+ " group by product,line.tracking_number,to_stock_location) m2 "
|
||||
+ " on m1.product = m2.product and ((m1.tracking_number is null and m2.tracking_number is null) or m1.tracking_number = m2.tracking_number) and m1.from_stock_location = m2.to_stock_location"
|
||||
// " where"+
|
||||
// " coalesce(m1.product, m2.product) = ?3 and "+
|
||||
// " coalesce(m1.tracking_number,m2.tracking_number) = ?4"+
|
||||
// " and coalesce(m1.from_stock_location,m2.to_stock_location) = ?5"
|
||||
);
|
||||
|
||||
q.setParameter(1, date);
|
||||
q.setParameter(2, date);
|
||||
// q.setParameter(3, productId);
|
||||
// q.setParameter(4, trackingNumberId);
|
||||
// q.setParameter(5, detailsStockLocationId);
|
||||
|
||||
List<Object[]> resultList = q.getResultList();
|
||||
System.out.println("*************resultList***************");
|
||||
System.out.println(resultList.toString());
|
||||
System.out.println("*************resultList***************");
|
||||
|
||||
for (Object[] result : resultList) {
|
||||
System.out.println("*************result***************");
|
||||
System.out.println(result.toString());
|
||||
System.out.println("*************result***************");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface StockLocationLineService {
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
TrackingNumber trackingNumber,
|
||||
int conformitySelect)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate)
|
||||
throws AxelorException;
|
||||
|
||||
public void minStockRules(
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
boolean current,
|
||||
boolean future)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateDetailLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
TrackingNumber trackingNumber)
|
||||
throws AxelorException;
|
||||
|
||||
public void checkStockMin(StockLocationLine stockLocationLine, boolean isDetailLocationLine)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check if the stock location has more than qty units of the product
|
||||
*
|
||||
* @param stockLocation
|
||||
* @param product
|
||||
* @param qty
|
||||
* @throws AxelorException if there is not enough qty in stock
|
||||
*/
|
||||
public void checkIfEnoughStock(StockLocation stockLocation, Product product, BigDecimal qty)
|
||||
throws AxelorException;
|
||||
|
||||
public StockLocationLine updateLocation(
|
||||
StockLocationLine stockLocationLine,
|
||||
Unit stockMoveLineUnit,
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateStockLocationFromProduct(StockLocationLine stockLocationLine, Product product)
|
||||
throws AxelorException;
|
||||
|
||||
public StockLocationLine updateLocationFromProduct(
|
||||
StockLocationLine stockLocationLine, Product product) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Getting the stock location line : We check if the location has a detailed line for a given
|
||||
* product. If no detailed location line is found, we create it.
|
||||
*
|
||||
* @param stockLocation A location
|
||||
* @param product A product
|
||||
* @return The found or created location line
|
||||
*/
|
||||
public StockLocationLine getOrCreateStockLocationLine(
|
||||
StockLocation stockLocation, Product product);
|
||||
|
||||
/**
|
||||
* Getting the detailed stock location line : We check if the location has a detailed line for a
|
||||
* given product, product variant and tracking number. If no detailed location line is found, we
|
||||
* create it.
|
||||
*
|
||||
* @param detailLocation A location
|
||||
* @param product A product
|
||||
* @param trackingNumber A tracking number
|
||||
* @return The found or created detailed location line
|
||||
*/
|
||||
public StockLocationLine getOrCreateDetailLocationLine(
|
||||
StockLocation detailLocation, Product product, TrackingNumber trackingNumber);
|
||||
|
||||
/**
|
||||
* Allow to get the location line of a given product in a given location.
|
||||
*
|
||||
* @param stockLocation A location
|
||||
* @param product A product
|
||||
* @return The stock location line if found, else null
|
||||
*/
|
||||
public StockLocationLine getStockLocationLine(StockLocation stockLocation, Product product);
|
||||
|
||||
/**
|
||||
* Allow to get the location lines of a given product.
|
||||
*
|
||||
* @param product
|
||||
* @return
|
||||
*/
|
||||
public List<StockLocationLine> getStockLocationLines(Product product);
|
||||
|
||||
/**
|
||||
* Allow to get the detailed location line of a given product, product variant and tracking number
|
||||
* in a given location.
|
||||
*
|
||||
* @param stockLocation A location
|
||||
* @param product A product
|
||||
* @param trackingNumber A tracking number
|
||||
* @return The stock location line if found, else null
|
||||
*/
|
||||
public StockLocationLine getDetailLocationLine(
|
||||
StockLocation stockLocation, Product product, TrackingNumber trackingNumber);
|
||||
|
||||
/**
|
||||
* Allow the creation of a location line of a given product in a given location.
|
||||
*
|
||||
* @param stockLocation A location
|
||||
* @param product A product
|
||||
* @return The created stock location line
|
||||
*/
|
||||
public StockLocationLine createLocationLine(StockLocation stockLocation, Product product);
|
||||
|
||||
/**
|
||||
* Allow the creation of a detailed location line of a given product, product variant, and
|
||||
* tracking number in a given location.
|
||||
*
|
||||
* @param stockLocation A location
|
||||
* @param product A product
|
||||
* @param trackingNumber A tracking number
|
||||
* @return The created detailed stock location line
|
||||
*/
|
||||
public StockLocationLine createDetailLocationLine(
|
||||
StockLocation stockLocation, Product product, TrackingNumber trackingNumber);
|
||||
|
||||
/**
|
||||
* Allow to get the available qty of product in a given location.
|
||||
*
|
||||
* @param stockLocation
|
||||
* @param product
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getAvailableQty(StockLocation stockLocation, Product product);
|
||||
|
||||
/**
|
||||
* For a given line, compute the future quantity of a stock location line from its current qty and
|
||||
* planned stock move lines with the same stock location and the same product.
|
||||
*
|
||||
* @param stockLocationLine a stock location line with a product and a stock location.
|
||||
* @return the future quantity of the stock location line.
|
||||
*/
|
||||
BigDecimal computeFutureQty(StockLocationLine stockLocationLine) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Create a query to find stock location line of a product of a specific/all company and a
|
||||
* specific/all stock location
|
||||
*
|
||||
* @param productId, companyId and stockLocationId
|
||||
* @return the query.
|
||||
*/
|
||||
public String getStockLocationLineListForAProduct(
|
||||
Long productId, Long companyId, Long stockLocationId);
|
||||
|
||||
/**
|
||||
* Create a query to find product's available qty of a specific/all company and a specific/all
|
||||
* stock location
|
||||
*
|
||||
* @param productId, companyId and stockLocationId
|
||||
* @return the query.
|
||||
*/
|
||||
public String getAvailableStockForAProduct(Long productId, Long companyId, Long stockLocationId);
|
||||
|
||||
/**
|
||||
* Create a query to find product's requested reserved qty of a specific/all company and a
|
||||
* specific/all stock location
|
||||
*
|
||||
* @param productId, companyId and stockLocationId
|
||||
* @return the query.
|
||||
*/
|
||||
public String getRequestedReservedQtyForAProduct(
|
||||
Long productId, Long companyId, Long stockLocationId);
|
||||
|
||||
/**
|
||||
* Update avgPrice in stock location line and save wap history in the line.
|
||||
*
|
||||
* @param stockLocationLine stock location line to updated.
|
||||
* @param wap weighted average price which will update the field avgPrice.
|
||||
*/
|
||||
void updateWap(StockLocationLine stockLocationLine, BigDecimal wap);
|
||||
|
||||
/**
|
||||
* Update avgPrice in stock location line and save wap history in the line.
|
||||
*
|
||||
* @param stockLocationLine stock location line to updated.
|
||||
* @param wap weighted average price which will update the field avgPrice.
|
||||
* @param stockMoveLine the move line responsible for the WAP change.
|
||||
*/
|
||||
void updateWap(StockLocationLine stockLocationLine, BigDecimal wap, StockMoveLine stockMoveLine);
|
||||
|
||||
/**
|
||||
* Allow to get the available qty of product for a given Tracking Number.
|
||||
*
|
||||
* @param stockLocation
|
||||
* @param trackingNumber
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getTrackingNumberAvailableQty(
|
||||
StockLocation stockLocation, TrackingNumber trackingNumber);
|
||||
}
|
||||
@ -0,0 +1,763 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.StockRules;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.WapHistory;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockRulesRepository;
|
||||
import com.axelor.apps.stock.db.repo.WapHistoryRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
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.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
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;
|
||||
|
||||
@RequestScoped
|
||||
public class StockLocationLineServiceImpl implements StockLocationLineService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected StockLocationLineRepository stockLocationLineRepo;
|
||||
|
||||
protected StockRulesService stockRulesService;
|
||||
|
||||
protected StockMoveLineRepository stockMoveLineRepository;
|
||||
|
||||
protected AppBaseService appBaseService;
|
||||
|
||||
protected WapHistoryRepository wapHistoryRepo;
|
||||
|
||||
@Inject
|
||||
public StockLocationLineServiceImpl(
|
||||
StockLocationLineRepository stockLocationLineRepo,
|
||||
StockRulesService stockRulesService,
|
||||
StockMoveLineRepository stockMoveLineRepository,
|
||||
AppBaseService appBaseService,
|
||||
WapHistoryRepository wapHistoryRepo) {
|
||||
this.stockLocationLineRepo = stockLocationLineRepo;
|
||||
this.stockRulesService = stockRulesService;
|
||||
this.stockMoveLineRepository = stockMoveLineRepository;
|
||||
this.appBaseService = appBaseService;
|
||||
this.wapHistoryRepo = wapHistoryRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
TrackingNumber trackingNumber,
|
||||
int conformitySelect)
|
||||
throws AxelorException {
|
||||
|
||||
this.updateLocation(
|
||||
stockLocation,
|
||||
product,
|
||||
stockMoveLineUnit,
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate);
|
||||
|
||||
if (trackingNumber != null ) {
|
||||
this.updateDetailLocation(
|
||||
stockLocation,
|
||||
product,
|
||||
stockMoveLineUnit,
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate,
|
||||
trackingNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate)
|
||||
throws AxelorException {
|
||||
|
||||
StockLocationLine stockLocationLine = this.getOrCreateStockLocationLine(stockLocation, product);
|
||||
|
||||
if (stockLocationLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
Unit stockLocationLineUnit = stockLocationLine.getUnit();
|
||||
|
||||
if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) {
|
||||
qty =
|
||||
unitConversionService.convert(
|
||||
stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product);
|
||||
}
|
||||
|
||||
LOG.debug(
|
||||
"Mise à jour du stock : Entrepot? {}, Produit? {}, Quantité? {}, Actuel? {}, Futur? {}, Incrément? {}, Date? {} ",
|
||||
stockLocation.getName(),
|
||||
product.getCode(),
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate);
|
||||
|
||||
if (!isIncrement) {
|
||||
minStockRules(product, qty, stockLocationLine, current, future);
|
||||
} else {
|
||||
maxStockRules(product, qty, stockLocationLine, current, future);
|
||||
}
|
||||
|
||||
stockLocationLine =
|
||||
this.updateLocation(
|
||||
stockLocationLine,
|
||||
stockMoveLineUnit,
|
||||
product,
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate);
|
||||
|
||||
this.checkStockMin(stockLocationLine, false);
|
||||
|
||||
stockLocationLineRepo.save(stockLocationLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void minStockRules(
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
boolean current,
|
||||
boolean future)
|
||||
throws AxelorException {
|
||||
|
||||
if (current) {
|
||||
stockRulesService.generateOrder(
|
||||
product, qty, stockLocationLine, StockRulesRepository.TYPE_CURRENT);
|
||||
}
|
||||
if (future) {
|
||||
stockRulesService.generateOrder(
|
||||
product, qty, stockLocationLine, StockRulesRepository.TYPE_FUTURE);
|
||||
}
|
||||
}
|
||||
|
||||
public void maxStockRules(
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
boolean current,
|
||||
boolean future)
|
||||
throws AxelorException {
|
||||
|
||||
if (current) {
|
||||
checkStockMax(
|
||||
product,
|
||||
qty,
|
||||
stockLocationLine,
|
||||
StockRulesRepository.TYPE_CURRENT,
|
||||
stockLocationLine.getCurrentQty());
|
||||
}
|
||||
|
||||
if (future) {
|
||||
checkStockMax(
|
||||
product,
|
||||
qty,
|
||||
stockLocationLine,
|
||||
StockRulesRepository.TYPE_FUTURE,
|
||||
stockLocationLine.getFutureQty());
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkStockMax(
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
int type,
|
||||
BigDecimal baseQty)
|
||||
throws AxelorException {
|
||||
StockLocation stockLocation = stockLocationLine.getStockLocation();
|
||||
StockRules stockRules =
|
||||
stockRulesService.getStockRules(
|
||||
product, stockLocation, type, StockRulesRepository.USE_CASE_STOCK_CONTROL);
|
||||
|
||||
if (stockRules == null || !stockRules.getUseMaxQty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (baseQty.add(qty).compareTo(stockRules.getMaxQty()) > 0) {
|
||||
throw new AxelorException(
|
||||
stockLocationLine,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOCATION_LINE_3),
|
||||
stockLocationLine.getProduct().getName(),
|
||||
stockLocationLine.getProduct().getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateDetailLocation(
|
||||
StockLocation stockLocation,
|
||||
Product product,
|
||||
Unit stockMoveLineUnit,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
TrackingNumber trackingNumber)
|
||||
throws AxelorException {
|
||||
|
||||
StockLocationLine detailLocationLine =
|
||||
this.getOrCreateDetailLocationLine(stockLocation, product, trackingNumber);
|
||||
|
||||
if (detailLocationLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
Unit stockLocationLineUnit = detailLocationLine.getUnit();
|
||||
|
||||
if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) {
|
||||
qty =
|
||||
unitConversionService.convert(
|
||||
stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product);
|
||||
}
|
||||
|
||||
LOG.debug(
|
||||
"Mise à jour du detail du stock : Entrepot? {}, Produit? {}, Quantité? {}, Actuel? {}, Futur? {}, Incrément? {}, Date? {}, Num de suivi? {} ",
|
||||
stockLocation.getName(),
|
||||
product.getCode(),
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate,
|
||||
trackingNumber);
|
||||
|
||||
detailLocationLine =
|
||||
this.updateLocation(
|
||||
detailLocationLine,
|
||||
stockMoveLineUnit,
|
||||
product,
|
||||
qty,
|
||||
current,
|
||||
future,
|
||||
isIncrement,
|
||||
lastFutureStockMoveDate);
|
||||
|
||||
this.checkStockMin(detailLocationLine, true);
|
||||
// detailLocationLine.setConformitySelect(StockLocationRepository.TYPE_QUARANTINE);
|
||||
|
||||
stockLocationLineRepo.save(detailLocationLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkStockMin(StockLocationLine stockLocationLine, boolean isDetailLocationLine)
|
||||
throws AxelorException {
|
||||
if (!isDetailLocationLine
|
||||
&& stockLocationLine.getCurrentQty().compareTo(BigDecimal.ZERO) < 0
|
||||
&& stockLocationLine.getStockLocation().getTypeSelect()
|
||||
!= StockLocationRepository.TYPE_VIRTUAL) {
|
||||
throw new AxelorException(
|
||||
stockLocationLine,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOCATION_LINE_1),
|
||||
stockLocationLine.getProduct().getName(),
|
||||
stockLocationLine.getProduct().getCode());
|
||||
|
||||
} else if (isDetailLocationLine
|
||||
&& stockLocationLine.getCurrentQty().compareTo(BigDecimal.ZERO) < 0
|
||||
&& ((stockLocationLine.getStockLocation() != null
|
||||
&& stockLocationLine.getStockLocation().getTypeSelect()
|
||||
!= StockLocationRepository.TYPE_VIRTUAL)
|
||||
|| (stockLocationLine.getDetailsStockLocation() != null
|
||||
&& stockLocationLine.getDetailsStockLocation().getTypeSelect()
|
||||
!= StockLocationRepository.TYPE_VIRTUAL))) {
|
||||
|
||||
String trackingNumber = "";
|
||||
if (stockLocationLine.getTrackingNumber() != null) {
|
||||
trackingNumber = stockLocationLine.getTrackingNumber().getTrackingNumberSeq();
|
||||
}
|
||||
|
||||
throw new AxelorException(
|
||||
stockLocationLine,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOCATION_LINE_2),
|
||||
stockLocationLine.getProduct().getName(),
|
||||
stockLocationLine.getProduct().getCode(),
|
||||
trackingNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkIfEnoughStock(StockLocation stockLocation, Product product, BigDecimal qty)
|
||||
throws AxelorException {
|
||||
|
||||
if (!product.getStockManaged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
StockLocationLine stockLocationLine = this.getStockLocationLine(stockLocation, product);
|
||||
|
||||
if (stockLocationLine != null && stockLocationLine.getCurrentQty().compareTo(qty) < 0) {
|
||||
throw new AxelorException(
|
||||
stockLocationLine,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.LOCATION_LINE_1),
|
||||
stockLocationLine.getProduct().getName(),
|
||||
stockLocationLine.getProduct().getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine updateLocation(
|
||||
StockLocationLine stockLocationLine,
|
||||
Unit stockMoveLineUnit,
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
boolean current,
|
||||
boolean future,
|
||||
boolean isIncrement,
|
||||
LocalDate lastFutureStockMoveDate)
|
||||
throws AxelorException {
|
||||
|
||||
if (current) {
|
||||
if (isIncrement) {
|
||||
stockLocationLine.setCurrentQty(stockLocationLine.getCurrentQty().add(qty));
|
||||
} else {
|
||||
stockLocationLine.setCurrentQty(stockLocationLine.getCurrentQty().subtract(qty));
|
||||
}
|
||||
}
|
||||
if (future) {
|
||||
stockLocationLine.setFutureQty(computeFutureQty(stockLocationLine));
|
||||
stockLocationLine.setLastFutureStockMoveDate(lastFutureStockMoveDate);
|
||||
}
|
||||
|
||||
return stockLocationLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine getOrCreateStockLocationLine(
|
||||
StockLocation stockLocation, Product product) {
|
||||
|
||||
if (!product.getStockManaged()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StockLocationLine stockLocationLine = this.getStockLocationLine(stockLocation, product);
|
||||
|
||||
if (stockLocationLine == null) {
|
||||
stockLocationLine = this.createLocationLine(stockLocation, product);
|
||||
}
|
||||
|
||||
LOG.debug(
|
||||
"Récupération ligne de stock: Entrepot? {}, Produit? {}, Qté actuelle? {}, Qté future? {}, Date? {} ",
|
||||
stockLocationLine.getStockLocation().getName(),
|
||||
product.getCode(),
|
||||
stockLocationLine.getCurrentQty(),
|
||||
stockLocationLine.getFutureQty(),
|
||||
stockLocationLine.getLastFutureStockMoveDate());
|
||||
|
||||
return stockLocationLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine getOrCreateDetailLocationLine(
|
||||
StockLocation detailLocation, Product product, TrackingNumber trackingNumber) {
|
||||
|
||||
StockLocationLine detailLocationLine =
|
||||
this.getDetailLocationLine(detailLocation, product, trackingNumber);
|
||||
|
||||
if (detailLocationLine == null) {
|
||||
|
||||
detailLocationLine = this.createDetailLocationLine(detailLocation, product, trackingNumber);
|
||||
}
|
||||
|
||||
LOG.debug(
|
||||
"Récupération ligne de détail de stock: Entrepot? {}, Produit? {}, Qté actuelle? {}, Qté future? {}, Date? {}, Variante? {}, Num de suivi? {} ",
|
||||
detailLocationLine.getDetailsStockLocation().getName(),
|
||||
product.getCode(),
|
||||
detailLocationLine.getCurrentQty(),
|
||||
detailLocationLine.getFutureQty(),
|
||||
detailLocationLine.getLastFutureStockMoveDate(),
|
||||
detailLocationLine.getTrackingNumber());
|
||||
|
||||
return detailLocationLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine getStockLocationLine(StockLocation stockLocation, Product product) {
|
||||
|
||||
if (product == null || !product.getStockManaged()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stockLocationLineRepo
|
||||
.all()
|
||||
.filter("self.stockLocation.id = :_stockLocationId " + "AND self.product.id = :_productId")
|
||||
.bind("_stockLocationId", stockLocation.getId())
|
||||
.bind("_productId", product.getId())
|
||||
.fetchOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockLocationLine> getStockLocationLines(Product product) {
|
||||
|
||||
if (product != null && !product.getStockManaged()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stockLocationLineRepo
|
||||
.all()
|
||||
.filter("self.product.id = :_productId")
|
||||
.bind("_productId", product.getId())
|
||||
.fetch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine getDetailLocationLine(
|
||||
StockLocation stockLocation, Product product, TrackingNumber trackingNumber) {
|
||||
|
||||
return stockLocationLineRepo
|
||||
.all()
|
||||
.filter(
|
||||
"self.detailsStockLocation.id = :_stockLocationId "
|
||||
+ "AND self.product.id = :_productId "
|
||||
+ "AND self.trackingNumber.id = :_trackingNumberId "
|
||||
)
|
||||
.bind("_stockLocationId", stockLocation.getId())
|
||||
.bind("_productId", product.getId())
|
||||
.bind("_trackingNumberId", trackingNumber.getId())
|
||||
.fetchOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine createLocationLine(StockLocation stockLocation, Product product) {
|
||||
|
||||
LOG.debug(
|
||||
"Création d'une ligne de stock : Entrepot? {}, Produit? {} ",
|
||||
stockLocation.getName(),
|
||||
product.getCode());
|
||||
|
||||
StockLocationLine stockLocationLine = new StockLocationLine();
|
||||
|
||||
stockLocationLine.setStockLocation(stockLocation);
|
||||
stockLocation.addStockLocationLineListItem(stockLocationLine);
|
||||
stockLocationLine.setProduct(product);
|
||||
stockLocationLine.setUnit(product.getUnit());
|
||||
stockLocationLine.setCurrentQty(BigDecimal.ZERO);
|
||||
stockLocationLine.setFutureQty(BigDecimal.ZERO);
|
||||
stockLocationLine.setConformitySelect(4); // Quarantine
|
||||
|
||||
return stockLocationLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine createDetailLocationLine(
|
||||
StockLocation stockLocation, Product product, TrackingNumber trackingNumber) {
|
||||
|
||||
LOG.debug(
|
||||
"**** Création d'une ligne de détail de stock : Entrepot? {}, Produit? {}, Num de suivi? {} ",
|
||||
stockLocation.getName(),
|
||||
product.getCode(),
|
||||
trackingNumber.getTrackingNumberSeq());
|
||||
|
||||
StockLocationLine detailLocationLine = new StockLocationLine();
|
||||
|
||||
detailLocationLine.setDetailsStockLocation(stockLocation);
|
||||
stockLocation.addDetailsStockLocationLineListItem(detailLocationLine);
|
||||
detailLocationLine.setProduct(product);
|
||||
detailLocationLine.setUnit(product.getUnit());
|
||||
detailLocationLine.setCurrentQty(BigDecimal.ZERO);
|
||||
detailLocationLine.setFutureQty(BigDecimal.ZERO);
|
||||
detailLocationLine.setTrackingNumber(trackingNumber);
|
||||
|
||||
return detailLocationLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getAvailableQty(StockLocation stockLocation, Product product) {
|
||||
StockLocationLine stockLocationLine = getStockLocationLine(stockLocation, product);
|
||||
BigDecimal availableQty = BigDecimal.ZERO;
|
||||
if (stockLocationLine != null) {
|
||||
availableQty = stockLocationLine.getCurrentQty();
|
||||
}
|
||||
return availableQty;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void updateStockLocationFromProduct(StockLocationLine stockLocationLine, Product product)
|
||||
throws AxelorException {
|
||||
|
||||
stockLocationLine = this.updateLocationFromProduct(stockLocationLine, product);
|
||||
stockLocationLineRepo.save(stockLocationLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocationLine updateLocationFromProduct(
|
||||
StockLocationLine stockLocationLine, Product product) throws AxelorException {
|
||||
Unit productUnit = product.getUnit();
|
||||
Unit stockLocationUnit = stockLocationLine.getUnit();
|
||||
|
||||
if (productUnit != null && !productUnit.equals(stockLocationUnit)) {
|
||||
int scale = Beans.get(AppBaseService.class).getNbDecimalDigitForUnitPrice();
|
||||
BigDecimal oldQty = stockLocationLine.getCurrentQty();
|
||||
BigDecimal oldAvgPrice = stockLocationLine.getAvgPrice();
|
||||
UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
|
||||
BigDecimal currentQty =
|
||||
unitConversionService.convert(
|
||||
stockLocationUnit,
|
||||
productUnit,
|
||||
stockLocationLine.getCurrentQty(),
|
||||
stockLocationLine.getCurrentQty().scale(),
|
||||
product);
|
||||
stockLocationLine.setCurrentQty(currentQty);
|
||||
|
||||
stockLocationLine.setUnit(product.getUnit());
|
||||
stockLocationLine.setFutureQty(computeFutureQty(stockLocationLine));
|
||||
|
||||
BigDecimal avgQty = BigDecimal.ZERO;
|
||||
if (currentQty.compareTo(BigDecimal.ZERO) != 0) {
|
||||
avgQty = oldQty.divide(currentQty, scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
BigDecimal newAvgPrice = oldAvgPrice.multiply(avgQty);
|
||||
updateWap(stockLocationLine, newAvgPrice.setScale(scale, RoundingMode.HALF_UP));
|
||||
}
|
||||
return stockLocationLine;
|
||||
}
|
||||
|
||||
protected static final String STOCK_MOVE_LINE_FILTER =
|
||||
"(self.stockMove.archived IS NULL OR self.archived IS FALSE) "
|
||||
+ "AND self.stockMove.statusSelect = :planned "
|
||||
+ "AND self.product.id = :productId ";
|
||||
|
||||
@Override
|
||||
public BigDecimal computeFutureQty(StockLocationLine stockLocationLine) throws AxelorException {
|
||||
// future quantity is current quantity minus planned outgoing stock move lines plus planned
|
||||
// incoming stock move lines.
|
||||
|
||||
UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
Product product = stockLocationLine.getProduct();
|
||||
|
||||
BigDecimal futureQty = stockLocationLine.getCurrentQty();
|
||||
|
||||
List<StockMoveLine> incomingStockMoveLineList =
|
||||
findIncomingPlannedStockMoveLines(stockLocationLine);
|
||||
List<StockMoveLine> outgoingStockMoveLineList =
|
||||
findOutgoingPlannedStockMoveLines(stockLocationLine);
|
||||
|
||||
for (StockMoveLine incomingStockMoveLine : incomingStockMoveLineList) {
|
||||
BigDecimal qtyToAdd =
|
||||
unitConversionService.convert(
|
||||
incomingStockMoveLine.getUnit(),
|
||||
stockLocationLine.getUnit(),
|
||||
incomingStockMoveLine.getRealQty(),
|
||||
incomingStockMoveLine.getRealQty().scale(),
|
||||
product);
|
||||
futureQty = futureQty.add(qtyToAdd);
|
||||
}
|
||||
|
||||
for (StockMoveLine outgoingStockMoveLine : outgoingStockMoveLineList) {
|
||||
BigDecimal qtyToSubtract =
|
||||
unitConversionService.convert(
|
||||
outgoingStockMoveLine.getUnit(),
|
||||
stockLocationLine.getUnit(),
|
||||
outgoingStockMoveLine.getRealQty(),
|
||||
outgoingStockMoveLine.getRealQty().scale(),
|
||||
product);
|
||||
futureQty = futureQty.subtract(qtyToSubtract);
|
||||
}
|
||||
|
||||
return futureQty;
|
||||
}
|
||||
|
||||
protected List<StockMoveLine> findIncomingPlannedStockMoveLines(
|
||||
StockLocationLine stockLocationLine) {
|
||||
boolean isDetailsStockLocationLine = stockLocationLine.getDetailsStockLocation() != null;
|
||||
String incomingStockMoveLineFilter =
|
||||
STOCK_MOVE_LINE_FILTER + "AND self.stockMove.toStockLocation.id = :stockLocationId";
|
||||
if (isDetailsStockLocationLine) {
|
||||
incomingStockMoveLineFilter =
|
||||
incomingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId";
|
||||
}
|
||||
Query<StockMoveLine> stockMoveLineQuery =
|
||||
stockMoveLineRepository
|
||||
.all()
|
||||
.filter(incomingStockMoveLineFilter)
|
||||
.bind("planned", StockMoveRepository.STATUS_PLANNED)
|
||||
.bind("productId", stockLocationLine.getProduct().getId());
|
||||
if (isDetailsStockLocationLine) {
|
||||
stockMoveLineQuery
|
||||
.bind("stockLocationId", stockLocationLine.getDetailsStockLocation().getId())
|
||||
.bind("trackingNumberId", stockLocationLine.getTrackingNumber().getId());
|
||||
} else {
|
||||
stockMoveLineQuery.bind("stockLocationId", stockLocationLine.getStockLocation().getId());
|
||||
}
|
||||
return stockMoveLineQuery.fetch();
|
||||
}
|
||||
|
||||
protected List<StockMoveLine> findOutgoingPlannedStockMoveLines(
|
||||
StockLocationLine stockLocationLine) {
|
||||
boolean isDetailsStockLocationLine = stockLocationLine.getDetailsStockLocation() != null;
|
||||
String outgoingStockMoveLineFilter =
|
||||
STOCK_MOVE_LINE_FILTER + "AND self.stockMove.fromStockLocation.id = :stockLocationId";
|
||||
if (isDetailsStockLocationLine) {
|
||||
outgoingStockMoveLineFilter =
|
||||
outgoingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId";
|
||||
}
|
||||
Query<StockMoveLine> stockMoveLineQuery =
|
||||
stockMoveLineRepository
|
||||
.all()
|
||||
.filter(outgoingStockMoveLineFilter)
|
||||
.bind("planned", StockMoveRepository.STATUS_PLANNED)
|
||||
.bind("productId", stockLocationLine.getProduct().getId());
|
||||
|
||||
if (isDetailsStockLocationLine) {
|
||||
stockMoveLineQuery
|
||||
.bind("stockLocationId", stockLocationLine.getDetailsStockLocation().getId())
|
||||
.bind("trackingNumberId", stockLocationLine.getTrackingNumber().getId());
|
||||
} else {
|
||||
stockMoveLineQuery.bind("stockLocationId", stockLocationLine.getStockLocation().getId());
|
||||
}
|
||||
return stockMoveLineQuery.fetch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStockLocationLineListForAProduct(
|
||||
Long productId, Long companyId, Long stockLocationId) {
|
||||
|
||||
String query =
|
||||
"self.product.id = "
|
||||
+ productId
|
||||
+ " AND self.stockLocation.typeSelect != "
|
||||
+ StockLocationRepository.TYPE_VIRTUAL;
|
||||
|
||||
if (companyId != 0L) {
|
||||
query += " AND self.stockLocation.company.id = " + companyId;
|
||||
if (stockLocationId != 0L) {
|
||||
StockLocation stockLocation =
|
||||
Beans.get(StockLocationRepository.class).find(stockLocationId);
|
||||
List<StockLocation> stockLocationList =
|
||||
Beans.get(StockLocationService.class)
|
||||
.getAllLocationAndSubLocation(stockLocation, false);
|
||||
if (!stockLocationList.isEmpty() && stockLocation.getCompany().getId().equals(companyId)) {
|
||||
query +=
|
||||
" AND self.stockLocation.id IN ("
|
||||
+ StringTool.getIdListString(stockLocationList)
|
||||
+ ") ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAvailableStockForAProduct(Long productId, Long companyId, Long stockLocationId) {
|
||||
String query = this.getStockLocationLineListForAProduct(productId, companyId, stockLocationId);
|
||||
query +=
|
||||
" AND (self.currentQty != 0 OR self.futureQty != 0) "
|
||||
+ " AND (self.stockLocation.isNotInCalculStock = false OR self.stockLocation.isNotInCalculStock IS NULL)";
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestedReservedQtyForAProduct(
|
||||
Long productId, Long companyId, Long stockLocationId) {
|
||||
String query = this.getStockLocationLineListForAProduct(productId, companyId, stockLocationId);
|
||||
query += " AND self.requestedReservedQty > 0";
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWap(StockLocationLine stockLocationLine, BigDecimal wap) {
|
||||
updateWap(stockLocationLine, wap, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWap(
|
||||
StockLocationLine stockLocationLine, BigDecimal wap, StockMoveLine stockMoveLine) {
|
||||
stockLocationLine.setAvgPrice(wap);
|
||||
wapHistoryRepo.save(
|
||||
new WapHistory(
|
||||
stockLocationLine,
|
||||
appBaseService.getTodayDate(),
|
||||
wap,
|
||||
stockLocationLine.getCurrentQty(),
|
||||
stockLocationLine.getUnit(),
|
||||
stockMoveLine));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getTrackingNumberAvailableQty(
|
||||
StockLocation stockLocation, TrackingNumber trackingNumber) {
|
||||
StockLocationLine detailStockLocationLine =
|
||||
getDetailLocationLine(stockLocation, trackingNumber.getProduct(), trackingNumber);
|
||||
|
||||
BigDecimal availableQty = BigDecimal.ZERO;
|
||||
|
||||
if (detailStockLocationLine != null) {
|
||||
availableQty = detailStockLocationLine.getCurrentQty();
|
||||
}
|
||||
return availableQty;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.PartnerStockSettings;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.repo.PartnerStockSettingsRepository;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.List;
|
||||
|
||||
public class StockLocationSaveService {
|
||||
|
||||
/**
|
||||
* Remove default stock locations in partner that are not linked with this stock location anymore.
|
||||
*
|
||||
* @param defaultStockLocation
|
||||
*/
|
||||
@Transactional
|
||||
public void removeForbiddenDefaultStockLocation(StockLocation defaultStockLocation) {
|
||||
Partner currentPartner = defaultStockLocation.getPartner();
|
||||
Company currentCompany = defaultStockLocation.getCompany();
|
||||
Long partnerId = currentPartner != null ? currentPartner.getId() : 0L;
|
||||
Long companyId = currentCompany != null ? currentCompany.getId() : 0L;
|
||||
PartnerStockSettingsRepository partnerStockSettingsRepo =
|
||||
Beans.get(PartnerStockSettingsRepository.class);
|
||||
List<PartnerStockSettings> partnerStockSettingsToRemove =
|
||||
partnerStockSettingsRepo
|
||||
.all()
|
||||
.filter(
|
||||
"(self.partner.id != :partnerId OR self.company.id != :companyId)"
|
||||
+ " AND (self.defaultStockLocation.id = :stockLocationId)")
|
||||
.bind("partnerId", partnerId)
|
||||
.bind("companyId", companyId)
|
||||
.bind("stockLocationId", defaultStockLocation.getId())
|
||||
.fetch();
|
||||
for (PartnerStockSettings partnerStockSettings : partnerStockSettingsToRemove) {
|
||||
Partner partnerToClean = partnerStockSettings.getPartner();
|
||||
partnerToClean.removePartnerStockSettingsListItem(partnerStockSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.meta.CallMethod;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface StockLocationService {
|
||||
|
||||
/**
|
||||
* Get default receipt location for the given company.
|
||||
*
|
||||
* @param company
|
||||
* @return the default stock location if found, null if there was an exception or if the default
|
||||
* location is empty
|
||||
*/
|
||||
StockLocation getDefaultReceiptStockLocation(Company company);
|
||||
|
||||
/**
|
||||
* Get default pickup location for the given company.
|
||||
*
|
||||
* @param company
|
||||
* @return the default stock location if found, null if there was an exception or if the default
|
||||
* location is empty
|
||||
*/
|
||||
StockLocation getPickupDefaultStockLocation(Company company);
|
||||
|
||||
public BigDecimal getQty(Long productId, Long locationId, Long companyId, String qtyType)
|
||||
throws AxelorException;
|
||||
|
||||
@CallMethod
|
||||
public BigDecimal getRealQty(Long productId, Long locationId, Long companyId)
|
||||
throws AxelorException;
|
||||
|
||||
@CallMethod
|
||||
public BigDecimal getFutureQty(Long productId, Long locationId, Long companyId)
|
||||
throws AxelorException;
|
||||
|
||||
@CallMethod
|
||||
public List<Long> getBadStockLocationLineId();
|
||||
|
||||
@CallMethod
|
||||
public Set<Long> getContentStockLocationIds(StockLocation stockLocation);
|
||||
|
||||
public List<StockLocation> getAllLocationAndSubLocation(
|
||||
StockLocation stockLocation, boolean isVirtualInclude);
|
||||
|
||||
public BigDecimal getStockLocationValue(StockLocation stockLocation);
|
||||
|
||||
public List<Long> getAllLocationAndSubLocationId(
|
||||
StockLocation stockLocation, boolean isVirtualInclude);
|
||||
|
||||
public boolean isConfigMissing(StockLocation stockLocation, int printType);
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockRules;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockRulesRepository;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.filter.Filter;
|
||||
import com.axelor.rpc.filter.JPQLFilter;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.Query;
|
||||
|
||||
@RequestScoped
|
||||
public class StockLocationServiceImpl implements StockLocationService {
|
||||
|
||||
protected StockLocationRepository stockLocationRepo;
|
||||
|
||||
protected StockLocationLineService stockLocationLineService;
|
||||
|
||||
protected ProductRepository productRepo;
|
||||
|
||||
protected Set<Long> locationIdSet = new HashSet<>();
|
||||
|
||||
@Inject
|
||||
public StockLocationServiceImpl(
|
||||
StockLocationRepository stockLocationRepo,
|
||||
StockLocationLineService stockLocationLineService,
|
||||
ProductRepository productRepo) {
|
||||
this.stockLocationRepo = stockLocationRepo;
|
||||
this.stockLocationLineService = stockLocationLineService;
|
||||
this.productRepo = productRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocation getDefaultReceiptStockLocation(Company company) {
|
||||
try {
|
||||
StockConfigService stockConfigService = Beans.get(StockConfigService.class);
|
||||
StockConfig stockConfig = stockConfigService.getStockConfig(company);
|
||||
return stockConfigService.getReceiptDefaultStockLocation(stockConfig);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockLocation getPickupDefaultStockLocation(Company company) {
|
||||
try {
|
||||
StockConfigService stockConfigService = Beans.get(StockConfigService.class);
|
||||
StockConfig stockConfig = stockConfigService.getStockConfig(company);
|
||||
return stockConfigService.getPickupDefaultStockLocation(stockConfig);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<StockLocation> getNonVirtualStockLocations(Long companyId) {
|
||||
List<Filter> queryFilter =
|
||||
Lists.newArrayList(new JPQLFilter("self.typeSelect != :stockLocationTypSelect"));
|
||||
if (companyId != null && companyId != 0L) {
|
||||
queryFilter.add(new JPQLFilter("self.company.id = :companyId "));
|
||||
}
|
||||
return Filter.and(queryFilter)
|
||||
.build(StockLocation.class)
|
||||
.bind("stockLocationTypSelect", StockLocationRepository.TYPE_VIRTUAL)
|
||||
.bind("companyId", companyId)
|
||||
.fetch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getQty(Long productId, Long locationId, Long companyId, String qtyType)
|
||||
throws AxelorException {
|
||||
if (productId != null) {
|
||||
Product product = productRepo.find(productId);
|
||||
Unit productUnit = product.getUnit();
|
||||
UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
|
||||
if (locationId == null || locationId == 0L) {
|
||||
List<StockLocation> stockLocations = getNonVirtualStockLocations(companyId);
|
||||
if (!stockLocations.isEmpty()) {
|
||||
BigDecimal qty = BigDecimal.ZERO;
|
||||
for (StockLocation stockLocation : stockLocations) {
|
||||
StockLocationLine stockLocationLine =
|
||||
stockLocationLineService.getOrCreateStockLocationLine(
|
||||
stockLocationRepo.find(stockLocation.getId()), productRepo.find(productId));
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
Unit stockLocationLineUnit = stockLocationLine.getUnit();
|
||||
qty =
|
||||
qty.add(
|
||||
qtyType.equals("real")
|
||||
? stockLocationLine.getCurrentQty()
|
||||
: stockLocationLine.getFutureQty());
|
||||
|
||||
if (productUnit != null && !productUnit.equals(stockLocationLineUnit)) {
|
||||
qty =
|
||||
unitConversionService.convert(
|
||||
stockLocationLineUnit, productUnit, qty, qty.scale(), product);
|
||||
}
|
||||
}
|
||||
}
|
||||
return qty;
|
||||
}
|
||||
} else {
|
||||
StockLocationLine stockLocationLine =
|
||||
stockLocationLineService.getOrCreateStockLocationLine(
|
||||
stockLocationRepo.find(locationId), productRepo.find(productId));
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
Unit stockLocationLineUnit = stockLocationLine.getUnit();
|
||||
BigDecimal qty = BigDecimal.ZERO;
|
||||
|
||||
qty =
|
||||
qtyType.equals("real")
|
||||
? stockLocationLine.getCurrentQty()
|
||||
: stockLocationLine.getFutureQty();
|
||||
|
||||
if (productUnit != null && !productUnit.equals(stockLocationLineUnit)) {
|
||||
qty =
|
||||
unitConversionService.convert(
|
||||
stockLocationLineUnit, productUnit, qty, qty.scale(), product);
|
||||
}
|
||||
return qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getRealQty(Long productId, Long locationId, Long companyId)
|
||||
throws AxelorException {
|
||||
return getQty(productId, locationId, companyId, "real");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getFutureQty(Long productId, Long locationId, Long companyId)
|
||||
throws AxelorException {
|
||||
return getQty(productId, locationId, companyId, "future");
|
||||
}
|
||||
|
||||
public List<Long> getBadStockLocationLineId() {
|
||||
|
||||
List<StockLocationLine> stockLocationLineList =
|
||||
Beans.get(StockLocationLineRepository.class)
|
||||
.all()
|
||||
.filter("self.stockLocation.typeSelect = 1 OR self.stockLocation.typeSelect = 2")
|
||||
.fetch();
|
||||
|
||||
List<Long> idList = new ArrayList<>();
|
||||
|
||||
StockRulesRepository stockRulesRepository = Beans.get(StockRulesRepository.class);
|
||||
|
||||
for (StockLocationLine stockLocationLine : stockLocationLineList) {
|
||||
StockRules stockRules =
|
||||
stockRulesRepository
|
||||
.all()
|
||||
.filter(
|
||||
"self.stockLocation = ?1 AND self.product = ?2",
|
||||
stockLocationLine.getStockLocation(),
|
||||
stockLocationLine.getProduct())
|
||||
.fetchOne();
|
||||
if (stockRules != null
|
||||
&& stockLocationLine.getFutureQty().compareTo(stockRules.getMinQty()) < 0) {
|
||||
idList.add(stockLocationLine.getId());
|
||||
}
|
||||
}
|
||||
|
||||
if (idList.isEmpty()) {
|
||||
idList.add(0L);
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getContentStockLocationIds(StockLocation stockLocation) {
|
||||
locationIdSet = new HashSet<>();
|
||||
if (stockLocation != null) {
|
||||
List<StockLocation> stockLocations = getAllLocationAndSubLocation(stockLocation, true);
|
||||
for (StockLocation item : stockLocations) {
|
||||
locationIdSet.add(item.getId());
|
||||
}
|
||||
} else {
|
||||
locationIdSet.add(0L);
|
||||
}
|
||||
|
||||
return locationIdSet;
|
||||
}
|
||||
|
||||
public List<StockLocation> getAllLocationAndSubLocation(
|
||||
StockLocation stockLocation, boolean isVirtualInclude) {
|
||||
|
||||
List<StockLocation> resultList = new ArrayList<>();
|
||||
if (stockLocation == null) {
|
||||
return resultList;
|
||||
}
|
||||
if (isVirtualInclude) {
|
||||
for (StockLocation subLocation :
|
||||
stockLocationRepo
|
||||
.all()
|
||||
.filter("self.parentStockLocation.id = :stockLocationId")
|
||||
.bind("stockLocationId", stockLocation.getId())
|
||||
.fetch()) {
|
||||
|
||||
resultList.addAll(this.getAllLocationAndSubLocation(subLocation, isVirtualInclude));
|
||||
}
|
||||
} else {
|
||||
for (StockLocation subLocation :
|
||||
stockLocationRepo
|
||||
.all()
|
||||
.filter(
|
||||
"self.parentStockLocation.id = :stockLocationId AND self.typeSelect != :virtual")
|
||||
.bind("stockLocationId", stockLocation.getId())
|
||||
.bind("virtual", StockLocationRepository.TYPE_VIRTUAL)
|
||||
.fetch()) {
|
||||
|
||||
resultList.addAll(this.getAllLocationAndSubLocation(subLocation, isVirtualInclude));
|
||||
}
|
||||
}
|
||||
resultList.add(stockLocation);
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getStockLocationValue(StockLocation stockLocation) {
|
||||
|
||||
Query query =
|
||||
JPA.em()
|
||||
.createQuery(
|
||||
"SELECT SUM( self.currentQty * CASE WHEN (location.company.stockConfig.stockValuationTypeSelect = 1) THEN "
|
||||
+ "(self.avgPrice) WHEN (location.company.stockConfig.stockValuationTypeSelect = 2) THEN "
|
||||
+ "CASE WHEN (self.product.costTypeSelect = 3) THEN (self.avgPrice) ELSE (self.product.costPrice) END "
|
||||
+ "WHEN (location.company.stockConfig.stockValuationTypeSelect = 3) THEN "
|
||||
+ "(self.product.salePrice) ELSE (self.avgPrice) END ) AS value "
|
||||
+ "FROM StockLocationLine AS self "
|
||||
+ "LEFT JOIN StockLocation AS location "
|
||||
+ "ON location.id= self.stockLocation "
|
||||
+ "WHERE self.stockLocation.id =:id");
|
||||
query.setParameter("id", stockLocation.getId());
|
||||
|
||||
List<?> result = query.getResultList();
|
||||
return (result.get(0) == null || ((BigDecimal) result.get(0)).signum() == 0)
|
||||
? BigDecimal.ZERO
|
||||
: ((BigDecimal) result.get(0)).setScale(2, BigDecimal.ROUND_HALF_EVEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getAllLocationAndSubLocationId(
|
||||
StockLocation stockLocation, boolean isVirtualInclude) {
|
||||
List<StockLocation> stockLocationList =
|
||||
getAllLocationAndSubLocation(stockLocation, isVirtualInclude);
|
||||
List<Long> stockLocationListId = null;
|
||||
if (stockLocationList != null) {
|
||||
stockLocationListId =
|
||||
stockLocationList.stream().map(StockLocation::getId).collect(Collectors.toList());
|
||||
}
|
||||
return stockLocationListId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigMissing(StockLocation stockLocation, int printType) {
|
||||
|
||||
StockConfig stockConfig = stockLocation.getCompany().getStockConfig();
|
||||
return printType == StockLocationRepository.PRINT_TYPE_LOCATION_FINANCIAL_DATA
|
||||
&& (stockConfig == null
|
||||
|| (!stockConfig.getIsDisplayAccountingValueInPrinting()
|
||||
&& !stockConfig.getIsDisplayAgPriceInPrinting()
|
||||
&& !stockConfig.getIsDisplaySaleValueInPrinting()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.stock.db.InternalTrackingNumber;
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.TrackingNumberConfiguration;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
public interface StockMoveLineService {
|
||||
|
||||
public static final int TYPE_NULL = 0;
|
||||
public static final int TYPE_SALES = 1;
|
||||
public static final int TYPE_PURCHASES = 2;
|
||||
public static final int TYPE_OUT_PRODUCTIONS = 3;
|
||||
public static final int TYPE_IN_PRODUCTIONS = 4;
|
||||
public static final int TYPE_WASTE_PRODUCTIONS = 5;
|
||||
|
||||
/**
|
||||
* Méthode générique permettant de créer une ligne de mouvement de stock en gérant les numéros de
|
||||
* suivi en fonction du type d'opération.
|
||||
*
|
||||
* @param product le produit
|
||||
* @param quantity la quantité
|
||||
* @param parent le StockMove parent
|
||||
* @param type 1 : Sales 2 : Purchases 3 : Productions
|
||||
* @return l'objet StockMoveLine
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public StockMoveLine createStockMoveLine(
|
||||
Product product,
|
||||
String productName,
|
||||
String description,
|
||||
BigDecimal quantity,
|
||||
BigDecimal unitPrice,
|
||||
BigDecimal companyUnitPriceUntaxed,
|
||||
Unit unit,
|
||||
StockMove stockMove,
|
||||
int type,
|
||||
boolean taxed,
|
||||
BigDecimal taxRate)
|
||||
throws AxelorException;
|
||||
|
||||
public void generateTrackingNumber(
|
||||
StockMoveLine stockMoveLine,
|
||||
TrackingNumberConfiguration trackingNumberConfiguration,
|
||||
Product product,
|
||||
BigDecimal qtyByTracking)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Allow the creation of a stock move line managing tracking numbers with operation type.
|
||||
*
|
||||
* @param product the line product
|
||||
* @param productName the line product name
|
||||
* @param description description of the line
|
||||
* @param quantity the line quantity
|
||||
* @param unitPriceUntaxed price untaxed of the line
|
||||
* @param unitPriceTaxed price taxed of the line
|
||||
* @param unit Unit of the line
|
||||
* @param stockMove parent stock move
|
||||
* @param trackingNumber tracking number used in the line
|
||||
* @return the created stock move line
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public StockMoveLine createStockMoveLine(
|
||||
Product product,
|
||||
String productName,
|
||||
String description,
|
||||
BigDecimal quantity,
|
||||
BigDecimal unitPriceUntaxed,
|
||||
BigDecimal unitPriceTaxed,
|
||||
BigDecimal companyUnitPriceUntaxed,
|
||||
Unit unit,
|
||||
StockMove stockMove,
|
||||
TrackingNumber trackingNumber)
|
||||
throws AxelorException;
|
||||
|
||||
public StockMoveLine assignOrGenerateTrackingNumber(
|
||||
StockMoveLine stockMoveLine,
|
||||
StockMove stockMove,
|
||||
Product product,
|
||||
TrackingNumberConfiguration trackingNumberConfiguration,
|
||||
int type)
|
||||
throws AxelorException;
|
||||
|
||||
public void checkTrackingNumber(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public void assignTrackingNumber(
|
||||
StockMoveLine stockMoveLine, Product product, StockLocation stockLocation)
|
||||
throws AxelorException;
|
||||
|
||||
public List<? extends StockLocationLine> getStockLocationLines(
|
||||
Product product, StockLocation stockLocation) throws AxelorException;
|
||||
|
||||
public StockMoveLine splitStockMoveLine(
|
||||
StockMoveLine stockMoveLine, BigDecimal qty, TrackingNumber trackingNumber)
|
||||
throws AxelorException;
|
||||
|
||||
public void updateLocations(
|
||||
StockLocation fromStockLocation,
|
||||
StockLocation toStockLocation,
|
||||
int fromStatus,
|
||||
int toStatus,
|
||||
List<StockMoveLine> stockMoveLineList,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
boolean realQty)
|
||||
throws AxelorException;
|
||||
|
||||
public void updateLocations(
|
||||
StockMoveLine stockMoveLine,
|
||||
StockLocation fromStockLocation,
|
||||
StockLocation toStockLocation,
|
||||
Product product,
|
||||
BigDecimal qty,
|
||||
int fromStatus,
|
||||
int toStatus,
|
||||
LocalDate lastFutureStockMoveDate,
|
||||
TrackingNumber trackingNumber,
|
||||
InternalTrackingNumber internalTrackingNumber)
|
||||
throws AxelorException;
|
||||
|
||||
public void updateAveragePriceLocationLine(
|
||||
StockLocation stockLocation, StockMoveLine stockMoveLine, int fromStatus, int toStatus)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check in the product if the stock move line needs to have a conformity selected.
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @param stockMove
|
||||
* @throws AxelorException if the stock move line needs to have a conformity selected and it is
|
||||
* not selected.
|
||||
*/
|
||||
public void checkConformitySelection(StockMoveLine stockMoveLine, StockMove stockMove)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check for all lines in the stock move if it needs to have a conformity selected.
|
||||
*
|
||||
* @param stockMove
|
||||
* @throws AxelorException if one or more stock move line needs to have a conformity selected and
|
||||
* it is not selected.
|
||||
*/
|
||||
public void checkConformitySelection(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check for warranty dates and expiration dates.
|
||||
*
|
||||
* @param stockMove
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void checkExpirationDates(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Return unit found in stock move line, or if the unit is empty, take the unit from the product.
|
||||
*/
|
||||
Unit getStockUnit(StockMoveLine stockMoveLine);
|
||||
|
||||
public StockMoveLine compute(StockMoveLine stockMoveLine, StockMove stockMove)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Store customs code information on each stock move line from its product.
|
||||
*
|
||||
* @param stockMoveLineList List of StockMoveLines on which to operate
|
||||
*/
|
||||
public void storeCustomsCodes(List<StockMoveLine> stockMoveLineList);
|
||||
|
||||
/**
|
||||
* Check whether a stock move line is fully spread over logistical form lines.
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @return
|
||||
*/
|
||||
boolean computeFullySpreadOverLogisticalFormLinesFlag(StockMoveLine stockMoveLine);
|
||||
|
||||
/**
|
||||
* Get the quantity spreadable over logistical form lines.
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @return
|
||||
*/
|
||||
BigDecimal computeSpreadableQtyOverLogisticalFormLines(StockMoveLine stockMoveLine);
|
||||
|
||||
/**
|
||||
* Get the quantity spreadable over logistical form lines. Take into account the lines from the
|
||||
* specified logistical form.
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @param logisticalForm
|
||||
* @return
|
||||
*/
|
||||
BigDecimal computeSpreadableQtyOverLogisticalFormLines(
|
||||
StockMoveLine stockMoveLine, LogisticalForm logisticalForm);
|
||||
|
||||
/**
|
||||
* Set product information.
|
||||
*
|
||||
* @param stockMove
|
||||
* @param stockMoveLine
|
||||
* @param company
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void setProductInfo(StockMove stockMove, StockMoveLine stockMoveLine, Company company)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check whether mass information is required.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
boolean checkMassesRequired(StockMove stockMove, StockMoveLine stockMoveLine);
|
||||
|
||||
@Transactional
|
||||
public void splitStockMoveLineByTrackingNumber(
|
||||
StockMoveLine stockMoveLine, List<LinkedHashMap<String, Object>> trackingNumbers);
|
||||
|
||||
/**
|
||||
* set the available quantity of product in a given location.
|
||||
*
|
||||
* @param stockMoveLine
|
||||
* @param stockLocation
|
||||
* @return
|
||||
*/
|
||||
public void updateAvailableQty(StockMoveLine stockMoveLine, StockLocation stockLocation);
|
||||
|
||||
public String createDomainForProduct(StockMoveLine stockMoveLine, StockMove stockMove);
|
||||
|
||||
public void setAvailableStatus(StockMoveLine stockMoveLine);
|
||||
|
||||
public List<TrackingNumber> getAvailableTrackingNumbers(
|
||||
StockMoveLine stockMoveLine, StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Fill realize avg price in stock move line. This method is called on realize, to save avg price
|
||||
* at the time of realization.
|
||||
*
|
||||
* @param stockMoveLine a stock move line being realized.
|
||||
*/
|
||||
public void fillRealizeWapPrice(StockMoveLine stockMoveLine);
|
||||
}
|
||||
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Address;
|
||||
import com.axelor.apps.base.db.CancelReason;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.FreightCarrierMode;
|
||||
import com.axelor.apps.stock.db.Incoterm;
|
||||
import com.axelor.apps.stock.db.ShipmentMode;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface StockMoveService {
|
||||
|
||||
/**
|
||||
* Generic method to create any stock move
|
||||
*
|
||||
* @param fromAddress
|
||||
* @param toAddress
|
||||
* @param company
|
||||
* @param clientPartner
|
||||
* @param fromStockLocation
|
||||
* @param toStockLocation
|
||||
* @param realDate
|
||||
* @param estimatedDate
|
||||
* @param description
|
||||
* @param shipmentMode
|
||||
* @param freightCarrierMode
|
||||
* @param carrierPartner
|
||||
* @param forwarderPartner
|
||||
* @param incoterm
|
||||
* @param typeSelect
|
||||
* @return
|
||||
* @throws AxelorException No Stock move sequence defined
|
||||
*/
|
||||
public StockMove createStockMove(
|
||||
Address fromAddress,
|
||||
Address toAddress,
|
||||
Company company,
|
||||
Partner clientPartner,
|
||||
StockLocation fromStockLocation,
|
||||
StockLocation toStockLocation,
|
||||
LocalDate realDate,
|
||||
LocalDate estimatedDate,
|
||||
String note,
|
||||
ShipmentMode shipmentMode,
|
||||
FreightCarrierMode freightCarrierMode,
|
||||
Partner carrierPartner,
|
||||
Partner forwarderPartner,
|
||||
Incoterm incoterm,
|
||||
int typeSelect)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Generic method to create any stock move for internal stock move (without partner information)
|
||||
*
|
||||
* @param clientPartner
|
||||
* @param shipmentMode
|
||||
* @param freightCarrierMode
|
||||
* @param carrierPartner
|
||||
* @param forwarderPartner
|
||||
* @param incoterm
|
||||
* @param fromAddress
|
||||
* @param toAddress
|
||||
* @param company
|
||||
* @param fromStockLocation
|
||||
* @param toStockLocation
|
||||
* @param realDate
|
||||
* @param estimatedDate
|
||||
* @param description
|
||||
* @param typeSelect
|
||||
* @return
|
||||
* @throws AxelorException No Stock move sequence defined
|
||||
*/
|
||||
public StockMove createStockMove(
|
||||
Address fromAddress,
|
||||
Address toAddress,
|
||||
Company company,
|
||||
StockLocation fromStockLocation,
|
||||
StockLocation toStockLocation,
|
||||
LocalDate realDate,
|
||||
LocalDate estimatedDate,
|
||||
String note,
|
||||
int typeSelect)
|
||||
throws AxelorException;
|
||||
|
||||
public void validate(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public void goBackToDraft(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public void plan(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public String realize(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public String realize(StockMove stockMove, boolean check) throws AxelorException;
|
||||
|
||||
public boolean mustBeSplit(List<StockMoveLine> stockMoveLineList);
|
||||
|
||||
public Optional<StockMove> copyAndSplitStockMove(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public Optional<StockMove> copyAndSplitStockMove(
|
||||
StockMove stockMove, List<StockMoveLine> stockMoveLines) throws AxelorException;
|
||||
|
||||
public Optional<StockMove> copyAndSplitStockMoveReverse(StockMove stockMove, boolean split)
|
||||
throws AxelorException;
|
||||
|
||||
public Optional<StockMove> copyAndSplitStockMoveReverse(
|
||||
StockMove stockMove, List<StockMoveLine> stockMoveLines, boolean split)
|
||||
throws AxelorException;
|
||||
|
||||
void cancel(StockMove stockMove) throws AxelorException;
|
||||
|
||||
void cancel(StockMove stockMove, CancelReason cancelReason, String cancelReasonStr)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional
|
||||
public Boolean splitStockMoveLinesUnit(List<StockMoveLine> stockMoveLines, BigDecimal splitQty);
|
||||
|
||||
@Transactional
|
||||
public void splitStockMoveLinesSpecial(
|
||||
StockMove stockMove, List<StockMoveLine> list, BigDecimal splitQty);
|
||||
|
||||
@Transactional
|
||||
public void splitStockMoveLinesSpecial2(
|
||||
StockMove stockMove, List<StockMoveLine> list, BigDecimal splitQty);
|
||||
|
||||
@Transactional
|
||||
public void copyQtyToRealQty(StockMove stockMove);
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Optional<StockMove> generateReversion(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public StockMove splitInto2(
|
||||
StockMove originalStockMove, List<StockMoveLine> modifiedStockMoveLines)
|
||||
throws AxelorException;
|
||||
|
||||
public StockMove splitInto2SameMove(
|
||||
StockMove originalStockMove, List<StockMoveLine> modifiedStockMoveLines)
|
||||
throws AxelorException;
|
||||
|
||||
public List<Map<String, Object>> getStockPerDate(
|
||||
Long locationId, Long productId, LocalDate fromDate, LocalDate toDate);
|
||||
|
||||
/**
|
||||
* Change conformity on each stock move line according to the stock move conformity.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
List<StockMoveLine> changeConformityStockMove(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Change stock move conformity according to the conformity on each stock move line.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
Integer changeConformityStockMoveLine(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Called from {@link com.axelor.apps.stock.web.StockMoveController#viewDirection}
|
||||
*
|
||||
* @param stockMove
|
||||
* @return the direction for the google map API
|
||||
*/
|
||||
Map<String, Object> viewDirection(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Print the given stock move.
|
||||
*
|
||||
* @param stockMove
|
||||
* @param lstSelectedMove
|
||||
* @param reportType true if we print a picking order
|
||||
* @return the link to the PDF file
|
||||
* @throws AxelorException
|
||||
*/
|
||||
String printStockMove(StockMove stockMove, List<Integer> lstSelectedMove, String reportType)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Update fully spread over logistical forms flag on stock move.
|
||||
*
|
||||
* @param stockMove
|
||||
*/
|
||||
void updateFullySpreadOverLogisticalFormsFlag(StockMove stockMove);
|
||||
|
||||
void setAvailableStatus(StockMove stockMove);
|
||||
|
||||
void checkExpirationDates(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Update editDate of one Outgoing Stock Move
|
||||
*
|
||||
* @param stockMove
|
||||
* @param userType
|
||||
*/
|
||||
void setPickingStockMoveEditDate(StockMove stockMove, String userType);
|
||||
|
||||
/**
|
||||
* Update editDate of a list of Outgoing Stock Move
|
||||
*
|
||||
* @param ids
|
||||
* @param userType
|
||||
*/
|
||||
void setPickingStockMovesEditDate(List<Long> ids, String userType);
|
||||
|
||||
/**
|
||||
* Update stocks using saved stock move line list and current stock move line list. Then we save
|
||||
* current stock move line list, replacing the saved list.
|
||||
*
|
||||
* @param stockMove
|
||||
*/
|
||||
void updateStocks(StockMove stockMove) throws AxelorException;
|
||||
|
||||
void updateProductNetMass(StockMove stockMove) throws AxelorException;
|
||||
|
||||
public void massCancel(List<Long> requestIds, CancelReason raison, String raisonStr)
|
||||
throws AxelorException;
|
||||
|
||||
public void massDraft(List<Long> requestIds) throws AxelorException;
|
||||
|
||||
public void massPlan(List<Long> requestIds) throws AxelorException;
|
||||
|
||||
public void massRealize(List<Long> requestIds) throws AxelorException;
|
||||
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Address;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface StockMoveToolService {
|
||||
|
||||
/**
|
||||
* Méthode permettant d'obtenir la séquence du StockMove.
|
||||
*
|
||||
* @param stockMoveType Type de mouvement de stock
|
||||
* @param company la société
|
||||
* @return la chaine contenant la séquence du StockMove
|
||||
* @throws AxelorException Aucune séquence de StockMove n'a été configurée
|
||||
*/
|
||||
public String getSequenceStockMove(int stockMoveType, Company company) throws AxelorException;
|
||||
|
||||
public int getStockMoveType(StockLocation fromStockLocation, StockLocation toStockLocation);
|
||||
|
||||
public BigDecimal compute(StockMove stockMove);
|
||||
|
||||
public boolean getDefaultISPM(Partner clientPartner, Address toAddress);
|
||||
|
||||
/**
|
||||
* Fill {@link StockMove#fromAddressStr} and {@link StockMove#toAddressStr}
|
||||
*
|
||||
* @param stockMove
|
||||
*/
|
||||
void computeAddressStr(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Compute stock move name.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
String computeName(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Compute stock move name with the given name.
|
||||
*
|
||||
* @param stockMove
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
String computeName(StockMove stockMove, String name);
|
||||
|
||||
/**
|
||||
* Get from address from stock move or stock location.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
Address getFromAddress(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Get to address from stock move or stock location.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
*/
|
||||
Address getToAddress(StockMove stockMove);
|
||||
|
||||
/**
|
||||
* Get partner address.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
Address getPartnerAddress(StockMove stockMove) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Get company address.
|
||||
*
|
||||
* @param stockMove
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
Address getCompanyAddress(StockMove stockMove) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Address;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.Sequence;
|
||||
import com.axelor.apps.base.db.repo.SequenceRepository;
|
||||
import com.axelor.apps.base.service.AddressService;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
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.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Objects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StockMoveToolServiceImpl implements StockMoveToolService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected StockMoveLineService stockMoveLineService;
|
||||
protected AppBaseService appBaseService;
|
||||
protected StockMoveRepository stockMoveRepo;
|
||||
protected PartnerProductQualityRatingService partnerProductQualityRatingService;
|
||||
private SequenceService sequenceService;
|
||||
private StockMoveLineRepository stockMoveLineRepo;
|
||||
|
||||
@Inject
|
||||
public StockMoveToolServiceImpl(
|
||||
StockMoveLineService stockMoveLineService,
|
||||
SequenceService sequenceService,
|
||||
StockMoveLineRepository stockMoveLineRepository,
|
||||
AppBaseService appBaseService,
|
||||
StockMoveRepository stockMoveRepository,
|
||||
PartnerProductQualityRatingService partnerProductQualityRatingService) {
|
||||
this.stockMoveLineService = stockMoveLineService;
|
||||
this.sequenceService = sequenceService;
|
||||
this.stockMoveLineRepo = stockMoveLineRepository;
|
||||
this.appBaseService = appBaseService;
|
||||
this.stockMoveRepo = stockMoveRepository;
|
||||
this.partnerProductQualityRatingService = partnerProductQualityRatingService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal compute(StockMove stockMove) {
|
||||
BigDecimal exTaxTotal = BigDecimal.ZERO;
|
||||
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
|
||||
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
|
||||
exTaxTotal =
|
||||
exTaxTotal.add(
|
||||
stockMoveLine.getRealQty().multiply(stockMoveLine.getUnitPriceUntaxed()));
|
||||
}
|
||||
}
|
||||
return exTaxTotal.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant d'obtenir la séquence du StockMove.
|
||||
*
|
||||
* @param stockMoveType Type de mouvement de stock
|
||||
* @param company la société
|
||||
* @return la chaine contenant la séquence du StockMove
|
||||
* @throws AxelorException Aucune séquence de StockMove n'a été configurée
|
||||
*/
|
||||
@Override
|
||||
public String getSequenceStockMove(int stockMoveType, Company company) throws AxelorException {
|
||||
|
||||
String ref = "";
|
||||
|
||||
switch (stockMoveType) {
|
||||
case StockMoveRepository.TYPE_INTERNAL:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.INTERNAL, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_1),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_INCOMING:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.INCOMING, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_2),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_OUTGOING:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.OUTGOING, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_3),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_INCOMING_CLIENT:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.INCOMING_CLIENT, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_3),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_OUTGOING_CLIENT:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.OUTGOING_CLIENT, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_3),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_INTERNAL_OUTGOING_CLIENT:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.CUST_DELIVERY, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_3),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case StockMoveRepository.TYPE_SUPPLIER_OUTGOING_CLIENT:
|
||||
ref = sequenceService.getSequenceNumber(SequenceRepository.SUP_RETURN, company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_3),
|
||||
company.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_4),
|
||||
company.getName());
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
// sequence for production request
|
||||
public String getSequenceStockProductionRequest(StockProductionRequest stockProductionRequest)
|
||||
throws AxelorException {
|
||||
String ref = "";
|
||||
Integer stockProductionRequestType = stockProductionRequest.getTypeSelect();
|
||||
Company company = stockProductionRequest.getCompany();
|
||||
|
||||
if (stockProductionRequestType == 1) {
|
||||
|
||||
ref = sequenceService.getSequenceNumber("stockProductionReques", company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_1),
|
||||
company.getName());
|
||||
} else {
|
||||
return ref;
|
||||
}
|
||||
} else if (stockProductionRequestType == 2) {
|
||||
|
||||
ref = sequenceService.getSequenceNumber("stockProductionRequestReturn", company);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_1),
|
||||
company.getName());
|
||||
} else {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
// sequence for production request
|
||||
public String getInternalSequence(int internalSequenceType, Company company ,LocalDate date)
|
||||
throws AxelorException {
|
||||
String ref = "";
|
||||
|
||||
if (internalSequenceType == 35) {
|
||||
Sequence seq = sequenceService.getSequence("acInternalSequence", company);
|
||||
ref = sequenceService.getSequenceNumber(seq, date);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_1),
|
||||
company.getName());
|
||||
} else {
|
||||
return ref;
|
||||
}
|
||||
} else if (internalSequenceType == 36) {
|
||||
Sequence seq = sequenceService.getSequence("mpInternalSequence", company);
|
||||
ref = sequenceService.getSequenceNumber(seq, date);
|
||||
if (ref == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_1),
|
||||
company.getName());
|
||||
} else {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientPartner
|
||||
* @param toAddress
|
||||
* @return default value for {@link StockMove#isIspmRequired}
|
||||
*/
|
||||
public boolean getDefaultISPM(Partner clientPartner, Address toAddress) {
|
||||
if (clientPartner != null && clientPartner.getIsIspmRequired()) {
|
||||
return true;
|
||||
} else {
|
||||
return toAddress != null
|
||||
&& toAddress.getAddressL7Country() != null
|
||||
&& toAddress.getAddressL7Country().getIsIspmRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStockMoveType(StockLocation fromStockLocation, StockLocation toStockLocation) {
|
||||
|
||||
if (fromStockLocation.getTypeSelect() == StockLocationRepository.TYPE_INTERNAL
|
||||
&& toStockLocation.getTypeSelect() == StockLocationRepository.TYPE_INTERNAL) {
|
||||
return StockMoveRepository.TYPE_INTERNAL;
|
||||
} else if (fromStockLocation.getTypeSelect() != StockLocationRepository.TYPE_INTERNAL
|
||||
&& toStockLocation.getTypeSelect() == StockLocationRepository.TYPE_INTERNAL) {
|
||||
return StockMoveRepository.TYPE_INCOMING;
|
||||
} else if (fromStockLocation.getTypeSelect() == StockLocationRepository.TYPE_INTERNAL
|
||||
&& toStockLocation.getTypeSelect() != StockLocationRepository.TYPE_INTERNAL) {
|
||||
return StockMoveRepository.TYPE_OUTGOING;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getFromAddress(StockMove stockMove) {
|
||||
Address fromAddress = stockMove.getFromAddress();
|
||||
if (fromAddress == null && stockMove.getFromStockLocation() != null) {
|
||||
fromAddress = stockMove.getFromStockLocation().getAddress();
|
||||
}
|
||||
return fromAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getToAddress(StockMove stockMove) {
|
||||
Address toAddress = stockMove.getToAddress();
|
||||
if (toAddress == null && stockMove.getToStockLocation() != null) {
|
||||
toAddress = stockMove.getToStockLocation().getAddress();
|
||||
}
|
||||
return toAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeAddressStr(StockMove stockMove) {
|
||||
AddressService addressService = Beans.get(AddressService.class);
|
||||
stockMove.setFromAddressStr(addressService.computeAddressStr(stockMove.getFromAddress()));
|
||||
stockMove.setToAddressStr(addressService.computeAddressStr(stockMove.getToAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String computeName(StockMove stockMove) {
|
||||
return computeName(stockMove, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String computeName(StockMove stockMove, String name) {
|
||||
Objects.requireNonNull(stockMove);
|
||||
StringBuilder nameBuilder = new StringBuilder();
|
||||
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
if (!Strings.isNullOrEmpty(stockMove.getStockMoveSeq())) {
|
||||
nameBuilder.append(stockMove.getStockMoveSeq());
|
||||
}
|
||||
} else {
|
||||
nameBuilder.append(name);
|
||||
}
|
||||
|
||||
if (stockMove.getPartner() != null
|
||||
&& !Strings.isNullOrEmpty(stockMove.getPartner().getFullName())) {
|
||||
if (nameBuilder.length() > 0) {
|
||||
nameBuilder.append(" - ");
|
||||
}
|
||||
|
||||
nameBuilder.append(stockMove.getPartner().getFullName());
|
||||
}
|
||||
|
||||
return nameBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getPartnerAddress(StockMove stockMove) throws AxelorException {
|
||||
Address address;
|
||||
|
||||
if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_OUTGOING) {
|
||||
address = getToAddress(stockMove);
|
||||
} else if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING) {
|
||||
address = getFromAddress(stockMove);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
stockMove, TraceBackRepository.CATEGORY_INCONSISTENCY, I18n.get("Bad stock move type"));
|
||||
}
|
||||
|
||||
if (address.getAddressL7Country() == null) {
|
||||
throw new AxelorException(address, TraceBackRepository.CATEGORY_NO_VALUE, "Missing country");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getCompanyAddress(StockMove stockMove) throws AxelorException {
|
||||
Address address;
|
||||
|
||||
if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_OUTGOING) {
|
||||
address = getFromAddress(stockMove);
|
||||
} else if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING) {
|
||||
address = getToAddress(stockMove);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
stockMove, TraceBackRepository.CATEGORY_INCONSISTENCY, I18n.get("Bad stock move type"));
|
||||
}
|
||||
|
||||
if (address.getAddressL7Country() == null) {
|
||||
throw new AxelorException(address, TraceBackRepository.CATEGORY_NO_VALUE, "Missing country");
|
||||
}
|
||||
|
||||
if (address.getCity() == null
|
||||
|| address.getCity().getDepartment() == null
|
||||
|| StringUtils.isBlank(address.getCity().getDepartment().getCode())) {
|
||||
throw new AxelorException(
|
||||
address, TraceBackRepository.CATEGORY_NO_VALUE, "Missing department");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.axelor.apps.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.db.StockProductionRequestLine;
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class StockProductionRequestLineService {
|
||||
|
||||
public void setProductInfo(
|
||||
StockProductionRequest productionRequest,
|
||||
StockProductionRequestLine productionRequestLine,
|
||||
Company company) {
|
||||
Preconditions.checkNotNull(productionRequestLine);
|
||||
Preconditions.checkNotNull(company);
|
||||
Product product = productionRequestLine.getProduct();
|
||||
if (product == null) {
|
||||
return;
|
||||
}
|
||||
productionRequestLine.setUnit(product.getUnit());
|
||||
productionRequestLine.setProductName(product.getName());
|
||||
}
|
||||
|
||||
public StockProductionRequestLine compute(
|
||||
StockProductionRequestLine productionRequestLine, StockProductionRequest productionRequest) {
|
||||
|
||||
BigDecimal unitPriceUntaxed = BigDecimal.ZERO;
|
||||
if (productionRequestLine.getProduct() != null && productionRequest != null) {
|
||||
unitPriceUntaxed = productionRequestLine.getProduct().getAvgPrice();
|
||||
}
|
||||
productionRequestLine.setUnitPriceUntaxed(unitPriceUntaxed);
|
||||
productionRequestLine.setUnitPriceTaxed(unitPriceUntaxed);
|
||||
productionRequestLine.setCompanyUnitPriceUntaxed(unitPriceUntaxed);
|
||||
return productionRequestLine;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockRules;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface StockRulesService {
|
||||
void generateOrder(Product product, BigDecimal qty, StockLocationLine stockLocationLine, int type)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
void generatePurchaseOrder(
|
||||
Product product, BigDecimal qty, StockLocationLine stockLocationLine, int type)
|
||||
throws AxelorException;
|
||||
|
||||
boolean useMinStockRules(
|
||||
StockLocationLine stockLocationLine, StockRules stockRules, BigDecimal qty, int type);
|
||||
|
||||
StockRules getStockRules(Product product, StockLocation stockLocation, int type, int useCase);
|
||||
|
||||
BigDecimal getQtyToOrder(
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
int type,
|
||||
StockRules stockRules,
|
||||
BigDecimal minReorderQty);
|
||||
|
||||
BigDecimal getQtyToOrder(
|
||||
BigDecimal qty, StockLocationLine stockLocationLine, int type, StockRules stockRules);
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockRules;
|
||||
import com.axelor.apps.stock.db.repo.StockRulesRepository;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class StockRulesServiceImpl implements StockRulesService {
|
||||
|
||||
protected StockRulesRepository stockRuleRepo;
|
||||
|
||||
@Inject
|
||||
public StockRulesServiceImpl(StockRulesRepository stockRuleRepo) {
|
||||
this.stockRuleRepo = stockRuleRepo;
|
||||
}
|
||||
|
||||
public void generateOrder(
|
||||
Product product, BigDecimal qty, StockLocationLine stockLocationLine, int type)
|
||||
throws AxelorException {
|
||||
this.generatePurchaseOrder(product, qty, stockLocationLine, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void generatePurchaseOrder(
|
||||
Product product, BigDecimal qty, StockLocationLine stockLocationLine, int type)
|
||||
throws AxelorException {
|
||||
|
||||
StockLocation stockLocation = stockLocationLine.getStockLocation();
|
||||
|
||||
// TODO à supprimer après suppression des variantes
|
||||
if (stockLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StockRules stockRules =
|
||||
this.getStockRules(
|
||||
product, stockLocation, type, StockRulesRepository.USE_CASE_STOCK_CONTROL);
|
||||
|
||||
if (stockRules == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.useMinStockRules(stockLocationLine, stockRules, qty, type)) {
|
||||
|
||||
if (stockRules.getOrderAlertSelect() == StockRulesRepository.ORDER_ALERT_ALERT) {
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on creating a new purchase or production order. Takes into account the reorder qty and
|
||||
* the min/ideal quantity in stock rules.
|
||||
*
|
||||
* <p>with L the quantity that will be left in the stock location, M the min/ideal qty, R the
|
||||
* reorder quantity and O the quantity to order :
|
||||
*
|
||||
* <p>O = max(R, M - L)
|
||||
*
|
||||
* @param qty the quantity of the stock move.
|
||||
* @param stockLocationLine
|
||||
* @param type current or future
|
||||
* @param stockRules
|
||||
* @param minReorderQty
|
||||
* @return the quantity to order
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal getQtyToOrder(
|
||||
BigDecimal qty,
|
||||
StockLocationLine stockLocationLine,
|
||||
int type,
|
||||
StockRules stockRules,
|
||||
BigDecimal minReorderQty) {
|
||||
minReorderQty = minReorderQty.max(stockRules.getReOrderQty());
|
||||
|
||||
BigDecimal stockLocationLineQty =
|
||||
(type == StockRulesRepository.TYPE_CURRENT)
|
||||
? stockLocationLine.getCurrentQty()
|
||||
: stockLocationLine.getFutureQty();
|
||||
|
||||
// Get the quantity left in stock location line.
|
||||
BigDecimal qtyToOrder = stockLocationLineQty.subtract(qty);
|
||||
|
||||
// The quantity to reorder is the difference between the min/ideal
|
||||
// quantity and the quantity left in the stock location.
|
||||
BigDecimal targetQty =
|
||||
stockRules.getUseIdealQty() ? stockRules.getIdealQty() : stockRules.getMinQty();
|
||||
qtyToOrder = targetQty.subtract(qtyToOrder);
|
||||
|
||||
// If the quantity we need to order is less than the reorder quantity,
|
||||
// we must choose the reorder quantity instead.
|
||||
qtyToOrder = qtyToOrder.max(minReorderQty);
|
||||
|
||||
// Limit the quantity to order in order to not exceed to max quantity
|
||||
// rule.
|
||||
if (stockRules.getUseMaxQty()) {
|
||||
BigDecimal maxQtyToReorder = stockRules.getMaxQty().subtract(stockLocationLineQty);
|
||||
qtyToOrder = qtyToOrder.min(maxQtyToReorder);
|
||||
}
|
||||
|
||||
return qtyToOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getQtyToOrder(
|
||||
BigDecimal qty, StockLocationLine stockLocationLine, int type, StockRules stockRules) {
|
||||
return getQtyToOrder(qty, stockLocationLine, type, stockRules, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMinStockRules(
|
||||
StockLocationLine stockLocationLine, StockRules stockRules, BigDecimal qty, int type) {
|
||||
|
||||
BigDecimal currentQty = stockLocationLine.getCurrentQty();
|
||||
BigDecimal futureQty = stockLocationLine.getFutureQty();
|
||||
|
||||
BigDecimal minQty = stockRules.getMinQty();
|
||||
|
||||
if (type == StockRulesRepository.TYPE_CURRENT) {
|
||||
|
||||
if (currentQty.compareTo(minQty) >= 0 && (currentQty.subtract(qty)).compareTo(minQty) == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (type == StockRulesRepository.TYPE_FUTURE) {
|
||||
|
||||
if (futureQty.compareTo(minQty) >= 0 && (futureQty.subtract(qty)).compareTo(minQty) == -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockRules getStockRules(
|
||||
Product product, StockLocation stockLocation, int type, int useCase) {
|
||||
|
||||
if (useCase == StockRulesRepository.USE_CASE_USED_FOR_MRP) {
|
||||
if (stockLocation == null) {
|
||||
return stockRuleRepo
|
||||
.all()
|
||||
.filter("self.product = ?1 AND self.useCaseSelect = ?2", product, useCase)
|
||||
.fetchOne();
|
||||
}
|
||||
return stockRuleRepo
|
||||
.all()
|
||||
.filter(
|
||||
"self.product = ?1 AND self.stockLocation = ?2 AND self.useCaseSelect = ?3",
|
||||
product,
|
||||
stockLocation,
|
||||
useCase)
|
||||
.fetchOne();
|
||||
} else if (useCase == StockRulesRepository.USE_CASE_STOCK_CONTROL) {
|
||||
return stockRuleRepo
|
||||
.all()
|
||||
.filter(
|
||||
"self.product = ?1 AND self.stockLocation = ?2 AND self.useCaseSelect = ?3 AND self.typeSelect = ?4",
|
||||
product,
|
||||
stockLocation,
|
||||
useCase,
|
||||
type)
|
||||
.fetchOne();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO , plusieurs régles min de stock par produit (achat a 500 et production a 100)...
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.axelor.apps.stock.service;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
LocalDate beginDate = LocalDate.now().withDayOfMonth(1);
|
||||
System.out.println(beginDate);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Sequence;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.TrackingNumberConfiguration;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberConfigurationRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class TrackingNumberService {
|
||||
|
||||
@Inject private SequenceService sequenceService;
|
||||
|
||||
@Inject private TrackingNumberRepository trackingNumberRepo;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public TrackingNumber getTrackingNumber(
|
||||
Product product, BigDecimal sizeOfLot, Company company, LocalDate date)
|
||||
throws AxelorException {
|
||||
|
||||
TrackingNumber trackingNumber =
|
||||
trackingNumberRepo
|
||||
.all()
|
||||
.filter("self.product = ?1 AND self.counter < ?2", product, sizeOfLot)
|
||||
.fetchOne();
|
||||
|
||||
if (trackingNumber == null) {
|
||||
trackingNumber = trackingNumberRepo.save(this.createTrackingNumber(product, company, date));
|
||||
}
|
||||
|
||||
trackingNumber.setCounter(trackingNumber.getCounter().add(sizeOfLot));
|
||||
|
||||
return trackingNumber;
|
||||
}
|
||||
|
||||
public String getOrderMethod(TrackingNumberConfiguration trackingNumberConfiguration) {
|
||||
int autoTrackingNbrOrderSelect = -1;
|
||||
if (trackingNumberConfiguration.getIsSaleTrackingManaged()) {
|
||||
autoTrackingNbrOrderSelect = trackingNumberConfiguration.getSaleAutoTrackingNbrOrderSelect();
|
||||
} else if (trackingNumberConfiguration.getIsProductionTrackingManaged()) {
|
||||
autoTrackingNbrOrderSelect =
|
||||
trackingNumberConfiguration.getProductAutoTrackingNbrOrderSelect();
|
||||
}
|
||||
switch (autoTrackingNbrOrderSelect) {
|
||||
case TrackingNumberConfigurationRepository.TRACKING_NUMBER_ORDER_FIFO:
|
||||
return " ORDER BY self.trackingNumber.perishableExpirationDate ASC";
|
||||
|
||||
case TrackingNumberConfigurationRepository.TRACKING_NUMBER_ORDER_LIFO:
|
||||
return " ORDER BY self.trackingNumber.perishableExpirationDate DESC";
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public TrackingNumber createTrackingNumber(Product product, Company company, LocalDate date)
|
||||
throws AxelorException {
|
||||
Preconditions.checkNotNull(product, I18n.get("Product cannot be null."));
|
||||
Preconditions.checkNotNull(company, I18n.get("Company cannot be null."));
|
||||
Preconditions.checkNotNull(date, I18n.get(IExceptionMessage.TRACK_NUMBER_DATE_MISSING));
|
||||
|
||||
TrackingNumber trackingNumber = new TrackingNumber();
|
||||
|
||||
if (product.getIsPerishable()) {
|
||||
trackingNumber.setPerishableExpirationDate(
|
||||
date.plusMonths(product.getPerishableNbrOfMonths()));
|
||||
}
|
||||
if (product.getHasWarranty()) {
|
||||
trackingNumber.setWarrantyExpirationDate(date.plusMonths(product.getWarrantyNbrOfMonths()));
|
||||
}
|
||||
|
||||
trackingNumber.setProduct(product);
|
||||
trackingNumber.setCounter(BigDecimal.ZERO);
|
||||
|
||||
TrackingNumberConfiguration trackingNumberConfiguration =
|
||||
product.getTrackingNumberConfiguration();
|
||||
|
||||
if (trackingNumberConfiguration.getSequence() == null) {
|
||||
throw new AxelorException(
|
||||
product,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.TRACKING_NUMBER_1),
|
||||
company.getName(),
|
||||
product.getCode());
|
||||
}
|
||||
|
||||
Sequence sequence = trackingNumberConfiguration.getSequence();
|
||||
String seq;
|
||||
while (true) {
|
||||
seq = sequenceService.getSequenceNumber(sequence);
|
||||
if (trackingNumberRepo
|
||||
.all()
|
||||
.filter("self.product = ?1 AND self.trackingNumberSeq = ?2", product, seq)
|
||||
.count()
|
||||
== 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
trackingNumber.setTrackingNumberSeq(seq);
|
||||
|
||||
return trackingNumber;
|
||||
}
|
||||
}
|
||||
@ -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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface WeightedAveragePriceService {
|
||||
|
||||
@Transactional
|
||||
public void computeAvgPriceForProduct(Product product);
|
||||
|
||||
public BigDecimal computeAvgPriceForCompany(Product product, Company company);
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.stock.service;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.ProductService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@RequestScoped
|
||||
public class WeightedAveragePriceServiceImpl implements WeightedAveragePriceService {
|
||||
|
||||
protected ProductRepository productRepo;
|
||||
protected AppBaseService appBaseService;
|
||||
|
||||
@Inject
|
||||
public WeightedAveragePriceServiceImpl(
|
||||
ProductRepository productRepo, AppBaseService appBaseService) {
|
||||
this.productRepo = productRepo;
|
||||
this.appBaseService = appBaseService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void computeAvgPriceForProduct(Product product) {
|
||||
|
||||
BigDecimal productAvgPrice = this.computeAvgPriceForCompany(product, null);
|
||||
|
||||
if (productAvgPrice.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
product.setAvgPrice(productAvgPrice);
|
||||
if (product.getCostTypeSelect() == ProductRepository.COST_TYPE_AVERAGE_PRICE) {
|
||||
product.setCostPrice(productAvgPrice);
|
||||
if (product.getAutoUpdateSalePrice()) {
|
||||
Beans.get(ProductService.class).updateSalePrice(product);
|
||||
}
|
||||
}
|
||||
productRepo.save(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal computeAvgPriceForCompany(Product product, Company company) {
|
||||
Long productId = product.getId();
|
||||
String query =
|
||||
"SELECT new list(self.id, self.avgPrice, self.currentQty) FROM StockLocationLine as self "
|
||||
+ "WHERE self.product.id = "
|
||||
+ productId
|
||||
+ " AND self.stockLocation.typeSelect != "
|
||||
+ StockLocationRepository.TYPE_VIRTUAL;
|
||||
|
||||
if (company != null) {
|
||||
query += " AND self.stockLocation.company = " + company.getId();
|
||||
}
|
||||
|
||||
int scale = appBaseService.getNbDecimalDigitForUnitPrice();
|
||||
BigDecimal productAvgPrice = BigDecimal.ZERO;
|
||||
BigDecimal qtyTot = BigDecimal.ZERO;
|
||||
List<List<Object>> results = JPA.em().createQuery(query).getResultList();
|
||||
if (results.isEmpty()) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
for (List<Object> result : results) {
|
||||
BigDecimal avgPrice = (BigDecimal) result.get(1);
|
||||
BigDecimal qty = (BigDecimal) result.get(2);
|
||||
productAvgPrice = productAvgPrice.add(avgPrice.multiply(qty));
|
||||
qtyTot = qtyTot.add(qty);
|
||||
}
|
||||
if (qtyTot.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
productAvgPrice = productAvgPrice.divide(qtyTot, scale, BigDecimal.ROUND_HALF_UP);
|
||||
return productAvgPrice;
|
||||
}
|
||||
}
|
||||
@ -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.stock.service.app;
|
||||
|
||||
import com.axelor.apps.base.db.AppStock;
|
||||
|
||||
public interface AppStockService {
|
||||
|
||||
public void generateStockConfigurations();
|
||||
|
||||
AppStock getAppStock();
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.stock.service.app;
|
||||
|
||||
import com.axelor.apps.base.db.AppStock;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.repo.AppStockRepository;
|
||||
import com.axelor.apps.base.db.repo.CompanyRepository;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.repo.StockConfigRepository;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.List;
|
||||
|
||||
public class AppStockServiceImpl implements AppStockService {
|
||||
|
||||
@Inject private CompanyRepository companyRepo;
|
||||
|
||||
@Inject private StockConfigRepository stockConfigRepo;
|
||||
|
||||
@Inject private AppStockRepository appStockRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void generateStockConfigurations() {
|
||||
|
||||
List<Company> companies = companyRepo.all().filter("self.stockConfig is null").fetch();
|
||||
|
||||
for (Company company : companies) {
|
||||
StockConfig stockConfig = new StockConfig();
|
||||
stockConfig.setCompany(company);
|
||||
stockConfigRepo.save(stockConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppStock getAppStock() {
|
||||
return appStockRepository.all().fetchOne();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.stock.service.config;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.stock.db.StockConfig;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
|
||||
public class StockConfigService {
|
||||
|
||||
public StockConfig getStockConfig(Company company) throws AxelorException {
|
||||
|
||||
StockConfig stockConfig = company.getStockConfig();
|
||||
|
||||
if (stockConfig == null) {
|
||||
throw new AxelorException(
|
||||
company,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_1),
|
||||
company.getName());
|
||||
}
|
||||
|
||||
return stockConfig;
|
||||
}
|
||||
|
||||
/** ****************************** STOCK LOCATION ******************************************* */
|
||||
public StockLocation getInventoryVirtualStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
|
||||
if (stockConfig.getInventoryVirtualStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_2),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
|
||||
return stockConfig.getInventoryVirtualStockLocation();
|
||||
}
|
||||
|
||||
public StockLocation getSupplierVirtualStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
|
||||
if (stockConfig.getSupplierVirtualStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_3),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
|
||||
return stockConfig.getSupplierVirtualStockLocation();
|
||||
}
|
||||
|
||||
public StockLocation getCustomerVirtualStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
|
||||
if (stockConfig.getCustomerVirtualStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_4),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
|
||||
return stockConfig.getCustomerVirtualStockLocation();
|
||||
}
|
||||
|
||||
public StockLocation getReceiptDefaultStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
|
||||
if (stockConfig.getReceiptDefaultStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_RECEIPT),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
|
||||
return stockConfig.getReceiptDefaultStockLocation();
|
||||
}
|
||||
|
||||
public StockLocation getPickupDefaultStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
if (stockConfig.getPickupDefaultStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_PICKUP),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
return stockConfig.getPickupDefaultStockLocation();
|
||||
}
|
||||
|
||||
public StockLocation getRawMaterialsDefaultStockLocation(StockConfig stockConfig)
|
||||
throws AxelorException {
|
||||
if (stockConfig.getPickupDefaultStockLocation() == null) {
|
||||
throw new AxelorException(
|
||||
stockConfig,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.STOCK_CONFIG_PICKUP),
|
||||
stockConfig.getCompany().getName());
|
||||
}
|
||||
return stockConfig.getRawMaterialsDefaultStockLocation();
|
||||
}
|
||||
}
|
||||
@ -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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface ConformityCertificatePrintService {
|
||||
|
||||
/**
|
||||
* Print a Conformity certificates for list of stock moves in the same output.
|
||||
*
|
||||
* @param ids ids of the stock move.
|
||||
* @return the link to the generated file.
|
||||
* @throws IOException
|
||||
*/
|
||||
String printConformityCertificates(List<Long> ids) throws IOException;
|
||||
|
||||
ReportSettings prepareReportSettings(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
File print(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
String printConformityCertificate(StockMove stockMove, String format)
|
||||
throws AxelorException, IOException;
|
||||
|
||||
String getFileName(StockMove stockMove);
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.tool.ModelTool;
|
||||
import com.axelor.apps.tool.ThrowConsumer;
|
||||
import com.axelor.apps.tool.file.PdfTool;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConformityCertificatePrintServiceImpl implements ConformityCertificatePrintService {
|
||||
|
||||
@Override
|
||||
public String printConformityCertificates(List<Long> ids) throws IOException {
|
||||
List<File> printedConformityCertificates = new ArrayList<>();
|
||||
ModelTool.apply(
|
||||
StockMove.class,
|
||||
ids,
|
||||
new ThrowConsumer<StockMove>() {
|
||||
@Override
|
||||
public void accept(StockMove stockMove) throws Exception {
|
||||
printedConformityCertificates.add(print(stockMove, ReportSettings.FORMAT_PDF));
|
||||
}
|
||||
});
|
||||
String fileName = getConformityCertificateFilesName(true, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.mergePdfToFileLink(printedConformityCertificates, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportSettings prepareReportSettings(StockMove stockMove, String format)
|
||||
throws AxelorException {
|
||||
if (stockMove.getPrintingSettings() == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVES_MISSING_PRINTING_SETTINGS),
|
||||
stockMove.getStockMoveSeq()),
|
||||
stockMove);
|
||||
}
|
||||
|
||||
String locale = ReportSettings.getPrintingLocale(stockMove.getPartner());
|
||||
String title = getFileName(stockMove);
|
||||
|
||||
ReportSettings reportSetting =
|
||||
ReportFactory.createReport(IReport.CONFORMITY_CERTIFICATE, title + " - ${date}");
|
||||
return reportSetting
|
||||
.addParam("StockMoveId", stockMove.getId())
|
||||
.addParam("Locale", locale)
|
||||
.addParam("HeaderHeight", stockMove.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", stockMove.getPrintingSettings().getPdfFooterHeight())
|
||||
.addFormat(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File print(StockMove stockMove, String format) throws AxelorException {
|
||||
ReportSettings reportSettings = prepareReportSettings(stockMove, format);
|
||||
return reportSettings.generate().getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printConformityCertificate(StockMove stockMove, String format)
|
||||
throws AxelorException, IOException {
|
||||
String fileName = getConformityCertificateFilesName(false, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.getFileLinkFromPdfFile(print(stockMove, format), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the printed certificate of conformity.
|
||||
*
|
||||
* @param plural if there is one or multiple certificates.
|
||||
*/
|
||||
public String getConformityCertificateFilesName(boolean plural, String format) {
|
||||
|
||||
return I18n.get(plural ? "Conformity Certificates" : "Certificate of conformity")
|
||||
+ " - "
|
||||
+ Beans.get(AppBaseService.class).getTodayDate().format(DateTimeFormatter.BASIC_ISO_DATE)
|
||||
+ "."
|
||||
+ format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName(StockMove stockMove) {
|
||||
|
||||
return I18n.get("Certificate of conformity") + " " + stockMove.getStockMoveSeq();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface PickingStockMovePrintService {
|
||||
|
||||
/**
|
||||
* Print a list of stock moves in the same output.
|
||||
*
|
||||
* @param ids ids of the stock move.
|
||||
* @param userType
|
||||
* @return the link to the generated file.
|
||||
* @throws IOException
|
||||
*/
|
||||
String printStockMoves(List<Long> ids, String userType) throws IOException;
|
||||
|
||||
ReportSettings prepareReportSettings(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
File print(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
String printStockMove(StockMove stockMove, String format, String userType)
|
||||
throws AxelorException, IOException;
|
||||
|
||||
String getFileName(StockMove stockMove);
|
||||
}
|
||||
@ -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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.tool.ModelTool;
|
||||
import com.axelor.apps.tool.ThrowConsumer;
|
||||
import com.axelor.apps.tool.file.PdfTool;
|
||||
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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PickingStockMovePrintServiceimpl implements PickingStockMovePrintService {
|
||||
|
||||
@Inject private StockMoveService stockMoveService;
|
||||
|
||||
@Override
|
||||
public String printStockMoves(List<Long> ids, String userType) throws IOException {
|
||||
List<File> printedStockMoves = new ArrayList<>();
|
||||
ModelTool.apply(
|
||||
StockMove.class,
|
||||
ids,
|
||||
new ThrowConsumer<StockMove>() {
|
||||
@Override
|
||||
public void accept(StockMove stockMove) throws Exception {
|
||||
printedStockMoves.add(print(stockMove, ReportSettings.FORMAT_PDF));
|
||||
}
|
||||
});
|
||||
stockMoveService.setPickingStockMovesEditDate(ids, userType);
|
||||
String fileName = getStockMoveFilesName(true, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.mergePdfToFileLink(printedStockMoves, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportSettings prepareReportSettings(StockMove stockMove, String format)
|
||||
throws AxelorException {
|
||||
if (stockMove.getPrintingSettings() == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVES_MISSING_PRINTING_SETTINGS),
|
||||
stockMove.getStockMoveSeq()),
|
||||
stockMove);
|
||||
}
|
||||
|
||||
String locale = ReportSettings.getPrintingLocale(stockMove.getPartner());
|
||||
String title = getFileName(stockMove);
|
||||
|
||||
ReportSettings reportSetting =
|
||||
ReportFactory.createReport(IReport.PICKING_STOCK_MOVE, title + " - ${date}");
|
||||
return reportSetting
|
||||
.addParam("StockMoveId", stockMove.getId())
|
||||
.addParam("Locale", locale)
|
||||
.addParam("HeaderHeight", stockMove.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", stockMove.getPrintingSettings().getPdfFooterHeight())
|
||||
.addFormat(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File print(StockMove stockMove, String format) throws AxelorException {
|
||||
ReportSettings reportSettings = prepareReportSettings(stockMove, format);
|
||||
return reportSettings.generate().getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printStockMove(StockMove stockMove, String format, String userType)
|
||||
throws AxelorException, IOException {
|
||||
stockMoveService.setPickingStockMoveEditDate(stockMove, userType);
|
||||
String fileName = getStockMoveFilesName(false, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.getFileLinkFromPdfFile(print(stockMove, format), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the printed stock move.
|
||||
*
|
||||
* @param plural if there is one or multiple stock moves.
|
||||
*/
|
||||
public String getStockMoveFilesName(boolean plural, String format) {
|
||||
|
||||
return I18n.get(plural ? "Stock moves" : "Stock move")
|
||||
+ " - "
|
||||
+ Beans.get(AppBaseService.class).getTodayDate().format(DateTimeFormatter.BASIC_ISO_DATE)
|
||||
+ "."
|
||||
+ format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName(StockMove stockMove) {
|
||||
|
||||
return I18n.get("Stock Move") + " " + stockMove.getStockMoveSeq();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface StockMovePrintService {
|
||||
|
||||
/**
|
||||
* Print a list of stock moves in the same output.
|
||||
*
|
||||
* @param ids ids of the stock move.
|
||||
* @return the link to the generated file.
|
||||
* @throws IOException
|
||||
*/
|
||||
String printStockMoves(List<Long> ids) throws IOException;
|
||||
|
||||
ReportSettings prepareReportSettings(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
File print(StockMove stockMove, String format) throws AxelorException;
|
||||
|
||||
String printStockMove(StockMove stockMove, String format) throws AxelorException, IOException;
|
||||
|
||||
String getFileName(StockMove stockMove);
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.ConvertNumberToFrenchWordsService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.tool.ModelTool;
|
||||
import com.axelor.apps.tool.ThrowConsumer;
|
||||
import com.axelor.apps.tool.file.PdfTool;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StockMovePrintServiceImpl implements StockMovePrintService {
|
||||
|
||||
@Override
|
||||
public String printStockMoves(List<Long> ids) throws IOException {
|
||||
List<File> printedStockMoves = new ArrayList<>();
|
||||
ModelTool.apply(
|
||||
StockMove.class,
|
||||
ids,
|
||||
new ThrowConsumer<StockMove>() {
|
||||
@Override
|
||||
public void accept(StockMove stockMove) throws Exception {
|
||||
printedStockMoves.add(print(stockMove, ReportSettings.FORMAT_PDF));
|
||||
}
|
||||
});
|
||||
String fileName = getStockMoveFilesName(true, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.mergePdfToFileLink(printedStockMoves, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportSettings prepareReportSettings(StockMove stockMove, String format)
|
||||
throws AxelorException {
|
||||
if (stockMove.getPrintingSettings() == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVES_MISSING_PRINTING_SETTINGS),
|
||||
stockMove.getStockMoveSeq()),
|
||||
stockMove);
|
||||
}
|
||||
|
||||
String locale = ReportSettings.getPrintingLocale(stockMove.getPartner());
|
||||
String title = getFileName(stockMove);
|
||||
|
||||
ReportSettings reportSetting =
|
||||
ReportFactory.createReport(IReport.STOCK_MOVE, title + " - ${date}");
|
||||
|
||||
|
||||
String[] exTaxTotal = stockMove.getExTaxTotal().toString().split("\\.");
|
||||
|
||||
String left = Beans.get(ConvertNumberToFrenchWordsService.class).convert(Long.parseLong(exTaxTotal[0]));
|
||||
String right = Beans.get(ConvertNumberToFrenchWordsService.class).convert(Long.parseLong(exTaxTotal[1]));
|
||||
String number = left+" dinars algériens et "+right+" Cts";
|
||||
|
||||
return reportSetting
|
||||
.addParam("StockMoveId", stockMove.getId())
|
||||
.addParam("NumberToWords", number)
|
||||
.addParam("Locale", locale)
|
||||
.addParam("HeaderHeight", stockMove.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", stockMove.getPrintingSettings().getPdfFooterHeight())
|
||||
.addFormat(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File print(StockMove stockMove, String format) throws AxelorException {
|
||||
ReportSettings reportSettings = prepareReportSettings(stockMove, format);
|
||||
return reportSettings.generate().getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printStockMove(StockMove stockMove, String format)
|
||||
throws AxelorException, IOException {
|
||||
String fileName = getStockMoveFilesName(false, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.getFileLinkFromPdfFile(print(stockMove, format), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the printed stock move.
|
||||
*
|
||||
* @param plural if there is one or multiple stock moves.
|
||||
*/
|
||||
public String getStockMoveFilesName(boolean plural, String format) {
|
||||
|
||||
return I18n.get(plural ? "Stock moves" : "Stock move")
|
||||
+ " - "
|
||||
+ Beans.get(AppBaseService.class).getTodayDate().format(DateTimeFormatter.BASIC_ISO_DATE)
|
||||
+ "."
|
||||
+ format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName(StockMove stockMove) {
|
||||
|
||||
return I18n.get("Stock Move") + " " + stockMove.getStockMoveSeq();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package com.axelor.apps.stock.service.stockmove.print;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.tool.ModelTool;
|
||||
import com.axelor.apps.tool.ThrowConsumer;
|
||||
import com.axelor.apps.tool.file.PdfTool;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StockProductionRequestPrintService {
|
||||
|
||||
public String printStockProductionRequest(
|
||||
StockProductionRequest purchaseRequest, String formatPdf) throws AxelorException {
|
||||
|
||||
String fileName = getPurchaseRequestFilesName(false, formatPdf);
|
||||
|
||||
return PdfTool.getFileLinkFromPdfFile(print(purchaseRequest, formatPdf), fileName);
|
||||
}
|
||||
|
||||
public String printStockProductionRequests(List<Long> ids) throws IOException {
|
||||
List<File> printedPurchaseRequests = new ArrayList<>();
|
||||
ModelTool.apply(
|
||||
StockProductionRequest.class,
|
||||
ids,
|
||||
new ThrowConsumer<StockProductionRequest>() {
|
||||
|
||||
public void accept(StockProductionRequest purchaseRequest) throws Exception {
|
||||
printedPurchaseRequests.add(print(purchaseRequest, ReportSettings.FORMAT_PDF));
|
||||
}
|
||||
});
|
||||
String fileName = getPurchaseRequestFilesName(true, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.mergePdfToFileLink(printedPurchaseRequests, fileName);
|
||||
}
|
||||
|
||||
public File print(StockProductionRequest stockProductionRequest, String formatPdf)
|
||||
throws AxelorException {
|
||||
ReportSettings reportSettings = prepareReportSettings(stockProductionRequest, formatPdf);
|
||||
return reportSettings.generate().getFile();
|
||||
}
|
||||
|
||||
public ReportSettings prepareReportSettings(
|
||||
StockProductionRequest stockProductionRequest, String formatPdf) throws AxelorException {
|
||||
if (stockProductionRequest.getPrintingSettings() == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_MISSING_TEMPLATE),
|
||||
stockProductionRequest.getStockProductionRequestSeq()),
|
||||
stockProductionRequest);
|
||||
}
|
||||
String locale = ReportSettings.getPrintingLocale(null);
|
||||
String title = getFileName(stockProductionRequest);
|
||||
ReportSettings reportSetting =
|
||||
ReportFactory.createReport(IReport.STOCK_PRODUCTION_REQUEST, title + " - ${date}");
|
||||
|
||||
return reportSetting
|
||||
.addParam("StockProductionRequestId", stockProductionRequest.getId())
|
||||
.addParam("Locale", locale)
|
||||
.addParam("HeaderHeight", stockProductionRequest.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", stockProductionRequest.getPrintingSettings().getPdfFooterHeight())
|
||||
.addFormat(formatPdf);
|
||||
}
|
||||
|
||||
protected String getPurchaseRequestFilesName(boolean plural, String formatPdf) {
|
||||
return I18n.get(plural ? "Stock production requests" : "Stock production request")
|
||||
+ " - "
|
||||
+ Beans.get(AppBaseService.class).getTodayDate().format(DateTimeFormatter.BASIC_ISO_DATE)
|
||||
+ "."
|
||||
+ formatPdf;
|
||||
}
|
||||
|
||||
public String getFileName(StockProductionRequest StockProductionRequest) {
|
||||
return I18n.get("Stock production request")
|
||||
+ " "
|
||||
+ StockProductionRequest.getStockProductionRequestSeq();
|
||||
}
|
||||
}
|
||||
@ -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.stock.translation;
|
||||
|
||||
public interface ITranslation {
|
||||
|
||||
public static final String STOCK_APP_NAME = /*$$(*/ "value:Stock"; /*)*/
|
||||
|
||||
public static final String ABC_ANALYSIS_STOCK_LOCATION = /*$$(*/
|
||||
"AbcAnalysis.stockLocation"; /*)*/
|
||||
|
||||
public static final String PICKING_STOCK_MOVE_NOTE = /*$$(*/ "PickingStockMove.note"; /*)*/
|
||||
}
|
||||
@ -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.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.service.app.AppStockService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class AppStockController {
|
||||
|
||||
public void generateStockConfigurations(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Beans.get(AppStockService.class).generateStockConfigurations();
|
||||
|
||||
response.setReload(true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.repo.InventoryRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.stock.service.InventoryService;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.db.MetaFile;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.eclipse.birt.core.exception.BirtException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class InventoryController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
/**
|
||||
* Fonction appeler par le bouton imprimer
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws BirtException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void showInventory(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Inventory inventory = request.getContext().asType(Inventory.class);
|
||||
|
||||
String name = I18n.get("Inventory") + " " + inventory.getInventorySeq();
|
||||
|
||||
String fileLink =
|
||||
ReportFactory.createReport(IReport.INVENTORY, name + "-${date}")
|
||||
.addParam("InventoryId", inventory.getId())
|
||||
.addParam("Locale", ReportSettings.getPrintingLocale(null))
|
||||
.addParam(
|
||||
"activateBarCodeGeneration",
|
||||
Beans.get(AppBaseService.class).getAppBase().getActivateBarCodeGeneration())
|
||||
.addFormat(inventory.getFormatSelect())
|
||||
.generate()
|
||||
.getFileLink();
|
||||
|
||||
logger.debug("Printing " + name);
|
||||
|
||||
response.setView(ActionView.define(name).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void exportInventory(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Inventory inventory = request.getContext().asType(Inventory.class);
|
||||
inventory = Beans.get(InventoryRepository.class).find(inventory.getId());
|
||||
|
||||
String name = I18n.get("Inventory") + " " + inventory.getInventorySeq();
|
||||
MetaFile metaFile = Beans.get(InventoryService.class).exportInventoryAsCSV(inventory);
|
||||
|
||||
response.setView(
|
||||
ActionView.define(name)
|
||||
.add(
|
||||
"html",
|
||||
"ws/rest/com.axelor.meta.db.MetaFile/"
|
||||
+ metaFile.getId()
|
||||
+ "/content/download?v="
|
||||
+ metaFile.getVersion())
|
||||
.map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void importFile(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Inventory inventory =
|
||||
Beans.get(InventoryRepository.class)
|
||||
.find(request.getContext().asType(Inventory.class).getId());
|
||||
|
||||
Path filePath = Beans.get(InventoryService.class).importFile(inventory);
|
||||
response.setFlash(
|
||||
String.format(I18n.get(IExceptionMessage.INVENTORY_8), filePath.toString()));
|
||||
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateInventory(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Long id = request.getContext().asType(Inventory.class).getId();
|
||||
Inventory inventory = Beans.get(InventoryRepository.class).find(id);
|
||||
Beans.get(InventoryService.class).validateInventory(inventory);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Inventory inventory = request.getContext().asType(Inventory.class);
|
||||
inventory = Beans.get(InventoryRepository.class).find(inventory.getId());
|
||||
Beans.get(InventoryService.class).cancel(inventory);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void fillInventoryLineList(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Long inventoryId = (Long) request.getContext().get("id");
|
||||
if (inventoryId != null) {
|
||||
Inventory inventory = Beans.get(InventoryRepository.class).find(inventoryId);
|
||||
Boolean succeed = Beans.get(InventoryService.class).fillInventoryLineList(inventory);
|
||||
if (succeed == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.INVENTORY_9));
|
||||
} else {
|
||||
if (succeed) {
|
||||
response.setNotify(I18n.get(IExceptionMessage.INVENTORY_10));
|
||||
} else {
|
||||
response.setNotify(I18n.get(IExceptionMessage.INVENTORY_11));
|
||||
}
|
||||
}
|
||||
}
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInventorySequence(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
|
||||
Inventory inventory = request.getContext().asType(Inventory.class);
|
||||
SequenceService sequenceService = Beans.get(SequenceService.class);
|
||||
|
||||
if (sequenceService.isEmptyOrDraftSequenceNumber(inventory.getInventorySeq())) {
|
||||
|
||||
StockLocation stockLocation = inventory.getStockLocation();
|
||||
|
||||
response.setValue(
|
||||
"inventorySeq",
|
||||
Beans.get(InventoryService.class).getInventorySequence(stockLocation.getCompany()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void showStockMoves(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Inventory inventory = request.getContext().asType(Inventory.class);
|
||||
List<StockMove> stockMoveList = Beans.get(InventoryService.class).findStockMoves(inventory);
|
||||
ActionViewBuilder builder =
|
||||
ActionView.define(I18n.get("Internal Stock Moves"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("grid", "stock-move-grid")
|
||||
.add("form", "stock-move-form");
|
||||
if (stockMoveList.isEmpty()) {
|
||||
response.setFlash(I18n.get("No stock moves found for this inventory."));
|
||||
} else {
|
||||
builder
|
||||
.context("_showSingle", true)
|
||||
.domain(
|
||||
String.format(
|
||||
"self.originTypeSelect = '%s' AND self.originId = %s",
|
||||
StockMoveRepository.ORIGIN_INVENTORY, inventory.getId()));
|
||||
response.setView(builder.map());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.db.InventoryLine;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.InventoryLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.InventoryRepository;
|
||||
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
|
||||
import com.axelor.apps.stock.service.InventoryLineService;
|
||||
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.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class InventoryLineController {
|
||||
|
||||
@Inject private ProductRepository productRepository;
|
||||
private TrackingNumberRepository trackingNumberRepository;
|
||||
private InventoryLineRepository inventoryLineRepository;
|
||||
private InventoryRepository inventoryRepository;
|
||||
|
||||
private Product mProduct;
|
||||
|
||||
public void updateInventoryLine(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
InventoryLine inventoryLine = request.getContext().asType(InventoryLine.class);
|
||||
Inventory inventory =
|
||||
request.getContext().getParent() != null
|
||||
? request.getContext().getParent().asType(Inventory.class)
|
||||
: inventoryLine.getInventory();
|
||||
inventoryLine =
|
||||
Beans.get(InventoryLineService.class).updateInventoryLine(inventoryLine, inventory);
|
||||
response.setValue("rack", inventoryLine.getRack());
|
||||
response.setValue("currentQty", inventoryLine.getCurrentQty());
|
||||
}
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) {
|
||||
InventoryLine inventoryLine = request.getContext().asType(InventoryLine.class);
|
||||
Inventory inventory =
|
||||
request.getContext().getParent() != null
|
||||
? request.getContext().getParent().asType(Inventory.class)
|
||||
: inventoryLine.getInventory();
|
||||
inventoryLine = Beans.get(InventoryLineService.class).compute(inventoryLine, inventory);
|
||||
response.setValues(inventoryLine);
|
||||
}
|
||||
|
||||
public void setProdut(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
|
||||
if (mProduct != null) {
|
||||
System.out.println("************* set product");
|
||||
Long id = ((Inventory) context.get("inventory")).getId();
|
||||
Inventory inventory = Beans.get(InventoryRepository.class).find(id);
|
||||
|
||||
System.out.println("*******" + mProduct.getId().toString());
|
||||
|
||||
StockLocationLine sll =
|
||||
inventory
|
||||
.getStockLocation()
|
||||
.getStockLocationLineList()
|
||||
.stream()
|
||||
.filter(
|
||||
t -> {
|
||||
System.out.println(
|
||||
mProduct.getId().toString()
|
||||
+ " || "
|
||||
+ t.getProduct().getId().toString()
|
||||
+ " ******* "
|
||||
+ (mProduct.getId().longValue() == t.getProduct().getId().longValue()));
|
||||
return (t.getProduct().getId().longValue() == mProduct.getId().longValue());
|
||||
})
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
BigDecimal currentQty = BigDecimal.ZERO;
|
||||
if (sll != null) {
|
||||
System.out.println("ssl" + sll.toString());
|
||||
currentQty = sll.getCurrentQty();
|
||||
}
|
||||
response.setValue("product", mProduct);
|
||||
response.setValue("unit", mProduct.getUnit());
|
||||
response.setValue("currentQty", currentQty);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFromParameter(ActionRequest request, ActionResponse response) {
|
||||
|
||||
InventoryLine inventoryLine = request.getContext().asType(InventoryLine.class);
|
||||
|
||||
Map<String, Object> requestData = request.getData();
|
||||
final Map<String, Object> jsonContextValues = (Map<String, Object>) requestData.get("context");
|
||||
Long id = Long.valueOf(jsonContextValues.get("vv").toString());
|
||||
|
||||
System.out.println("setFromParameter ****************" + id.toString());
|
||||
|
||||
Product product = Beans.get(ProductRepository.class).find(id);
|
||||
|
||||
System.out.println("setFromParameter ****************" + product.toString());
|
||||
|
||||
response.setValue("product", product);
|
||||
}
|
||||
|
||||
public void setInventaire(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
Long stockLocationId =
|
||||
new Long((Integer) ((Map) request.getContext().get("stockLocation")).get("id"));
|
||||
Inventory inventory =
|
||||
Beans.get(InventoryRepository.class)
|
||||
.all()
|
||||
.filter("self.stockLocation.id = ?1", stockLocationId)
|
||||
.order("-createdOn")
|
||||
.fetchOne();
|
||||
|
||||
String code = context.get("productHolder").toString();
|
||||
|
||||
Product product = productRepository.findByCode(code);
|
||||
|
||||
if (product != null) {
|
||||
response.setValue("product", product);
|
||||
response.setValue("unit", product.getUnit());
|
||||
response.setValue("inventory", inventory);
|
||||
// response.setView(
|
||||
// ActionView.define("Inventory lines")
|
||||
// .model(InventoryLine.class.getName())
|
||||
// .add("form", "inventory-reports-wizard-form2")
|
||||
// .param("show-toolbar", "false")
|
||||
// .param("show-confirm", "false")
|
||||
// .param("popup-save", "false")
|
||||
// .param("show-toolbar","false")
|
||||
// .param("popup", "true")
|
||||
// .param("forceEdit", "true")
|
||||
// .context("_product", product)
|
||||
// .context("_unit", product.getUnit())
|
||||
// .context("_inventory", inventory)
|
||||
// .context("_showRecord", inventoryLine.getId())
|
||||
// .map());
|
||||
} else {
|
||||
String strWithoutSlash = String.join("%", code.split("/"));
|
||||
String strWithoutDash = String.join("%", strWithoutSlash.split("_"));
|
||||
product =
|
||||
productRepository.all().filter("self.code like '" + strWithoutDash + "'").fetchOne();
|
||||
response.setValue("product", product);
|
||||
response.setValue("unit", product.getUnit());
|
||||
response.setValue("inventory", inventory);
|
||||
// response.setFlash("Veuillez contacter votre administrateur");
|
||||
}
|
||||
}
|
||||
|
||||
public void setInventoryLine(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
Context context = request.getContext();
|
||||
|
||||
Inventory inventory = ((Inventory) context.get("inventory"));
|
||||
// Long stockLocationId = new Long((Integer) ((Map)
|
||||
// request.getContext().get("inventory")).get("id"));
|
||||
// Long trackingNumberId = new Long((Integer) ((Map)
|
||||
// request.getContext().get("trackingNumber")).get("id"));
|
||||
TrackingNumber trackingNumber = ((TrackingNumber) context.get("trackingNumber"));
|
||||
Product product = ((Product) context.get("product"));
|
||||
|
||||
// Inventory inventory = Beans.get(InventoryRepository.class).find(stockLocationId);
|
||||
// TrackingNumber trackingNumber =
|
||||
// Beans.get(TrackingNumberRepository.class).find(trackingNumberId);
|
||||
|
||||
System.out.println(inventory.toString());
|
||||
System.out.println(trackingNumber);
|
||||
System.out.println(product.toString());
|
||||
|
||||
// Product product = productRepository.find(productId);
|
||||
// Inventory inventory = inventoryRepository.find(inventoryId);
|
||||
// TrackingNumber trackingNumber = trackingNumberRepository.find(trackingNumberId);
|
||||
|
||||
if (inventory == null || product == null) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_NO_VALUE, I18n.get("Cannot find the data"));
|
||||
}
|
||||
|
||||
Integer countingType = Integer.parseInt(context.get("countingTypeSelect").toString());
|
||||
BigDecimal firstCounting = (BigDecimal) context.get("firstCounting");
|
||||
BigDecimal secondCounting = (BigDecimal) context.get("secondCounting");
|
||||
BigDecimal controlCounting = (BigDecimal) context.get("controlCounting");
|
||||
|
||||
Beans.get(InventoryLineService.class)
|
||||
.setInventoryLine(
|
||||
inventory,
|
||||
product,
|
||||
trackingNumber,
|
||||
countingType,
|
||||
firstCounting,
|
||||
secondCounting,
|
||||
controlCounting);
|
||||
response.setFlash("Updated Successfully");
|
||||
// response.setReload(true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.repo.LogisticalFormRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormError;
|
||||
import com.axelor.apps.stock.exception.LogisticalFormWarning;
|
||||
import com.axelor.apps.stock.service.LogisticalFormService;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
public class LogisticalFormController {
|
||||
|
||||
public void addStockMove(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> stockMoveMap =
|
||||
(Map<String, Object>) request.getContext().get("stockMove");
|
||||
if (stockMoveMap != null) {
|
||||
StockMove stockMove = Mapper.toBean(StockMove.class, stockMoveMap);
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
|
||||
if (stockMove.getStockMoveLineList() != null) {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
LogisticalFormService logisticalFormService = Beans.get(LogisticalFormService.class);
|
||||
|
||||
logisticalFormService.addDetailLines(logisticalForm, stockMove);
|
||||
response.setValue("logisticalFormLineList", logisticalForm.getLogisticalFormLineList());
|
||||
response.setValue("$stockMove", null);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void computeTotals(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
Beans.get(LogisticalFormService.class).computeTotals(logisticalForm);
|
||||
response.setValue("totalNetMass", logisticalForm.getTotalNetMass());
|
||||
response.setValue("totalGrossMass", logisticalForm.getTotalGrossMass());
|
||||
response.setValue("totalVolume", logisticalForm.getTotalVolume());
|
||||
} catch (LogisticalFormError e) {
|
||||
response.setError(e.getLocalizedMessage());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkLines(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
LogisticalFormService logisticalFormService = Beans.get(LogisticalFormService.class);
|
||||
|
||||
logisticalFormService.sortLines(logisticalForm);
|
||||
logisticalFormService.checkLines(logisticalForm);
|
||||
} catch (LogisticalFormWarning e) {
|
||||
response.setAlert(e.getLocalizedMessage());
|
||||
} catch (LogisticalFormError e) {
|
||||
response.setError(e.getLocalizedMessage());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStockMoveDomain(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
String domain = Beans.get(LogisticalFormService.class).getStockMoveDomain(logisticalForm);
|
||||
response.setAttr("$stockMove", "domain", domain);
|
||||
|
||||
if (logisticalForm.getDeliverToCustomerPartner() == null) {
|
||||
response.setNotify(I18n.get("Deliver to customer is not set."));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void processCollected(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
logisticalForm = Beans.get(LogisticalFormRepository.class).find(logisticalForm.getId());
|
||||
Beans.get(LogisticalFormService.class).processCollected(logisticalForm);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCustomerAccountNumberToCarrier(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
Optional<String> customerAccountNumberToCarrier =
|
||||
Beans.get(LogisticalFormService.class).getCustomerAccountNumberToCarrier(logisticalForm);
|
||||
response.setValue(
|
||||
"customerAccountNumberToCarrier", customerAccountNumberToCarrier.orElse(null));
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshProductNetMass(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalForm logisticalForm = request.getContext().asType(LogisticalForm.class);
|
||||
LogisticalFormService logisticalFormService = Beans.get(LogisticalFormService.class);
|
||||
logisticalFormService.updateProductNetMass(logisticalForm);
|
||||
response.setValue("logisticalFormLineList", logisticalForm.getLogisticalFormLineList());
|
||||
response.setValue("totalNetMass", logisticalForm.getTotalNetMass());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.db.LogisticalForm;
|
||||
import com.axelor.apps.stock.db.LogisticalFormLine;
|
||||
import com.axelor.apps.stock.service.LogisticalFormLineService;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Singleton
|
||||
public class LogisticalFormLineController {
|
||||
|
||||
public void setQty(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalFormLine logisticalFormLine = getLogisticalFormLine(request);
|
||||
if (logisticalFormLine.getQty() == null) {
|
||||
BigDecimal qty =
|
||||
Beans.get(LogisticalFormLineService.class).getUnspreadQty(logisticalFormLine);
|
||||
response.setValue("qty", qty);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStockMoveLineDomain(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalFormLine logisticalFormLine = getLogisticalFormLine(request);
|
||||
String domain =
|
||||
Beans.get(LogisticalFormLineService.class).getStockMoveLineDomain(logisticalFormLine);
|
||||
response.setAttr("stockMoveLine", "domain", domain);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void initParcelPallet(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
LogisticalFormLine logisticalFormLine = getLogisticalFormLine(request);
|
||||
Beans.get(LogisticalFormLineService.class).initParcelPallet(logisticalFormLine);
|
||||
response.setValue("parcelPalletNumber", logisticalFormLine.getParcelPalletNumber());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
private LogisticalFormLine getLogisticalFormLine(ActionRequest request) {
|
||||
LogisticalFormLine logisticalFormLine = request.getContext().asType(LogisticalFormLine.class);
|
||||
|
||||
if (logisticalFormLine.getLogisticalForm() == null) {
|
||||
logisticalFormLine.setLogisticalForm(
|
||||
request.getContext().getParent().asType(LogisticalForm.class));
|
||||
}
|
||||
|
||||
return logisticalFormLine;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.service.StockLocationLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.stock.service.WeightedAveragePriceService;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Singleton;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class ProductStockController {
|
||||
|
||||
public void setStockPerDay(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
|
||||
Long productId = Long.parseLong(context.get("id").toString());
|
||||
Long locationId = Long.parseLong(context.get("locationId").toString());
|
||||
LocalDate fromDate = LocalDate.parse(context.get("stockFromDate").toString());
|
||||
LocalDate toDate = LocalDate.parse(context.get("stockToDate").toString());
|
||||
|
||||
List<Map<String, Object>> stocks =
|
||||
Beans.get(StockMoveService.class).getStockPerDate(locationId, productId, fromDate, toDate);
|
||||
response.setValue("$stockPerDayList", stocks);
|
||||
}
|
||||
|
||||
public void displayStockMoveLine(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
if (context.get("date") != null && context.getParent().get("locationId") != null) {
|
||||
LocalDate stockDate = LocalDate.parse(context.get("date").toString());
|
||||
Long locationId = Long.parseLong(context.getParent().get("locationId").toString());
|
||||
|
||||
if (request.getContext().getParent().get("id") != null)
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock Move Lines"))
|
||||
.model(StockMoveLine.class.getName())
|
||||
.add("grid", "stock-move-line-all-grid")
|
||||
.add("form", "stock-move-line-all-form")
|
||||
.domain(
|
||||
"self.product.id = :id AND (self.stockMove.fromStockLocation.id = :locationId OR self.stockMove.toStockLocation.id = :locationId) AND self.stockMove.statusSelect != :status AND (self.stockMove.estimatedDate <= :stockDate OR self.stockMove.realDate <= :stockDate)")
|
||||
.context("id", request.getContext().getParent().get("id"))
|
||||
.context("locationId", locationId)
|
||||
.context("status", StockMoveRepository.STATUS_CANCELED)
|
||||
.context("stockDate", stockDate)
|
||||
.map());
|
||||
response.setCanClose(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateStockLocation(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Product product = request.getContext().asType(Product.class);
|
||||
StockLocationLineService stockLocationLineService = Beans.get(StockLocationLineService.class);
|
||||
if (product.getId() == null) {
|
||||
return;
|
||||
}
|
||||
product = Beans.get(ProductRepository.class).find(product.getId());
|
||||
List<StockLocationLine> stockLocationLineList =
|
||||
stockLocationLineService.getStockLocationLines(product);
|
||||
|
||||
for (StockLocationLine stockLocationLine : stockLocationLineList) {
|
||||
stockLocationLineService.updateStockLocationFromProduct(stockLocationLine, product);
|
||||
}
|
||||
Beans.get(WeightedAveragePriceService.class).computeAvgPriceForProduct(product);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.db.StockCorrection;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.repo.StockCorrectionRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.StockCorrectionService;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class StockCorrectionController {
|
||||
|
||||
public void setDefaultDetails(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Long stockLocaLocationLineId =
|
||||
Long.valueOf(request.getContext().get("_stockLocationLineId").toString());
|
||||
StockLocationLine stockLocationLine =
|
||||
Beans.get(StockLocationLineRepository.class).find(stockLocaLocationLineId);
|
||||
Map<String, Object> stockCorrectionDetails;
|
||||
|
||||
if (stockLocationLine != null) {
|
||||
stockCorrectionDetails =
|
||||
Beans.get(StockCorrectionService.class).fillDefaultValues(stockLocationLine);
|
||||
response.setValues(stockCorrectionDetails);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultQtys(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
StockCorrection stockCorrection = request.getContext().asType(StockCorrection.class);
|
||||
|
||||
Map<String, Object> stockCorrectionQtys =
|
||||
Beans.get(StockCorrectionService.class).fillDeafultQtys(stockCorrection);
|
||||
response.setValues(stockCorrectionQtys);
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void validate(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Long id = request.getContext().asType(StockCorrection.class).getId();
|
||||
StockCorrection stockCorrection = Beans.get(StockCorrectionRepository.class).find(id);
|
||||
boolean success = Beans.get(StockCorrectionService.class).validate(stockCorrection);
|
||||
if (success) {
|
||||
response.setReload(true);
|
||||
} else {
|
||||
response.setError(I18n.get(IExceptionMessage.STOCK_CORRECTION_2));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void showGeneratedStockMove(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Long stockCorrectionId = request.getContext().asType(StockCorrection.class).getId();
|
||||
StockMove stockMove =
|
||||
Beans.get(StockMoveRepository.class)
|
||||
.all()
|
||||
.filter(
|
||||
"self.originTypeSelect = ? AND self.originId = ?",
|
||||
StockMoveRepository.ORIGIN_STOCK_CORRECTION,
|
||||
stockCorrectionId)
|
||||
.fetchOne();
|
||||
if (stockMove != null) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock move"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("grid", "stock-move-grid")
|
||||
.add("form", "stock-move-form")
|
||||
.context("_showRecord", stockMove.getId().toString())
|
||||
.map());
|
||||
} else {
|
||||
response.setFlash(I18n.get("No record found"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, 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.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.db.StockHistoryLine;
|
||||
import com.axelor.apps.stock.service.StockHistoryService;
|
||||
import com.axelor.apps.stock.service.StockHistoryServiceImpl;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.db.MetaFile;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class StockHistoryController {
|
||||
|
||||
/**
|
||||
* Called from stock history form view, on new and on date change. Call {@link
|
||||
* StockHistoryService#computeStockHistoryLineList(Long, Long, Long, LocalDate, LocalDate)}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void fillStockHistoryLineList(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Context context = request.getContext();
|
||||
Long productId = null;
|
||||
if (context.get("product") != null) {
|
||||
productId = Long.parseLong(((LinkedHashMap) context.get("product")).get("id").toString());
|
||||
}
|
||||
Long companyId = null;
|
||||
if (context.get("company") != null) {
|
||||
companyId = Long.parseLong(((LinkedHashMap) context.get("company")).get("id").toString());
|
||||
}
|
||||
Long stockLocationId = null;
|
||||
if (context.get("stockLocation") != null) {
|
||||
stockLocationId =
|
||||
Long.parseLong(((LinkedHashMap) context.get("stockLocation")).get("id").toString());
|
||||
}
|
||||
Object beginDateContext = context.get("beginDate");
|
||||
LocalDate beginDate = null;
|
||||
if (beginDateContext != null) {
|
||||
beginDate = LocalDate.parse(beginDateContext.toString());
|
||||
}
|
||||
|
||||
Object endDateContext = context.get("endDate");
|
||||
LocalDate endDate = null;
|
||||
if (endDateContext != null) {
|
||||
endDate = LocalDate.parse(endDateContext.toString());
|
||||
}
|
||||
|
||||
List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();
|
||||
if (productId != null
|
||||
&& companyId != null
|
||||
&& stockLocationId != null
|
||||
&& beginDate != null
|
||||
&& endDate != null) {
|
||||
stockHistoryLineList =
|
||||
Beans.get(StockHistoryService.class)
|
||||
.computeStockHistoryLineList(
|
||||
productId, companyId, stockLocationId, beginDate, endDate);
|
||||
}
|
||||
response.setValue("$stockHistoryLineList", stockHistoryLineList);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from stock history form view, on new and on date change. Call {@link
|
||||
* StockHistoryService#computeStockHistoryLineList(Long, Long, Long, LocalDate, LocalDate)}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void fillStockHistoryLineListPerDate(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Context context = request.getContext();
|
||||
Long productId = null;
|
||||
if (context.get("product") != null) {
|
||||
productId = Long.parseLong(((LinkedHashMap) context.get("product")).get("id").toString());
|
||||
}
|
||||
Long companyId = null;
|
||||
if (context.get("company") != null) {
|
||||
companyId = Long.parseLong(((LinkedHashMap) context.get("company")).get("id").toString());
|
||||
}
|
||||
Long stockLocationId = null;
|
||||
if (context.get("stockLocation") != null) {
|
||||
stockLocationId =
|
||||
Long.parseLong(((LinkedHashMap) context.get("stockLocation")).get("id").toString());
|
||||
}
|
||||
|
||||
Object beginDateContext = context.get("beginDate");
|
||||
|
||||
LocalDate beginDate = null;
|
||||
if (beginDateContext != null) {
|
||||
beginDate = LocalDate.parse(beginDateContext.toString());
|
||||
}
|
||||
|
||||
Integer searchTypeSelect = Integer.parseInt((String) context.get("productSearchTypeSelect"));
|
||||
|
||||
Long categoryId = null;
|
||||
if (context.get("familleProduit") != null) {
|
||||
categoryId =
|
||||
Long.parseLong(((LinkedHashMap) context.get("familleProduit")).get("id").toString());
|
||||
}
|
||||
|
||||
Long trackingNumberId = null;
|
||||
if (context.get("trackingNumber") != null) {
|
||||
trackingNumberId =
|
||||
Long.parseLong(((LinkedHashMap) context.get("trackingNumber")).get("id").toString());
|
||||
}
|
||||
|
||||
System.out.println("**************searchTypeSelect**********************");
|
||||
System.out.println(searchTypeSelect);
|
||||
System.out.println(categoryId);
|
||||
System.out.println(request.getContext().get("familleProduit"));
|
||||
System.out.println("**************searchTypeSelect**********************");
|
||||
|
||||
LocalDate endDate = LocalDate.now();
|
||||
|
||||
List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();
|
||||
// if (
|
||||
// productId != null
|
||||
// && companyId != null
|
||||
// && stockLocationId != null
|
||||
// && beginDate != null
|
||||
// ) {
|
||||
stockHistoryLineList =
|
||||
Beans.get(StockHistoryService.class)
|
||||
.compuHistoryLinesPerDate(
|
||||
productId,
|
||||
companyId,
|
||||
stockLocationId,
|
||||
beginDate,
|
||||
endDate,
|
||||
categoryId,
|
||||
trackingNumberId,
|
||||
searchTypeSelect);
|
||||
// }
|
||||
|
||||
response.setValue("$stockHistoryLineList", stockHistoryLineList);
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void exportStockHistoryLines(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException, IOException {
|
||||
|
||||
Context context = request.getContext();
|
||||
|
||||
List<HashMap<String, Object>> stockHistoryLineList = new ArrayList<>();
|
||||
|
||||
if (context.get("stockHistoryLineList") != null) {
|
||||
stockHistoryLineList = (List<HashMap<String, Object>>) context.get("stockHistoryLineList");
|
||||
}
|
||||
|
||||
MetaFile metaFile = Beans.get(StockHistoryServiceImpl.class).exportToCSV(stockHistoryLineList);
|
||||
|
||||
response.setView(
|
||||
ActionView.define("name")
|
||||
.add(
|
||||
"html",
|
||||
"ws/rest/com.axelor.meta.db.MetaFile/"
|
||||
+ metaFile.getId()
|
||||
+ "/content/download?v="
|
||||
+ metaFile.getVersion())
|
||||
.map());
|
||||
}
|
||||
}
|
||||
@ -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.stock.web;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.db.Wizard;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.report.IReport;
|
||||
import com.axelor.apps.stock.service.StockLocationService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.eclipse.birt.core.exception.BirtException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class StockLocationController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
/**
|
||||
* Method that generate inventory as a pdf
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws BirtException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void print(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
|
||||
Context context = request.getContext();
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedHashMap<String, Object> stockLocationMap =
|
||||
(LinkedHashMap<String, Object>) context.get("_stockLocation");
|
||||
Integer stockLocationId = (Integer) stockLocationMap.get("id");
|
||||
StockLocationService stockLocationService = Beans.get(StockLocationService.class);
|
||||
StockLocationRepository stockLocationRepository = Beans.get(StockLocationRepository.class);
|
||||
|
||||
StockLocation stockLocation =
|
||||
stockLocationId != null ? stockLocationRepository.find(new Long(stockLocationId)) : null;
|
||||
String locationIds = "";
|
||||
|
||||
String printType = (String) context.get("printingType");
|
||||
String exportType = (String) context.get("exportTypeSelect");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> lstSelectedLocations = (List<Integer>) context.get("_ids");
|
||||
if (lstSelectedLocations != null) {
|
||||
for (Integer it : lstSelectedLocations) {
|
||||
Set<Long> idSet =
|
||||
stockLocationService.getContentStockLocationIds(
|
||||
stockLocationRepository.find(new Long(it)));
|
||||
if (!idSet.isEmpty()) {
|
||||
locationIds += Joiner.on(",").join(idSet) + ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locationIds.equals("")) {
|
||||
locationIds = locationIds.substring(0, locationIds.length() - 1);
|
||||
stockLocation = stockLocationRepository.find(new Long(lstSelectedLocations.get(0)));
|
||||
} else if (stockLocation != null && stockLocation.getId() != null) {
|
||||
Set<Long> idSet =
|
||||
stockLocationService.getContentStockLocationIds(
|
||||
stockLocationRepository.find(stockLocation.getId()));
|
||||
if (!idSet.isEmpty()) {
|
||||
locationIds = Joiner.on(",").join(idSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (!locationIds.equals("")) {
|
||||
String language = ReportSettings.getPrintingLocale(null);
|
||||
|
||||
String title = I18n.get("Stock location");
|
||||
if (stockLocation.getName() != null) {
|
||||
title =
|
||||
lstSelectedLocations == null
|
||||
? I18n.get("Stock location") + " " + stockLocation.getName()
|
||||
: I18n.get("Stock location(s)");
|
||||
}
|
||||
|
||||
if (stockLocationService.isConfigMissing(stockLocation, Integer.parseInt(printType))) {
|
||||
response.setNotify(I18n.get(IExceptionMessage.STOCK_CONFIGURATION_MISSING));
|
||||
}
|
||||
|
||||
String fileLink =
|
||||
ReportFactory.createReport(IReport.STOCK_LOCATION, title + "-${date}")
|
||||
.addParam("StockLocationId", locationIds)
|
||||
.addParam("Locale", language)
|
||||
.addFormat(exportType)
|
||||
.addParam("PrintType", printType)
|
||||
.generate()
|
||||
.getFileLink();
|
||||
|
||||
logger.debug("Printing " + title);
|
||||
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
|
||||
} else {
|
||||
response.setFlash(I18n.get(IExceptionMessage.LOCATION_2));
|
||||
}
|
||||
response.setCanClose(true);
|
||||
}
|
||||
|
||||
public void setStocklocationValue(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockLocation stockLocation = request.getContext().asType(StockLocation.class);
|
||||
|
||||
response.setValue(
|
||||
"stockLocationValue",
|
||||
Beans.get(StockLocationService.class).getStockLocationValue(stockLocation));
|
||||
}
|
||||
|
||||
public void openPrintWizard(ActionRequest request, ActionResponse response) {
|
||||
StockLocation stockLocation = request.getContext().asType(StockLocation.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> lstSelectedLocations = (List<Integer>) request.getContext().get("_ids");
|
||||
|
||||
response.setView(
|
||||
ActionView.define(I18n.get(IExceptionMessage.STOCK_LOCATION_PRINT_WIZARD_TITLE))
|
||||
.model(Wizard.class.getName())
|
||||
.add("form", "stock-location-print-wizard-form")
|
||||
.param("popup", "true")
|
||||
.param("show-toolbar", "false")
|
||||
.param("show-confirm", "false")
|
||||
.param("popup-save", "false")
|
||||
.context("_ids", lstSelectedLocations)
|
||||
.context("_stockLocation", stockLocation)
|
||||
.map());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,903 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.base.db.CancelReason;
|
||||
import com.axelor.apps.base.db.PrintingSettings;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.service.BarcodeGeneratorService;
|
||||
import com.axelor.apps.base.service.TradingNameService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.StockMoveLineLocation;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.stock.service.StockMoveToolService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.ConformityCertificatePrintService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.PickingStockMovePrintService;
|
||||
import com.axelor.apps.stock.service.stockmove.print.StockMovePrintService;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
import com.axelor.common.ObjectUtils;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
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.inject.Beans;
|
||||
import com.axelor.meta.MetaFiles;
|
||||
import com.axelor.meta.db.MetaFile;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.Query;
|
||||
import javax.validation.ValidationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class StockMoveController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
public void plan(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
try {
|
||||
Beans.get(StockMoveService.class)
|
||||
.plan(Beans.get(StockMoveRepository.class).find(stockMove.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void manageBackorder(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Manage backorder?"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("form", "popup-stock-move-backorder-form")
|
||||
.param("popup", "reload")
|
||||
.param("show-toolbar", "false")
|
||||
.param("show-confirm", "false")
|
||||
.param("popup-save", "false")
|
||||
.context("_showRecord", stockMove.getId())
|
||||
.map());
|
||||
}
|
||||
|
||||
public void realize(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMove stockMoveFromRequest = request.getContext().asType(StockMove.class);
|
||||
|
||||
try {
|
||||
StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromRequest.getId());
|
||||
String newSeq = Beans.get(StockMoveService.class).realize(stockMove);
|
||||
|
||||
response.setReload(true);
|
||||
|
||||
if (newSeq != null) {
|
||||
if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING) {
|
||||
response.setFlash(
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_INCOMING_PARTIAL_GENERATED), newSeq));
|
||||
} else if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_OUTGOING) {
|
||||
response.setFlash(
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_OUTGOING_PARTIAL_GENERATED), newSeq));
|
||||
} else {
|
||||
response.setFlash(String.format(I18n.get(IExceptionMessage.STOCK_MOVE_9), newSeq));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void draft(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
try {
|
||||
Beans.get(StockMoveService.class)
|
||||
.goBackToDraft(Beans.get(StockMoveRepository.class).find(stockMove.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
try {
|
||||
Beans.get(StockMoveService.class)
|
||||
.cancel(
|
||||
Beans.get(StockMoveRepository.class).find(stockMove.getId()),
|
||||
stockMove.getCancelReason(),
|
||||
stockMove.getCancelReasonStr());
|
||||
response.setCanClose(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void massDraft(ActionRequest request, ActionResponse response) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> requestIds = (List<Long>) request.getContext().get("_ids");
|
||||
|
||||
if (requestIds == null || requestIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!requestIds.isEmpty()) {
|
||||
Beans.get(StockMoveService.class).massDraft(requestIds);
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void massCancel(ActionRequest request, ActionResponse response) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> requestIds = (List<Long>) request.getContext().get("_ids");
|
||||
CancelReason raison = (CancelReason) request.getContext().get("cancelRaison");
|
||||
String raisonStr = (String) request.getContext().get("cancelRaisonStr");
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
|
||||
System.out.println("*********************************************");
|
||||
System.out.println(raisonStr);
|
||||
System.out.println(raison);
|
||||
System.out.println(stockMove.getCancelReason());
|
||||
System.out.println(stockMove.getCancelReasonStr());
|
||||
System.out.println("*********************************************");
|
||||
|
||||
if (requestIds == null || requestIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!requestIds.isEmpty()) {
|
||||
Beans.get(StockMoveService.class).massCancel(requestIds, raison, raisonStr);
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void massPlan(ActionRequest request, ActionResponse response) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> requestIds = (List<Long>) request.getContext().get("_ids");
|
||||
|
||||
if (requestIds == null || requestIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!requestIds.isEmpty()) {
|
||||
Beans.get(StockMoveService.class).massPlan(requestIds);
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void massRealize(ActionRequest request, ActionResponse response) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> requestIds = (List<Long>) request.getContext().get("_ids");
|
||||
|
||||
if (requestIds == null || requestIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!requestIds.isEmpty()) {
|
||||
Beans.get(StockMoveService.class).massRealize(requestIds);
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called from stock move form and grid view. Print one or more stock move as PDF
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void printStockMove(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
String fileLink;
|
||||
String title;
|
||||
|
||||
try {
|
||||
StockMovePrintService stockMovePrintService = Beans.get(StockMovePrintService.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(request.getContext().get("_ids"))) {
|
||||
List<Long> ids =
|
||||
(List)
|
||||
(((List) context.get("_ids"))
|
||||
.stream()
|
||||
.filter(ObjectUtils::notEmpty)
|
||||
.map(input -> Long.parseLong(input.toString()))
|
||||
.collect(Collectors.toList()));
|
||||
fileLink = stockMovePrintService.printStockMoves(ids);
|
||||
title = I18n.get("Stock Moves");
|
||||
} else if (context.get("id") != null) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
title = stockMovePrintService.getFileName(stockMove);
|
||||
fileLink = stockMovePrintService.printStockMove(stockMove, ReportSettings.FORMAT_PDF);
|
||||
logger.debug("Printing " + title);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_PRINT));
|
||||
}
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called from stock move form and grid view. Print one or more stock move as PDF
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void printPickingStockMove(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
String fileLink;
|
||||
String title;
|
||||
String userType = (String) context.get("_userType");
|
||||
|
||||
try {
|
||||
|
||||
PickingStockMovePrintService pickingstockMovePrintService =
|
||||
Beans.get(PickingStockMovePrintService.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(context.get("_ids"))) {
|
||||
List<Long> ids =
|
||||
(List)
|
||||
(((List) context.get("_ids"))
|
||||
.stream()
|
||||
.filter(ObjectUtils::notEmpty)
|
||||
.map(input -> Long.parseLong(input.toString()))
|
||||
.collect(Collectors.toList()));
|
||||
fileLink = pickingstockMovePrintService.printStockMoves(ids, userType);
|
||||
title = I18n.get("Stock Moves");
|
||||
} else if (context.get("id") != null) {
|
||||
StockMove stockMove = context.asType(StockMove.class);
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
title = pickingstockMovePrintService.getFileName(stockMove);
|
||||
fileLink =
|
||||
pickingstockMovePrintService.printStockMove(
|
||||
stockMove, ReportSettings.FORMAT_PDF, userType);
|
||||
logger.debug("Printing " + title);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_PRINT));
|
||||
}
|
||||
response.setReload(true);
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from stock move form view. Print conformity certificate for the given stock move.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void printConformityCertificate(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
String fileLink;
|
||||
String title;
|
||||
|
||||
try {
|
||||
|
||||
ConformityCertificatePrintService conformityCertificatePrintService =
|
||||
Beans.get(ConformityCertificatePrintService.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(context.get("_ids"))) {
|
||||
List<Long> ids =
|
||||
(List)
|
||||
(((List) context.get("_ids"))
|
||||
.stream()
|
||||
.filter(ObjectUtils::notEmpty)
|
||||
.map(input -> Long.parseLong(input.toString()))
|
||||
.collect(Collectors.toList()));
|
||||
fileLink = conformityCertificatePrintService.printConformityCertificates(ids);
|
||||
title = I18n.get("Conformity Certificates");
|
||||
} else if (context.get("id") != null) {
|
||||
|
||||
StockMove stockMove = context.asType(StockMove.class);
|
||||
title = conformityCertificatePrintService.getFileName(stockMove);
|
||||
fileLink =
|
||||
conformityCertificatePrintService.printConformityCertificate(
|
||||
stockMove, ReportSettings.FORMAT_PDF);
|
||||
|
||||
logger.debug("Printing " + title);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.STOCK_MOVE_PRINT));
|
||||
}
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void viewDirection(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
try {
|
||||
Map<String, Object> result = Beans.get(StockMoveService.class).viewDirection(stockMove);
|
||||
Map<String, Object> mapView = new HashMap<>();
|
||||
mapView.put("title", I18n.get("Map"));
|
||||
mapView.put("resource", result.get("url"));
|
||||
mapView.put("viewType", "html");
|
||||
response.setView(mapView);
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void splitStockMoveLinesUnit(ActionRequest request, ActionResponse response) {
|
||||
List<StockMoveLine> stockMoveLines =
|
||||
(List<StockMoveLine>) request.getContext().get("stockMoveLineList");
|
||||
if (stockMoveLines == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_14));
|
||||
return;
|
||||
}
|
||||
Boolean selected =
|
||||
Beans.get(StockMoveService.class)
|
||||
.splitStockMoveLinesUnit(stockMoveLines, new BigDecimal(1));
|
||||
|
||||
if (!selected) response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_15));
|
||||
response.setReload(true);
|
||||
response.setCanClose(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void splitStockMoveLinesSpecial(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
List<HashMap> selectedStockMoveLineMapList =
|
||||
(List<HashMap>) request.getContext().get("stockMoveLineList");
|
||||
Map stockMoveMap = (Map<String, Object>) request.getContext().get("stockMove");
|
||||
if (selectedStockMoveLineMapList == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_14));
|
||||
return;
|
||||
}
|
||||
|
||||
List<StockMoveLine> stockMoveLineList = new ArrayList<>();
|
||||
StockMoveLineRepository stockMoveLineRepo = Beans.get(StockMoveLineRepository.class);
|
||||
for (HashMap map : selectedStockMoveLineMapList) {
|
||||
StockMoveLine stockMoveLine = (StockMoveLine) Mapper.toBean(StockMoveLine.class, map);
|
||||
stockMoveLineList.add(stockMoveLineRepo.find(stockMoveLine.getId()));
|
||||
}
|
||||
|
||||
if (stockMoveLineList.isEmpty()) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_15));
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal splitQty = new BigDecimal(request.getContext().get("splitQty").toString());
|
||||
if (splitQty == null || splitQty.compareTo(BigDecimal.ZERO) < 1) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_16));
|
||||
return;
|
||||
}
|
||||
|
||||
StockMove stockMove = Mapper.toBean(StockMove.class, stockMoveMap);
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
Beans.get(StockMoveService.class)
|
||||
.splitStockMoveLinesSpecial(stockMove, stockMoveLineList, splitQty);
|
||||
response.setCanClose(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
// sophal
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void splitStockMoveLinesSpecial2(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
List<HashMap> selectedStockMoveLineMapList =
|
||||
(List<HashMap>) request.getContext().get("stockMoveLineList");
|
||||
Map stockMoveMap = (Map<String, Object>) request.getContext().get("stockMove");
|
||||
if (selectedStockMoveLineMapList == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_14));
|
||||
return;
|
||||
}
|
||||
|
||||
List<StockMoveLine> stockMoveLineList = new ArrayList<>();
|
||||
StockMoveLineRepository stockMoveLineRepo = Beans.get(StockMoveLineRepository.class);
|
||||
for (HashMap map : selectedStockMoveLineMapList) {
|
||||
StockMoveLine stockMoveLine = (StockMoveLine) Mapper.toBean(StockMoveLine.class, map);
|
||||
stockMoveLineList.add(stockMoveLineRepo.find(stockMoveLine.getId()));
|
||||
}
|
||||
|
||||
if (stockMoveLineList.isEmpty()) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_15));
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal splitQty = new BigDecimal(request.getContext().get("splitQty").toString());
|
||||
if (splitQty == null || splitQty.compareTo(BigDecimal.ZERO) < 1) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_16));
|
||||
return;
|
||||
}
|
||||
|
||||
StockMove stockMove = Mapper.toBean(StockMove.class, stockMoveMap);
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
Beans.get(StockMoveService.class)
|
||||
.splitStockMoveLinesSpecial2(stockMove, stockMoveLineList, splitQty);
|
||||
response.setCanClose(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void shipReciveAllProducts(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
Beans.get(StockMoveService.class)
|
||||
.copyQtyToRealQty(Beans.get(StockMoveRepository.class).find(stockMove.getId()));
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
public void generateReversion(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
try {
|
||||
Optional<StockMove> reversion =
|
||||
Beans.get(StockMoveService.class)
|
||||
.generateReversion(Beans.get(StockMoveRepository.class).find(stockMove.getId()));
|
||||
if (reversion.isPresent()) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock move"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("grid", "stock-move-grid")
|
||||
.add("form", "stock-move-form")
|
||||
.param("forceEdit", "true")
|
||||
.context("_showRecord", String.valueOf(reversion.get().getId()))
|
||||
.map());
|
||||
} else {
|
||||
response.setFlash(I18n.get("No reversion generated"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void splitInto2(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
List<StockMoveLine> modifiedStockMoveLineList = stockMove.getStockMoveLineList();
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
try {
|
||||
StockMove newStockMove =
|
||||
Beans.get(StockMoveService.class).splitInto2(stockMove, modifiedStockMoveLineList);
|
||||
|
||||
if (newStockMove == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_SPLIT_NOT_GENERATED));
|
||||
} else {
|
||||
response.setCanClose(true);
|
||||
|
||||
response.setView(
|
||||
ActionView.define("Stock move")
|
||||
.model(StockMove.class.getName())
|
||||
.add("grid", "stock-move-grid")
|
||||
.add("form", "stock-move-form")
|
||||
.param("forceEdit", "true")
|
||||
.context("_showRecord", String.valueOf(newStockMove.getId()))
|
||||
.map());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void splitInto2Same(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
List<StockMoveLine> modifiedStockMoveLineList = stockMove.getStockMoveLineList();
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
|
||||
try {
|
||||
StockMove newStockMove =
|
||||
Beans.get(StockMoveService.class)
|
||||
.splitInto2SameMove(stockMove, modifiedStockMoveLineList);
|
||||
|
||||
if (newStockMove == null) {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_SPLIT_NOT_GENERATED));
|
||||
} else {
|
||||
response.setReload(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeConformityStockMove(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
response.setValue(
|
||||
"stockMoveLineList",
|
||||
Beans.get(StockMoveService.class).changeConformityStockMove(stockMove));
|
||||
}
|
||||
|
||||
public void changeConformityStockMoveLine(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
response.setValue(
|
||||
"conformitySelect",
|
||||
Beans.get(StockMoveService.class).changeConformityStockMoveLine(stockMove));
|
||||
}
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
response.setValue("exTaxTotal", Beans.get(StockMoveToolService.class).compute(stockMove));
|
||||
}
|
||||
|
||||
public void openStockPerDay(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
|
||||
Long locationId =
|
||||
Long.parseLong(((Map<String, Object>) context.get("stockLocation")).get("id").toString());
|
||||
LocalDate fromDate = LocalDate.parse(context.get("stockFromDate").toString());
|
||||
LocalDate toDate = LocalDate.parse(context.get("stockToDate").toString());
|
||||
|
||||
Collection<Map<String, Object>> products =
|
||||
(Collection<Map<String, Object>>) context.get("productSet");
|
||||
|
||||
String domain = null;
|
||||
List<Object> productIds = null;
|
||||
if (products != null && !products.isEmpty()) {
|
||||
productIds = Arrays.asList(products.stream().map(p -> p.get("id")).toArray());
|
||||
domain = "self.id in (:productIds)";
|
||||
}
|
||||
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stocks"))
|
||||
.model(Product.class.getName())
|
||||
.add("cards", "stock-product-cards")
|
||||
.add("grid", "stock-product-grid")
|
||||
.add("form", "stock-product-form")
|
||||
.domain(domain)
|
||||
.context("fromStockWizard", true)
|
||||
.context("productIds", productIds)
|
||||
.context("stockFromDate", fromDate)
|
||||
.context("stockToDate", toDate)
|
||||
.context("locationId", locationId)
|
||||
.map());
|
||||
}
|
||||
|
||||
public void fillAddressesStr(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
Beans.get(StockMoveToolService.class).computeAddressStr(stockMove);
|
||||
|
||||
response.setValues(stockMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on printing settings select. Set the the domain for {@link StockMove#printingSettings}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void filterPrintingSettings(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
List<PrintingSettings> printingSettingsList =
|
||||
Beans.get(TradingNameService.class)
|
||||
.getPrintingSettingsList(stockMove.getTradingName(), stockMove.getCompany());
|
||||
String domain =
|
||||
String.format(
|
||||
"self.id IN (%s)",
|
||||
!printingSettingsList.isEmpty()
|
||||
? StringTool.getIdListString(printingSettingsList)
|
||||
: "0");
|
||||
|
||||
response.setAttr("printingSettings", "domain", domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on trading name change. Set the default value for {@link StockMove#printingSettings}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void fillDefaultPrintingSettings(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
response.setValue(
|
||||
"printingSettings",
|
||||
Beans.get(TradingNameService.class)
|
||||
.getDefaultPrintingSettings(stockMove.getTradingName(), stockMove.getCompany()));
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAvailableStatus(ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
Beans.get(StockMoveService.class).setAvailableStatus(stockMove);
|
||||
response.setValue("stockMoveLineList", stockMove.getStockMoveLineList());
|
||||
}
|
||||
|
||||
public void updateMoveLineFilterOnAvailableproduct(
|
||||
ActionRequest request, ActionResponse response) {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
if (stockMove.getStockMoveLineList() != null) {
|
||||
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
|
||||
stockMoveLine.setFilterOnAvailableProducts(stockMove.getFilterOnAvailableProducts());
|
||||
}
|
||||
response.setValue("stockMoveLineList", stockMove.getStockMoveLineList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from stock move form view on save. Call {@link
|
||||
* StockMoveService#updateStocks(StockMove)}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void updateStocks(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
Beans.get(StockMoveService.class)
|
||||
.updateStocks(Beans.get(StockMoveRepository.class).find(stockMove.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshProductNetMass(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
Beans.get(StockMoveService.class).updateProductNetMass(stockMove);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
// sortie de stock
|
||||
public void getSequenceOutStock(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
if (context.get("stockMoveSeq") == null) {
|
||||
String num;
|
||||
int currentYear = Beans.get(AppBaseService.class).getTodayDateTime().getYear();
|
||||
String[] splityear = Integer.toString(currentYear).split("20");
|
||||
String year = splityear[1];
|
||||
year = "SR" + year;
|
||||
Query q =
|
||||
JPA.em()
|
||||
.createQuery(
|
||||
" select stockMoveSeq from StockMove where stockMoveSeq like ?1 ORDER BY stockMoveSeq DESC",
|
||||
String.class);
|
||||
q.setParameter(1, year + "%");
|
||||
|
||||
if (q.getResultList().size() == 0) {
|
||||
response.setValue("stockMoveSeq", year + "00001");
|
||||
} else {
|
||||
String result = (String) q.getResultList().get(0);
|
||||
String arr[] = result.split(year);
|
||||
String nbrString = arr[1];
|
||||
int nbrInt = Integer.parseInt(nbrString);
|
||||
nbrInt = nbrInt + 1;
|
||||
num = Integer.toString(nbrInt);
|
||||
String padding = "00000".substring(num.length()) + num;
|
||||
result = year + padding;
|
||||
response.setValue("stockMoveSeq", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// controler les sequences pour les livraisons client => bloc si ne sont pas séquentielle
|
||||
public void controleSequenceBL(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
StockMove stockMove = request.getContext().asType(StockMove.class);
|
||||
|
||||
if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_OUTGOING
|
||||
&& stockMove.getIsReversion() == false
|
||||
&& stockMove.getPartner().getId() != 853) {
|
||||
|
||||
int currentYear = Beans.get(AppBaseService.class).getTodayDateTime().getYear();
|
||||
String[] splityear = Integer.toString(currentYear).split("20");
|
||||
String year = "BL" + splityear[1];
|
||||
Query q =
|
||||
JPA.em()
|
||||
.createQuery(
|
||||
" select stockMoveSeq from StockMove where stockMoveSeq like ?1 and stockMoveSeq != ?2"
|
||||
+ " and LENGTH(stock_move_seq) = 9 and partner != 853 and is_reversion = false "
|
||||
+ " ORDER BY stockMoveSeq DESC",
|
||||
String.class);
|
||||
q.setParameter(1, year + "%");
|
||||
q.setParameter(2, stockMove.getStockMoveSeq());
|
||||
|
||||
if (q.getResultList().size() > 0) {
|
||||
List<String> squences = q.getResultList();
|
||||
String maxNumberString = squences.get(0);
|
||||
String[] a = maxNumberString.split(year);
|
||||
Integer maxNumber = Integer.parseInt(a[1]);
|
||||
|
||||
String originNumberString = stockMove.getStockMoveSeq();
|
||||
String[] b = originNumberString.split(year);
|
||||
Integer originNumber = Integer.parseInt(b[1]);
|
||||
|
||||
Integer nextNumber = maxNumber + 1;
|
||||
String padding = "00000".substring(nextNumber.toString().length()) + nextNumber.toString();
|
||||
if (nextNumber != originNumber && originNumber > nextNumber) {
|
||||
throw new AxelorException(
|
||||
stockMove,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
"please correct current sequence to: " + year + padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfAlreadyFactory(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
StockMove stockMoveFromRequest = request.getContext().asType(StockMove.class);
|
||||
|
||||
StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromRequest.getId());
|
||||
Query sql =
|
||||
JPA.em()
|
||||
.createNativeQuery(
|
||||
"SELECT "
|
||||
+ " FROM account_invoice_stock_move_set"
|
||||
+ " WHERE stock_move_set = :objectName");
|
||||
sql.setParameter("objectName", stockMove);
|
||||
if (sql.getResultList().size() > 0) {
|
||||
throw new AxelorException(
|
||||
stockMove,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
"vous avez deja une facture sur cette Piece");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfQuarantine(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
StockMove stockMoveFromRequest = request.getContext().asType(StockMove.class);
|
||||
|
||||
StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromRequest.getId());
|
||||
Query sql =
|
||||
JPA.em()
|
||||
.createNativeQuery(
|
||||
"SELECT LINE.ID"+
|
||||
" FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product"+
|
||||
" where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2");
|
||||
sql.setParameter(1, stockMove.getFromStockLocation().getId());
|
||||
sql.setParameter(2, stockMove.getId());
|
||||
logger.debug("sql.getResultList().size()",sql.getResultList().size());
|
||||
if (sql.getResultList().size() > 0) {
|
||||
throw new AxelorException(
|
||||
stockMove,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
"vous avez une ligne en etat qurantaine");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfNonConformityTag(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
StockMove stockMoveFromContext = request.getContext().asType(StockMove.class);
|
||||
StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId());
|
||||
Query sql =
|
||||
JPA.em()
|
||||
.createNativeQuery(
|
||||
"SELECT LINE.ID " +
|
||||
" FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product"
|
||||
+" where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = ?2");
|
||||
sql.setParameter(1, stockMove.getToStockLocation().getId());
|
||||
sql.setParameter(2, stockMove.getId());
|
||||
logger.debug("sql.getResultList().size()",sql.getResultList().size());
|
||||
if (sql.getResultList().size() > 0) {
|
||||
throw new AxelorException(
|
||||
stockMove,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
"vous avez une ligne non étiqueté");
|
||||
}
|
||||
}
|
||||
|
||||
public void createStockMoveLineLocationBarCodeSeq(
|
||||
ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
boolean addPadding = false;
|
||||
InputStream inStream = null;
|
||||
|
||||
StockMoveLineLocation stockMoveLineLocationContext =
|
||||
request.getContext().asType(StockMoveLineLocation.class);
|
||||
|
||||
StockMoveLineLocation stockMoveLineLocation =
|
||||
Beans.get(StockMoveLineLocationRepository.class)
|
||||
.find(stockMoveLineLocationContext.getId());
|
||||
|
||||
inStream =
|
||||
Beans.get(BarcodeGeneratorService.class)
|
||||
.createBarCode(
|
||||
stockMoveLineLocation.getCode(),
|
||||
Beans.get(AppBaseService.class)
|
||||
.getAppBase()
|
||||
.getBarcodeTypeConfigPurchaseOrderSeq(),
|
||||
addPadding);
|
||||
|
||||
if (inStream != null) {
|
||||
MetaFile barcodeFile =
|
||||
Beans.get(MetaFiles.class)
|
||||
.upload(
|
||||
inStream,
|
||||
String.format("StockMoveLineLocation%d.png", stockMoveLineLocation.getId()));
|
||||
stockMoveLineLocation.setBarCodeSeq(barcodeFile);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (AxelorException e) {
|
||||
throw new ValidationException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void showPopup(ActionRequest request, ActionResponse response) {
|
||||
response.setAlert("Hello words!!");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.stock.web;
|
||||
|
||||
import com.axelor.apps.base.db.Wizard;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockLocationLine;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.StockLocationLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveLineService;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.ResponseMessageType;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Singleton;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@Singleton
|
||||
public class StockMoveLineController {
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
StockMoveLine stockMoveLine = request.getContext().asType(StockMoveLine.class);
|
||||
StockMove stockMove = stockMoveLine.getStockMove();
|
||||
if (stockMove == null) {
|
||||
Context parentContext = request.getContext().getParent();
|
||||
Context superParentContext = parentContext.getParent();
|
||||
if (parentContext.getContextClass().equals(StockMove.class)) {
|
||||
stockMove = parentContext.asType(StockMove.class);
|
||||
} else if (superParentContext.getContextClass().equals(StockMove.class)) {
|
||||
stockMove = superParentContext.asType(StockMove.class);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!(stockMove.getPartner() != stockMove.getCompany().getPartner()
|
||||
|| stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING)) {
|
||||
stockMoveLine = Beans.get(StockMoveLineService.class).compute(stockMoveLine, stockMove);
|
||||
response.setValue("unitPriceUntaxed", stockMoveLine.getUnitPriceUntaxed());
|
||||
response.setValue("unitPriceTaxed", stockMoveLine.getUnitPriceTaxed());
|
||||
response.setValue("companyUnitPriceUntaxed", stockMoveLine.getCompanyUnitPriceUntaxed());
|
||||
}
|
||||
}
|
||||
|
||||
public void setProductInfo(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockMoveLine stockMoveLine;
|
||||
|
||||
try {
|
||||
stockMoveLine = request.getContext().asType(StockMoveLine.class);
|
||||
StockMove stockMove = stockMoveLine.getStockMove();
|
||||
|
||||
if (stockMove == null) {
|
||||
stockMove = request.getContext().getParent().asType(StockMove.class);
|
||||
}
|
||||
|
||||
if (stockMoveLine.getProduct() == null) {
|
||||
stockMoveLine = new StockMoveLine();
|
||||
response.setValues(Mapper.toMap(stockMoveLine));
|
||||
return;
|
||||
}
|
||||
|
||||
Beans.get(StockMoveLineService.class)
|
||||
.setProductInfo(stockMove, stockMoveLine, stockMove.getCompany());
|
||||
response.setValues(stockMoveLine);
|
||||
} catch (Exception e) {
|
||||
stockMoveLine = new StockMoveLine();
|
||||
response.setValues(Mapper.toMap(stockMoveLine));
|
||||
TraceBackService.trace(response, e, ResponseMessageType.INFORMATION);
|
||||
}
|
||||
}
|
||||
|
||||
public void emptyLine(ActionRequest request, ActionResponse response) {
|
||||
StockMoveLine stockMoveLine = request.getContext().asType(StockMoveLine.class);
|
||||
if (stockMoveLine.getLineTypeSelect() != StockMoveLineRepository.TYPE_NORMAL) {
|
||||
Map<String, Object> newStockMoveLine = Mapper.toMap(new StockMoveLine());
|
||||
newStockMoveLine.put("qty", BigDecimal.ZERO);
|
||||
newStockMoveLine.put("id", stockMoveLine.getId());
|
||||
newStockMoveLine.put("version", stockMoveLine.getVersion());
|
||||
newStockMoveLine.put("lineTypeSelect", stockMoveLine.getLineTypeSelect());
|
||||
response.setValues(newStockMoveLine);
|
||||
}
|
||||
}
|
||||
|
||||
public void splitStockMoveLineByTrackingNumber(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
|
||||
if (context.get("trackingNumbers") == null) {
|
||||
response.setAlert(I18n.get(IExceptionMessage.TRACK_NUMBER_WIZARD_NO_RECORD_ADDED_ERROR));
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedHashMap<String, Object> stockMoveLineMap =
|
||||
(LinkedHashMap<String, Object>) context.get("_stockMoveLine");
|
||||
Integer stockMoveLineId = (Integer) stockMoveLineMap.get("id");
|
||||
StockMoveLine stockMoveLine =
|
||||
Beans.get(StockMoveLineRepository.class).find(new Long(stockMoveLineId));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<LinkedHashMap<String, Object>> trackingNumbers =
|
||||
(ArrayList<LinkedHashMap<String, Object>>) context.get("trackingNumbers");
|
||||
|
||||
Beans.get(StockMoveLineService.class)
|
||||
.splitStockMoveLineByTrackingNumber(stockMoveLine, trackingNumbers);
|
||||
response.setCanClose(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void openTrackNumberWizard(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
StockMoveLine stockMoveLine = context.asType(StockMoveLine.class);
|
||||
StockMove stockMove = null;
|
||||
if (context.getParent() != null
|
||||
&& context.getParent().get("_model").equals("com.axelor.apps.stock.db.StockMove")) {
|
||||
stockMove = context.getParent().asType(StockMove.class);
|
||||
} else if (stockMoveLine.getStockMove() != null
|
||||
&& stockMoveLine.getStockMove().getId() != null) {
|
||||
stockMove = Beans.get(StockMoveRepository.class).find(stockMoveLine.getStockMove().getId());
|
||||
}
|
||||
|
||||
boolean _hasWarranty = false, _isPerishable = false;
|
||||
if (stockMoveLine.getProduct() != null) {
|
||||
_hasWarranty = stockMoveLine.getProduct().getHasWarranty();
|
||||
_isPerishable = stockMoveLine.getProduct().getIsPerishable();
|
||||
}
|
||||
response.setView(
|
||||
ActionView.define(I18n.get(IExceptionMessage.TRACK_NUMBER_WIZARD_TITLE))
|
||||
.model(Wizard.class.getName())
|
||||
.add("form", "stock-move-line-track-number-wizard-form")
|
||||
.param("popup", "reload")
|
||||
.param("show-toolbar", "false")
|
||||
.param("show-confirm", "false")
|
||||
.param("width", "large")
|
||||
.param("popup-save", "false")
|
||||
.context("_stockMove", stockMove)
|
||||
.context("_stockMoveLine", stockMoveLine)
|
||||
.context("_hasWarranty", _hasWarranty)
|
||||
.context("_isPerishable", _isPerishable)
|
||||
.map());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void computeAvailableQty(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
Context context = request.getContext();
|
||||
StockMoveLine stockMoveLineContext = context.asType(StockMoveLine.class);
|
||||
StockMoveLine stockMoveLine = null;
|
||||
if (stockMoveLineContext.getId() != null) {
|
||||
stockMoveLine = Beans.get(StockMoveLineRepository.class).find(stockMoveLineContext.getId());
|
||||
if (stockMoveLineContext.getProduct() != null
|
||||
&& !stockMoveLineContext.getProduct().equals(stockMoveLine.getProduct())) {
|
||||
stockMoveLine = stockMoveLineContext;
|
||||
}
|
||||
} else {
|
||||
stockMoveLine = stockMoveLineContext;
|
||||
}
|
||||
|
||||
StockLocation stockLocation = null;
|
||||
if (context.get("_parent") != null
|
||||
&& ((Map) context.get("_parent")).get("fromStockLocation") != null) {
|
||||
|
||||
Map<String, Object> _parent = (Map<String, Object>) context.get("_parent");
|
||||
|
||||
stockLocation =
|
||||
Beans.get(StockLocationRepository.class)
|
||||
.find(Long.parseLong(((Map) _parent.get("fromStockLocation")).get("id").toString()));
|
||||
|
||||
} else if (stockMoveLine.getStockMove() != null) {
|
||||
stockLocation = stockMoveLine.getStockMove().getFromStockLocation();
|
||||
}
|
||||
|
||||
if (stockLocation != null) {
|
||||
Beans.get(StockMoveLineService.class).updateAvailableQty(stockMoveLine, stockLocation);
|
||||
response.setValue("$availableQty", stockMoveLine.getAvailableQty());
|
||||
response.setValue("$availableQtyForProduct", stockMoveLine.getAvailableQtyForProduct());
|
||||
}
|
||||
}
|
||||
|
||||
public void setProductDomain(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
StockMoveLine stockMoveLine = context.asType(StockMoveLine.class);
|
||||
StockMove stockMove =
|
||||
context.getParent() != null
|
||||
? context.getParent().asType(StockMove.class)
|
||||
: stockMoveLine.getStockMove();
|
||||
String domain =
|
||||
Beans.get(StockMoveLineService.class).createDomainForProduct(stockMoveLine, stockMove);
|
||||
response.setAttr("product", "domain", domain);
|
||||
}
|
||||
|
||||
public void setAvailableStatus(ActionRequest request, ActionResponse response) {
|
||||
StockMoveLine stockMoveLine = request.getContext().asType(StockMoveLine.class);
|
||||
Beans.get(StockMoveLineService.class).setAvailableStatus(stockMoveLine);
|
||||
response.setValue("availableStatus", stockMoveLine.getAvailableStatus());
|
||||
response.setValue("availableStatusSelect", stockMoveLine.getAvailableStatusSelect());
|
||||
}
|
||||
|
||||
public void displayAvailableTrackingNumber(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedHashMap<String, Object> stockMoveLineMap =
|
||||
(LinkedHashMap<String, Object>) context.get("_stockMoveLine");
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedHashMap<String, Object> stockMoveMap =
|
||||
(LinkedHashMap<String, Object>) context.get("_stockMove");
|
||||
Integer stockMoveLineId = (Integer) stockMoveLineMap.get("id");
|
||||
Integer stockMoveId = (Integer) stockMoveMap.get("id");
|
||||
StockMoveLine stockMoveLine =
|
||||
Beans.get(StockMoveLineRepository.class).find(new Long(stockMoveLineId));
|
||||
StockMove stockMove = Beans.get(StockMoveRepository.class).find(new Long(stockMoveId));
|
||||
|
||||
if (stockMoveLine == null
|
||||
|| stockMoveLine.getProduct() == null
|
||||
|| stockMove == null
|
||||
|| stockMove.getFromStockLocation() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<TrackingNumber> trackingNumberList =
|
||||
Beans.get(StockMoveLineService.class).getAvailableTrackingNumbers(stockMoveLine, stockMove);
|
||||
if (trackingNumberList == null || trackingNumberList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SortedSet<Map<String, Object>> trackingNumbers =
|
||||
new TreeSet<Map<String, Object>>(
|
||||
Comparator.comparing(m -> (String) m.get("trackingNumberSeq")));
|
||||
StockLocationLineService stockLocationLineService = Beans.get(StockLocationLineService.class);
|
||||
for (TrackingNumber trackingNumber : trackingNumberList) {
|
||||
StockLocationLine detailStockLocationLine =
|
||||
stockLocationLineService.getDetailLocationLine(
|
||||
stockMove.getFromStockLocation(), stockMoveLine.getProduct(), trackingNumber);
|
||||
BigDecimal availableQty =
|
||||
detailStockLocationLine != null
|
||||
? detailStockLocationLine.getCurrentQty()
|
||||
: BigDecimal.ZERO;
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("trackingNumber", trackingNumber);
|
||||
map.put("trackingNumberSeq", trackingNumber.getTrackingNumberSeq());
|
||||
map.put("counter", BigDecimal.ZERO);
|
||||
map.put("warrantyExpirationDate", trackingNumber.getWarrantyExpirationDate());
|
||||
map.put("perishableExpirationDate", trackingNumber.getPerishableExpirationDate());
|
||||
map.put("$availableQty", availableQty);
|
||||
map.put("$moveTypeSelect", stockMove.getTypeSelect());
|
||||
trackingNumbers.add(map);
|
||||
}
|
||||
response.setValue("$trackingNumbers", trackingNumbers);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
package com.axelor.apps.stock.web;
|
||||
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockProductionRequestRepository;
|
||||
import com.axelor.apps.stock.exception.IExceptionMessage;
|
||||
import com.axelor.apps.stock.service.stockmove.print.StockProductionRequestPrintService;
|
||||
import com.axelor.common.ObjectUtils;
|
||||
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.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StockProductionRequestController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
public void draft(ActionRequest request, ActionResponse response) {
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
try {
|
||||
Beans.get(StockProductionRequestService.class)
|
||||
.draft(Beans.get(StockProductionRequestRepository.class).find(productionRequest.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void plan(ActionRequest request, ActionResponse response) {
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
try {
|
||||
Beans.get(StockProductionRequestService.class)
|
||||
.plan(Beans.get(StockProductionRequestRepository.class).find(productionRequest.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void realize(ActionRequest request, ActionResponse response) {
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
try {
|
||||
Beans.get(StockProductionRequestService.class)
|
||||
.realize(
|
||||
Beans.get(StockProductionRequestRepository.class).find(productionRequest.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(ActionRequest request, ActionResponse response) {
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
try {
|
||||
Beans.get(StockProductionRequestService.class)
|
||||
.cancel(
|
||||
Beans.get(StockProductionRequestRepository.class).find(productionRequest.getId()));
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void print(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
String fileLink;
|
||||
String title;
|
||||
StockProductionRequestPrintService productionRequestPrintService =
|
||||
Beans.get(StockProductionRequestPrintService.class);
|
||||
|
||||
try {
|
||||
if (!ObjectUtils.isEmpty(request.getContext().get("_ids"))) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<Long> ids =
|
||||
Lists.transform(
|
||||
(List) request.getContext().get("_ids"),
|
||||
new Function<Object, Long>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Long apply(@Nullable Object input) {
|
||||
return Long.parseLong(input.toString());
|
||||
}
|
||||
});
|
||||
fileLink = productionRequestPrintService.printStockProductionRequests(ids);
|
||||
title = I18n.get("Purchase requests");
|
||||
} else if (context.get("id") != null) {
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
title = productionRequestPrintService.getFileName(productionRequest);
|
||||
fileLink =
|
||||
productionRequestPrintService.printStockProductionRequest(
|
||||
productionRequest, ReportSettings.FORMAT_PDF);
|
||||
logger.debug("Printing " + title);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.INVENTORY_3_DATA_NULL_OR_EMPTY));
|
||||
}
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void createStockMove(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockProductionRequest productionRequest =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
|
||||
try {
|
||||
if (productionRequest.getId() != null) {
|
||||
|
||||
StockProductionRequestService productionRequestService =
|
||||
Beans.get(StockProductionRequestService.class);
|
||||
|
||||
List<Long> stockMoveList =
|
||||
productionRequestService.createStocksMovesFromStockProductionRequest(
|
||||
Beans.get(StockProductionRequestRepository.class).find(productionRequest.getId()));
|
||||
|
||||
if (stockMoveList != null && stockMoveList.size() == 1) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock move"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("form", "stock-move-form")
|
||||
.add("grid", "stock-move-grid")
|
||||
.param("forceEdit", "true")
|
||||
.domain("self.id = " + stockMoveList.get(0))
|
||||
.context("_showRecord", String.valueOf(stockMoveList.get(0)))
|
||||
.context("_userType", StockMoveRepository.USER_TYPE_SENDER)
|
||||
.map());
|
||||
} else if (stockMoveList != null && stockMoveList.size() > 1) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock move"))
|
||||
.model(StockMove.class.getName())
|
||||
.add("grid", "stock-move-grid")
|
||||
.add("form", "stock-move-form")
|
||||
.domain("self.id in (" + Joiner.on(",").join(stockMoveList) + ")")
|
||||
.context("_userType", StockMoveRepository.USER_TYPE_SENDER)
|
||||
.map());
|
||||
} else {
|
||||
response.setFlash(I18n.get(IExceptionMessage.STOCK_MOVE_4));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateReversion(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
StockProductionRequest productionRequestContext =
|
||||
request.getContext().asType(StockProductionRequest.class);
|
||||
StockProductionRequest productionRequest =
|
||||
Beans.get(StockProductionRequestRepository.class).find(productionRequestContext.getId());
|
||||
|
||||
StockProductionRequestService productionRequestService =
|
||||
Beans.get(StockProductionRequestService.class);
|
||||
|
||||
StockProductionRequest stockProductionRequest =
|
||||
productionRequestService.generateReversion(productionRequest);
|
||||
|
||||
if (stockProductionRequest != null) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Stock production request"))
|
||||
.model(StockProductionRequest.class.getName())
|
||||
.add("form", "stock-production-request-form")
|
||||
.add("grid", "stock-production-request-grid")
|
||||
.param("forceEdit", "true")
|
||||
.domain("self.id = " + stockProductionRequest.getId())
|
||||
.context("_showRecord", String.valueOf(stockProductionRequest.getId()))
|
||||
.map());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.axelor.apps.stock.web;
|
||||
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.db.StockProductionRequestLine;
|
||||
import com.axelor.apps.stock.service.StockProductionRequestLineService;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.ResponseMessageType;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
|
||||
public class StockProductionRequestLineController {
|
||||
|
||||
public void setProductInfo(ActionRequest request, ActionResponse response) {
|
||||
|
||||
StockProductionRequestLine productionRequestLine;
|
||||
|
||||
try {
|
||||
productionRequestLine = request.getContext().asType(StockProductionRequestLine.class);
|
||||
StockProductionRequest productionRequest = productionRequestLine.getStockProductionRequest();
|
||||
|
||||
if (productionRequest == null) {
|
||||
productionRequest = request.getContext().getParent().asType(StockProductionRequest.class);
|
||||
}
|
||||
|
||||
if (productionRequestLine.getProduct() == null) {
|
||||
productionRequestLine = new StockProductionRequestLine();
|
||||
response.setValues(Mapper.toMap(productionRequestLine));
|
||||
return;
|
||||
}
|
||||
|
||||
Beans.get(StockProductionRequestLineService.class)
|
||||
.setProductInfo(productionRequest, productionRequestLine, productionRequest.getCompany());
|
||||
response.setValues(productionRequestLine);
|
||||
} catch (Exception e) {
|
||||
productionRequestLine = new StockProductionRequestLine();
|
||||
response.setValues(Mapper.toMap(productionRequestLine));
|
||||
TraceBackService.trace(response, e, ResponseMessageType.INFORMATION);
|
||||
}
|
||||
}
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
StockProductionRequestLine productionRequestLine =
|
||||
request.getContext().asType(StockProductionRequestLine.class);
|
||||
StockProductionRequest productionRequest = productionRequestLine.getStockProductionRequest();
|
||||
if (productionRequest == null) {
|
||||
Context parentContext = request.getContext().getParent();
|
||||
Context superParentContext = parentContext.getParent();
|
||||
if (parentContext.getContextClass().equals(StockProductionRequest.class)) {
|
||||
productionRequest = parentContext.asType(StockProductionRequest.class);
|
||||
} else if (superParentContext.getContextClass().equals(StockProductionRequest.class)) {
|
||||
productionRequest = superParentContext.asType(StockProductionRequest.class);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
productionRequestLine =
|
||||
Beans.get(StockProductionRequestLineService.class)
|
||||
.compute(productionRequestLine, productionRequest);
|
||||
response.setValue("unitPriceUntaxed", productionRequestLine.getUnitPriceUntaxed());
|
||||
response.setValue("unitPriceTaxed", productionRequestLine.getUnitPriceTaxed());
|
||||
response.setValue(
|
||||
"companyUnitPriceUntaxed", productionRequestLine.getCompanyUnitPriceUntaxed());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,312 @@
|
||||
package com.axelor.apps.stock.web;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.StockLocation;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.StockMoveLine;
|
||||
import com.axelor.apps.stock.db.StockProductionRequest;
|
||||
import com.axelor.apps.stock.db.StockProductionRequestLine;
|
||||
import com.axelor.apps.stock.db.repo.StockLocationRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockProductionRequestLineRepository;
|
||||
import com.axelor.apps.stock.db.repo.StockProductionRequestRepository;
|
||||
import com.axelor.apps.stock.service.StockMoveLineService;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.stock.service.StockMoveToolService;
|
||||
import com.axelor.apps.stock.service.StockMoveToolServiceImpl;
|
||||
import com.axelor.apps.stock.service.config.StockConfigService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StockProductionRequestService {
|
||||
|
||||
protected StockProductionRequestRepository productionRequestRepository;
|
||||
protected StockMoveService stockMoveService;
|
||||
protected StockMoveLineService stockMoveLineService;
|
||||
protected StockConfigService stockConfigService;
|
||||
protected UnitConversionService unitConversionService;
|
||||
protected StockMoveLineRepository stockMoveLineRepository;
|
||||
protected AppBaseService appBaseService;
|
||||
protected StockMoveToolService stockMoveToolService;
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Inject
|
||||
public StockProductionRequestService(
|
||||
StockProductionRequestRepository productionRequestRepository,
|
||||
StockMoveService stockMoveService,
|
||||
StockMoveLineService stockMoveLineService,
|
||||
StockConfigService stockConfigService,
|
||||
UnitConversionService unitConversionService,
|
||||
StockMoveLineRepository stockMoveLineRepository,
|
||||
StockMoveToolService stockMoveToolService,
|
||||
AppBaseService appBaseService) {
|
||||
this.productionRequestRepository = productionRequestRepository;
|
||||
this.stockMoveService = stockMoveService;
|
||||
this.stockMoveLineService = stockMoveLineService;
|
||||
this.stockConfigService = stockConfigService;
|
||||
this.unitConversionService = unitConversionService;
|
||||
this.stockMoveLineRepository = stockMoveLineRepository;
|
||||
this.appBaseService = appBaseService;
|
||||
this.stockMoveToolService = stockMoveToolService;
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void draft(StockProductionRequest stockProductionRequest) {
|
||||
stockProductionRequest.setStatusSelect(1);
|
||||
productionRequestRepository.save(stockProductionRequest);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void plan(StockProductionRequest stockProductionRequest) throws AxelorException {
|
||||
stockProductionRequest.setStatusSelect(2);
|
||||
stockProductionRequest.setStockProductionRequestSeq(
|
||||
Beans.get(StockMoveToolServiceImpl.class)
|
||||
.getSequenceStockProductionRequest(stockProductionRequest));
|
||||
productionRequestRepository.save(stockProductionRequest);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void realize(StockProductionRequest stockProductionRequest) {
|
||||
stockProductionRequest.setStatusSelect(3);
|
||||
productionRequestRepository.save(stockProductionRequest);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void cancel(StockProductionRequest stockProductionRequest) {
|
||||
stockProductionRequest.setStatusSelect(4);
|
||||
productionRequestRepository.save(stockProductionRequest);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public List<Long> createStocksMovesFromStockProductionRequest(
|
||||
StockProductionRequest stockProductionRequest) throws AxelorException {
|
||||
|
||||
List<Long> stockMoveList = new ArrayList<>();
|
||||
|
||||
// StockLocationLine stockLocationLine = this.getOrCreateStockLocationLine(stockLocation,
|
||||
// product);
|
||||
|
||||
// UnitConversionService unitConversionService = Beans.get(UnitConversionService.class);
|
||||
// Unit stockLocationLineUnit = stockLocationLine.getUnit();
|
||||
|
||||
// if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) {
|
||||
// qty =
|
||||
// unitConversionService.convert(
|
||||
// stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product);
|
||||
// }
|
||||
|
||||
Company company = stockProductionRequest.getCompany();
|
||||
StockMove stockMove = this.createStockMove(stockProductionRequest, company, LocalDate.now());
|
||||
|
||||
stockMove.setExTaxTotal(stockMoveToolService.compute(stockMove));
|
||||
|
||||
stockMoveService.plan(stockMove);
|
||||
|
||||
logger.debug("stockMove ************ {}", stockMove.toString());
|
||||
|
||||
stockMoveList.add(stockMove.getId());
|
||||
|
||||
for (StockProductionRequestLine productionRequestLine :
|
||||
stockProductionRequest.getStockProductionRequestLineList()) {
|
||||
if (productionRequestLine.getIsWithBackorder()) {
|
||||
StockMoveLine line =
|
||||
this.createStockMoveLine(
|
||||
stockMove, productionRequestLine, productionRequestLine.getRealQty());
|
||||
logger.debug("createStockMoveLine ************ {}", line.toString());
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("stockMoveList ************ {}", stockMoveList);
|
||||
return stockMoveList;
|
||||
}
|
||||
|
||||
public StockMove createStockMove(
|
||||
StockProductionRequest stockProductionRequest,
|
||||
Company company,
|
||||
LocalDate estimatedDeliveryDate)
|
||||
throws AxelorException {
|
||||
|
||||
StockLocation fromStockLocation =
|
||||
stockConfigService.getRawMaterialsDefaultStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
StockLocationRepository stockLocationRepository = Beans.get(StockLocationRepository.class);
|
||||
|
||||
switch (stockProductionRequest.getFamilleProduit().getId().intValue()) {
|
||||
// matiere premiere
|
||||
case 67:
|
||||
fromStockLocation = stockLocationRepository.find(75L);
|
||||
break;
|
||||
// MATIERE PREMIERE INJECTABLE
|
||||
case 68:
|
||||
fromStockLocation = stockLocationRepository.find(75L);
|
||||
break;
|
||||
// ARTICLES DE CONDITIONEMENT
|
||||
case 59:
|
||||
fromStockLocation = stockLocationRepository.find(75L);
|
||||
break;
|
||||
|
||||
default:
|
||||
fromStockLocation =
|
||||
stockConfigService.getRawMaterialsDefaultStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
break;
|
||||
}
|
||||
|
||||
// stockProductionRequest.getStocklocation();
|
||||
|
||||
StockLocation toStockLocation = stockProductionRequest.getStocklocation();
|
||||
// stockConfigService.getCustomerVirtualStockLocation(
|
||||
// stockConfigService.getStockConfig(company));
|
||||
int stockType = StockMoveRepository.TYPE_INTERNAL;
|
||||
if (stockProductionRequest.getTypeSelect()
|
||||
== StockProductionRequestRepository.TYPE_SELECT_RETURN) {
|
||||
toStockLocation = fromStockLocation;
|
||||
fromStockLocation =
|
||||
// stockProductionRequest.getStocklocation();
|
||||
stockConfigService.getCustomerVirtualStockLocation(
|
||||
stockConfigService.getStockConfig(company));
|
||||
stockType = StockMoveRepository.TYPE_INCOMING;
|
||||
System.out.println("***************************************yes*********************");
|
||||
System.out.println(stockProductionRequest.getTypeSelect());
|
||||
System.out.println(StockProductionRequestRepository.TYPE_SELECT_RETURN);
|
||||
System.out.println(fromStockLocation);
|
||||
System.out.println(toStockLocation);
|
||||
System.out.println("***************************************yes*********************");
|
||||
}
|
||||
|
||||
StockMove stockMove =
|
||||
stockMoveService.createStockMove(
|
||||
null,
|
||||
null,
|
||||
company,
|
||||
stockProductionRequest.getPartner(),
|
||||
fromStockLocation,
|
||||
toStockLocation,
|
||||
null,
|
||||
estimatedDeliveryDate,
|
||||
stockProductionRequest.getNote(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
stockType);
|
||||
|
||||
stockMove.setToAddressStr(null);
|
||||
stockMove.setOriginId(stockProductionRequest.getId());
|
||||
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_STOCK_PRODUCTION_REQUEST);
|
||||
stockMove.setOrigin(stockProductionRequest.getStockProductionRequestSeq());
|
||||
stockMove.setStockMoveLineList(new ArrayList<>());
|
||||
stockMove.setTradingName(null);
|
||||
stockMove.setNote(stockProductionRequest.getNote());
|
||||
return stockMove;
|
||||
}
|
||||
|
||||
public StockMoveLine createStockMoveLine(
|
||||
StockMove stockMove, StockProductionRequestLine productionRequestLine, BigDecimal qty)
|
||||
throws AxelorException {
|
||||
|
||||
int scale = Beans.get(AppBaseService.class).getNbDecimalDigitForSalePrice();
|
||||
if (this.isStockMoveProduct(productionRequestLine)) {
|
||||
|
||||
Unit unit = productionRequestLine.getProduct().getUnit();
|
||||
|
||||
if (unit != null && !unit.equals(productionRequestLine.getUnit())) {
|
||||
qty =
|
||||
unitConversionService.convert(
|
||||
productionRequestLine.getUnit(),
|
||||
unit,
|
||||
qty,
|
||||
qty.scale(),
|
||||
productionRequestLine.getProduct());
|
||||
}
|
||||
|
||||
BigDecimal priceDiscounted = productionRequestLine.getUnitPriceTaxed();
|
||||
BigDecimal companyUnitPriceUntaxed = productionRequestLine.getCompanyUnitPriceUntaxed();
|
||||
BigDecimal unitPriceUntaxed = productionRequestLine.getUnitPriceUntaxed();
|
||||
|
||||
StockMoveLine stockMoveLine =
|
||||
stockMoveLineService.createStockMoveLine(
|
||||
productionRequestLine.getProduct(),
|
||||
productionRequestLine.getProductName(),
|
||||
productionRequestLine.getDescription(),
|
||||
qty,
|
||||
unitPriceUntaxed,
|
||||
companyUnitPriceUntaxed,
|
||||
companyUnitPriceUntaxed,
|
||||
unit,
|
||||
stockMove,
|
||||
productionRequestLine.getTrackingNumber());
|
||||
return stockMoveLine;
|
||||
}
|
||||
if (productionRequestLine.getDeliveryState() == 0) {
|
||||
productionRequestLine.setDeliveryState(
|
||||
StockProductionRequestLineRepository.DELIVERY_STATE_NOT_DELIVERED);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public StockProductionRequest generateReversion(StockProductionRequest stockProductionRequest)
|
||||
throws AxelorException {
|
||||
StockProductionRequestRepository moveRepository =
|
||||
Beans.get(StockProductionRequestRepository.class);
|
||||
StockProductionRequestLineRepository moveRepositoryLineRepository =
|
||||
Beans.get(StockProductionRequestLineRepository.class);
|
||||
StockProductionRequest newStockProductionRequest =
|
||||
moveRepository.copy(stockProductionRequest, false);
|
||||
newStockProductionRequest.setStockProductionRequestSeq(null);
|
||||
newStockProductionRequest.setStatusSelect(StockProductionRequestRepository.STATUS_DRAFT);
|
||||
newStockProductionRequest.setTypeSelect(StockProductionRequestRepository.TYPE_SELECT_RETURN);
|
||||
|
||||
for (StockProductionRequestLine requestLine :
|
||||
stockProductionRequest.getStockProductionRequestLineList()) {
|
||||
StockProductionRequestLine sLine = moveRepositoryLineRepository.copy(requestLine, false);
|
||||
newStockProductionRequest.addStockProductionRequestLineListItem(sLine);
|
||||
}
|
||||
|
||||
return moveRepository.save(newStockProductionRequest);
|
||||
// StockMove stockMove = Beans.get(StockMoveRepository.class).all()
|
||||
// .filter(
|
||||
// "self.originTypeSelect = ?1 AND self.originId = ?2 AND self.statusSelect = ?3",
|
||||
// StockMoveRepository.ORIGIN_STOCK_PRODUCTION_REQUEST,
|
||||
// stockProductionRequest.getId())
|
||||
// .fetchOne();
|
||||
// Optional<StockMove> stockMove =
|
||||
// Beans.get(StockMoveServiceImpl.class).copyAndSplitStockMoveReverse(stockMove, false);
|
||||
// return stockMove;
|
||||
|
||||
}
|
||||
|
||||
public boolean isStockMoveProduct(StockProductionRequestLine productionRequestLine)
|
||||
throws AxelorException {
|
||||
return isStockMoveProduct(
|
||||
productionRequestLine, productionRequestLine.getStockProductionRequest());
|
||||
}
|
||||
|
||||
public boolean isStockMoveProduct(
|
||||
StockProductionRequestLine productionRequestLine, StockProductionRequest productionRequest)
|
||||
throws AxelorException {
|
||||
|
||||
Product product = productionRequestLine.getProduct();
|
||||
return (product != null
|
||||
&& (product.getProductTypeSelect().equals(ProductRepository.PRODUCT_TYPE_STORABLE)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.csv.script;
|
||||
|
||||
import com.axelor.apps.stock.db.Inventory;
|
||||
import com.axelor.apps.stock.db.InventoryLine;
|
||||
import com.axelor.apps.stock.service.InventoryService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportInventory {
|
||||
|
||||
@Inject InventoryService inventoryService;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Object validateInventory(Object bean, Map<String, Object> values) throws AxelorException {
|
||||
|
||||
assert bean instanceof InventoryLine;
|
||||
|
||||
Inventory inventory = (Inventory) bean;
|
||||
inventoryService.validateInventory(inventory);
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public Object importInventory(Object bean, Map<String, Object> values) throws AxelorException {
|
||||
|
||||
assert bean instanceof Inventory;
|
||||
|
||||
Inventory inventory = (Inventory) bean;
|
||||
inventory.setInventoryTitle(inventoryService.computeTitle(inventory));
|
||||
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
@ -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.csv.script;
|
||||
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.stock.db.InventoryLine;
|
||||
import com.axelor.apps.stock.db.TrackingNumber;
|
||||
import com.axelor.apps.stock.db.TrackingNumberConfiguration;
|
||||
import com.axelor.apps.stock.db.repo.InventoryLineRepository;
|
||||
import com.axelor.apps.stock.service.InventoryLineService;
|
||||
import com.axelor.apps.stock.service.TrackingNumberService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportInventoryLine {
|
||||
|
||||
@Inject private InventoryLineRepository inventoryLineRepo;
|
||||
|
||||
@Inject private InventoryLineService inventoryLineService;
|
||||
|
||||
@Inject private TrackingNumberService trackingNumberService;
|
||||
|
||||
@Inject private AppBaseService appBaseService;
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Object importInventoryLine(Object bean, Map<String, Object> values)
|
||||
throws AxelorException {
|
||||
|
||||
assert bean instanceof InventoryLine;
|
||||
|
||||
InventoryLine inventoryLine = (InventoryLine) bean;
|
||||
|
||||
TrackingNumberConfiguration trackingNumberConfig =
|
||||
inventoryLine.getProduct().getTrackingNumberConfiguration();
|
||||
|
||||
BigDecimal qtyByTracking = BigDecimal.ONE;
|
||||
|
||||
BigDecimal realQtyRemaning = inventoryLine.getRealQty();
|
||||
|
||||
inventoryLineService.compute(inventoryLine, inventoryLine.getInventory());
|
||||
|
||||
TrackingNumber trackingNumber;
|
||||
|
||||
if (trackingNumberConfig != null) {
|
||||
|
||||
if (trackingNumberConfig.getGenerateProductionAutoTrackingNbr()) {
|
||||
qtyByTracking = trackingNumberConfig.getProductionQtyByTracking();
|
||||
} else if (trackingNumberConfig.getGeneratePurchaseAutoTrackingNbr()) {
|
||||
qtyByTracking = trackingNumberConfig.getPurchaseQtyByTracking();
|
||||
} else {
|
||||
qtyByTracking = trackingNumberConfig.getSaleQtyByTracking();
|
||||
}
|
||||
|
||||
InventoryLine inventoryLineNew;
|
||||
|
||||
for (int i = 0; i < inventoryLine.getRealQty().intValue(); i += qtyByTracking.intValue()) {
|
||||
|
||||
trackingNumber =
|
||||
trackingNumberService.createTrackingNumber(
|
||||
inventoryLine.getProduct(),
|
||||
inventoryLine.getInventory().getStockLocation().getCompany(),
|
||||
appBaseService.getTodayDate());
|
||||
|
||||
if (realQtyRemaning.compareTo(qtyByTracking) < 0) {
|
||||
trackingNumber.setCounter(realQtyRemaning);
|
||||
} else {
|
||||
trackingNumber.setCounter(qtyByTracking);
|
||||
}
|
||||
|
||||
inventoryLineNew =
|
||||
inventoryLineService.createInventoryLine(
|
||||
inventoryLine.getInventory(),
|
||||
inventoryLine.getProduct(),
|
||||
inventoryLine.getCurrentQty(),
|
||||
inventoryLine.getRack(),
|
||||
trackingNumber);
|
||||
|
||||
inventoryLineNew.setUnit(inventoryLine.getProduct().getUnit());
|
||||
|
||||
if (realQtyRemaning.compareTo(qtyByTracking) < 0) {
|
||||
inventoryLineNew.setRealQty(realQtyRemaning);
|
||||
} else {
|
||||
inventoryLineNew.setRealQty(qtyByTracking);
|
||||
realQtyRemaning = realQtyRemaning.subtract(qtyByTracking);
|
||||
}
|
||||
|
||||
inventoryLineRepo.save(inventoryLineNew);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@ -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.csv.script;
|
||||
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.service.StockMoveToolService;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportStockMove {
|
||||
|
||||
protected StockMoveToolService stockMoveToolService;
|
||||
|
||||
@Inject
|
||||
public ImportStockMove(StockMoveToolService stockMoveToolService) {
|
||||
this.stockMoveToolService = stockMoveToolService;
|
||||
}
|
||||
|
||||
public Object importAddressStr(Object bean, Map<String, Object> values) {
|
||||
assert bean instanceof StockMove;
|
||||
|
||||
StockMove stockMove = (StockMove) bean;
|
||||
stockMoveToolService.computeAddressStr(stockMove);
|
||||
return stockMove;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.csv.script;
|
||||
|
||||
import com.axelor.apps.base.db.Sequence;
|
||||
import com.axelor.apps.stock.db.TrackingNumberConfiguration;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportTrackingNumberConfig {
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public Object computeFullName(Object bean, Map<String, Object> values) throws AxelorException {
|
||||
|
||||
assert bean instanceof TrackingNumberConfiguration;
|
||||
|
||||
TrackingNumberConfiguration trackingNumberConfiguration = (TrackingNumberConfiguration) bean;
|
||||
Sequence sequence = trackingNumberConfiguration.getSequence();
|
||||
String name = trackingNumberConfiguration.getName();
|
||||
trackingNumberConfiguration.setFullName(name);
|
||||
if (sequence != null) {
|
||||
trackingNumberConfiguration.setFullName(name + " / " + sequence.getFullName());
|
||||
}
|
||||
|
||||
return trackingNumberConfiguration;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
|
||||
200;"trackingNumberSeqForClassicServeur";"Tracking number sequence for classic serveur";1;4;"F4";;1;1
|
||||
201;"trackingNumberSeqForClassicPack";"Tracking number sequence for classic pack";1;4;"F2";;1;1
|
||||
202;"trackingNumberSeqForInkjetPrinter";"Tracking number sequence for inkjet printer";1;4;"A5";;1;1
|
||||
5;"intStockMove";"Internal stock move N°";1;4;"ISM";;1;0
|
||||
6;"outStockMove";"Delivery order N°";1;4;"DO";;1;0
|
||||
7;"inStockMove";"Goods receipt order N°";1;4;"RO";;1;0
|
||||
8;"inventory";"Inventory N°";1;4;"INV%M%YY";;1;0
|
||||
9;"productTrackingNumber";"Tracking N°";1;4;"TN";;1;0
|
||||
203;"logisticalForm";"Logistical form";1;4;"LF";;1;0
|
||||
|
@ -0,0 +1,10 @@
|
||||
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
|
||||
200;"productTrackingNumber";"Sequence de numéro de suivi pour le produit Serveur Classique";1;4;"F4";;1;1
|
||||
201;"productTrackingNumber";"Sequence de numéro de suivi pour le produit Classique Pack";1;4;"F2";;1;1
|
||||
202;"productTrackingNumber";"Sequence de numéro de suivi pour le produit Imprimante Jet d’encre";1;4;"A5";;1;1
|
||||
5;"intStockMove";"Mouvement de stock interne";1;4;"ISM";;1;0
|
||||
6;"outStockMove";"Bon de livraison N°";1;4;"DO";;1;0
|
||||
7;"inStockMove";"Bon de réception de bien N°";1;4;"RO";;1;0
|
||||
8;"inventory";"Inventaire N°";1;4;"INV%M%YY";;1;0
|
||||
9;"productTrackingNumber";"Numéro de suivi";1;4;"TN";;1;0
|
||||
203;"logisticalForm";"Fiche logistique";1;4;"FL";;1;0
|
||||
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
<input file="base_sequence.csv" separator=";" type="com.axelor.apps.base.db.Sequence" search="self.importId = :importId">
|
||||
<bind to="yearlyResetOk" column="yearlyResetOk" eval="yearlyResetOk == '1' ? true : false"/>
|
||||
<bind to="nextNum" column="nextNum" eval="nextNum?.empty ? '1' : nextNum"/>
|
||||
<bind to="padding" column="padding" eval="padding?.empty ? '1' : padding"/>
|
||||
<bind to="toBeAdded" column="toBeAdded" eval="toBeAdded?.empty ? '1' : toBeAdded"/>
|
||||
<bind to="resetDate" eval="call:com.axelor.apps.base.service.app.AppBaseService:getTodayDate()" />
|
||||
</input>
|
||||
|
||||
</csv-inputs>
|
||||
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
<input file="auth_permission.csv" separator=";" type="com.axelor.auth.db.Permission" search="self.name = :name" call="com.axelor.csv.script.ImportPermission:importPermissionToRole">
|
||||
<bind to="canRead" eval="can_read == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canWrite" eval="can_write == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canCreate" eval="can_create == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canRemove" eval="can_remove == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canExport" eval="can_export == 'x' ? 'true' : 'false'"/>
|
||||
</input>
|
||||
|
||||
<input file="base_appStock.csv" separator=";" type="com.axelor.apps.base.db.AppStock" call="com.axelor.csv.script.ImportApp:importApp">
|
||||
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
|
||||
</input>
|
||||
|
||||
<input file="meta_helpEN.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
|
||||
<bind to="language" eval="'en'" />
|
||||
<bind to="type" eval="'tooltip'" />
|
||||
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
|
||||
</input>
|
||||
|
||||
<input file="meta_helpFR.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
|
||||
<bind to="language" eval="'fr'" />
|
||||
<bind to="type" eval="'tooltip'" />
|
||||
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
|
||||
</input>
|
||||
|
||||
<input file="meta_metaMenu.csv" separator=";" type="com.axelor.meta.db.MetaMenu" search="self.name = :name" update="true" />
|
||||
|
||||
|
||||
</csv-inputs>
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
|
||||
"perm.stock.all";"com.axelor.apps.stock.db.*";"x";"x";"x";"x";"x";;;"Admin"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
|
||||
"Stock";"stock";3;"Stock configuration";"app-stock.png";"axelor-stock";"base";14
|
||||
|
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 820 B |
|
After Width: | Height: | Size: 786 B |
|
After Width: | Height: | Size: 801 B |
|
After Width: | Height: | Size: 771 B |
|
After Width: | Height: | Size: 769 B |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 699 B |
|
After Width: | Height: | Size: 660 B |
|
After Width: | Height: | Size: 961 B |
@ -0,0 +1,19 @@
|
||||
"module";"object";"view";"field";"help"
|
||||
"axelor-stock";"StockMove";"stock-move-form";"conformitySelect";"Enables to specify if the product (usually received) answers the request. "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"isWithReturnSurplus";"Box to be checked in order to automatically generate a new stock movement, in case of a surplus in the quantity of a product (after actual values have been entered). "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"isWithBackorder";"Box to be checked in order to automatically generate a new stock movement, in case of insufficient quantity of a product (after actual values have been entered). "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"fromAddress";"For supplier arrivals only. This field enables to select the departure address for stock movements, among addresses recorded in the supplier's form. "
|
||||
"axelor-stock";"StockMoveLine";"stock-move-line-form";"conformitySelect";"Enables to specify if the product (usually received) answers the request. "
|
||||
"axelor-stock";"Inventory";"inventory-form";"excludeOutOfStock";"Box to be checked in order to exclude products for which the stock quantity equals 0 from inventory lines. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generatePurchaseAutoTrackingNbr";"Box to be checked in order to allow automatic generation of a tracking number from a configurable sequence. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"purchaseQtyByTracking";"Enables to specify the product quantity (in product unit) which will be batched under the same tracking number. By default, this number is set to 1. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generateProductionAutoTrackingNbr";"Box to be checked in order to allow automatic generation of a tracking number from a configurable sequence. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"productionQtyByTracking";"Enables to specify the product quantity (in product unit) which will be batched under the same tracking number. By default, this number is set to 1. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generateSaleAutoTrackingNbr";"Box to be checked in order to allow automatic generation of a tracking number from a configurable sequence. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"hasSaleAutoSelectTrackingNbr";"Box to be checked in order to specify that the tracking number attached to a product on a customer order will be automatically selected, observing a rule regarding entry into inventory (FIFO/LIFO). "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"saleAutoTrackingNbrOrderSelect";"Enables to specify whether, in the case of sale, the most ancient (FIFO) or most recent (LIFO) tracking number will be selected. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"saleQtyByTracking";"Enables to specify the product quantity (in product unit) which will be batched under the same tracking number. By default, this number is set to 1. "
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"realizeStockMovesUponParcelPalletCollection";"If this option is activated, when a logistic form goes to the collected status, the customer delivery automatically changes to the status ""Realized""."
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"freightCarrierCustomerAccountNumberList";"Allows you to enter your customer number to the carriers companies.
|
||||
"
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"stockMoveAutomaticMail";"By checking this box, an email will automatically be sent when performing a stock movement. You can select the message template that will be sent."
|
||||
|
@ -0,0 +1,19 @@
|
||||
"module";"object";"view";"field";"help"
|
||||
"axelor-stock";"StockMove";"stock-move-form";"conformitySelect";"Permet de spécifier si le produit (généralement reçu) est bien conforme à la demande. "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"isWithReturnSurplus";"Cette case à cocher permet de faire générer automatiquement un nouveau mouvement de stock en cas de surplus de quantité sur un produit (une fois les valeurs réelles renseignées). "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"isWithBackorder";"Cette case à cocher permet de faire générer automatiquement un nouveau mouvement de stock en cas de manque de quantité sur un produit (une fois les valeurs réelles renseignées). "
|
||||
"axelor-stock";"StockMove";"stock-move-form";"fromAddress";"Uniquement pour les réceptions. Ce champ permet de sélectionner l'adresse de départ du mouvement de stock parmi les adresses configurées dans la fiche du fournisseur. "
|
||||
"axelor-stock";"StockMoveLine";"stock-move-line-form";"conformitySelect";"Permet de spécifier si le produit (généralement reçu) est bien conforme à la demande. "
|
||||
"axelor-stock";"Inventory";"inventory-form";"excludeOutOfStock";"Case à cocher permettant d'exclure des lignes d'inventaires les produits ayant un stock à 0. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generatePurchaseAutoTrackingNbr";"Case à cocher permettant de déclencher une génération automatique du numéro de suivi à partir d'une séquence paramétrable. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"purchaseQtyByTracking";"Permet de renseigner la quantité (en unité produit) de produit qui sera regroupée en lot sous un même numéro de suivi. Par défaut cette valeur est paramétrée à 1."
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generateProductionAutoTrackingNbr";"Case à cocher permettant de déclencher une génération automatique du numéro de suivi à partir d'une séquence paramétrable. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"productionQtyByTracking";"Permet de renseigner la quantité (en unité produit) de produit qui sera regroupée en lot sous un même numéro de suivi. Par défaut cette valeur est paramétrée à 1."
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"generateSaleAutoTrackingNbr";"Case à cocher permettant de déclencher une génération automatique du numéro de suivi à partir d'une séquence paramétrable. "
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"hasSaleAutoSelectTrackingNbr";"Case à cocher permettant de préciser que le numéro de suivi associé à un produit sur une commande client sera automatiquement sélectionné en respectant une règle sur l'entrée en stock du produit (FIFO / LIFO)"
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"saleAutoTrackingNbrOrderSelect";"Permet de sélectionner si lors de la vente d'un produit on viendra sélectionner le numéro de suivi le plus ancien (FIFO) ou le plus récent (LIFO)"
|
||||
"axelor-stock";"TrackingNumberConfiguration";"tracking-number-configuration-form";"saleQtyByTracking";"Permet de renseigner la quantité (en unité produit) de produit qui sera regroupée en lot sous un même numéro de suivi. Par défaut cette valeur est paramétrée à 1."
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"realizeStockMovesUponParcelPalletCollection";"Si cette option est activée, quand une fiche logistique passe au statut enlevé, le BL passe automatiquement au statut ""Réalisé"".
|
||||
"
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"freightCarrierCustomerAccountNumberList";"Permet de rentrer son numéro de compte client chez ses transporteurs."
|
||||
"axelor-stock";"StockConfig";"stock-config-form";"stockMoveAutomaticMail";"En cochant cette case, un email va automatiquement être envoyé lors de la réalisation d'un mouvement de stock. Vous pouvez sélectionner le modèle de message qui sera envoyé."
|
||||
|