First commit waiting for Budget Alert

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

View File

@ -0,0 +1,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")
}

View File

@ -0,0 +1,56 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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;
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,62 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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." /*)*/;
}

View File

@ -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));
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,28 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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";
}

View File

@ -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"; /*)*/
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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)
: "");
}
}

View File

@ -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);
}

View File

@ -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()));
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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;
}

View File

@ -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());
}
}

View File

@ -0,0 +1,56 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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;
}

View File

@ -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***************");
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}

View File

@ -0,0 +1,308 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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()));
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -0,0 +1,103 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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)...
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,27 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.stock.service.app;
import com.axelor.apps.base.db.AppStock;
public interface AppStockService {
public void generateStockConfigurations();
AppStock getAppStock();
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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();
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,28 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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"; /*)*/
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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());
}
}

View File

@ -0,0 +1,162 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.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());
}
}

View File

@ -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!!");
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}
}

View File

@ -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());
}
}

View File

@ -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)));
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,111 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.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;
}
}

View File

@ -0,0 +1,41 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.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;
}
}

View File

@ -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;
}
}

View File

@ -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
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 200 trackingNumberSeqForClassicServeur Tracking number sequence for classic serveur 1 4 F4 1 1
3 201 trackingNumberSeqForClassicPack Tracking number sequence for classic pack 1 4 F2 1 1
4 202 trackingNumberSeqForInkjetPrinter Tracking number sequence for inkjet printer 1 4 A5 1 1
5 5 intStockMove Internal stock move N° 1 4 ISM 1 0
6 6 outStockMove Delivery order N° 1 4 DO 1 0
7 7 inStockMove Goods receipt order N° 1 4 RO 1 0
8 8 inventory Inventory N° 1 4 INV%M%YY 1 0
9 9 productTrackingNumber Tracking N° 1 4 TN 1 0
10 203 logisticalForm Logistical form 1 4 LF 1 0

View File

@ -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 dencre";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
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 200 productTrackingNumber Sequence de numéro de suivi pour le produit Serveur Classique 1 4 F4 1 1
3 201 productTrackingNumber Sequence de numéro de suivi pour le produit Classique Pack 1 4 F2 1 1
4 202 productTrackingNumber Sequence de numéro de suivi pour le produit Imprimante Jet d’encre 1 4 A5 1 1
5 5 intStockMove Mouvement de stock interne 1 4 ISM 1 0
6 6 outStockMove Bon de livraison N° 1 4 DO 1 0
7 7 inStockMove Bon de réception de bien N° 1 4 RO 1 0
8 8 inventory Inventaire N° 1 4 INV%M%YY 1 0
9 9 productTrackingNumber Numéro de suivi 1 4 TN 1 0
10 203 logisticalForm Fiche logistique 1 4 FL 1 0

View File

@ -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>

View File

@ -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>

View File

@ -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"
1 name object can_read can_write can_create can_remove can_export condition conditionParams roleName
2 perm.stock.all com.axelor.apps.stock.db.* x x x x x Admin

View File

@ -0,0 +1,2 @@
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
"Stock";"stock";3;"Stock configuration";"app-stock.png";"axelor-stock";"base";14
1 name code installOrder description imagePath modules dependsOn sequence
2 Stock stock 3 Stock configuration app-stock.png axelor-stock base 14

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

View File

@ -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."
1 module object view field help
2 axelor-stock StockMove stock-move-form conformitySelect Enables to specify if the product (usually received) answers the request.
3 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).
4 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).
5 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.
6 axelor-stock StockMoveLine stock-move-line-form conformitySelect Enables to specify if the product (usually received) answers the request.
7 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.
8 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.
9 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.
10 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.
11 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.
12 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.
13 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).
14 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.
15 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.
16 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".
17 axelor-stock StockConfig stock-config-form freightCarrierCustomerAccountNumberList Allows you to enter your customer number to the carriers companies.
18 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.

View File

@ -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é."
1 module object view field help
2 axelor-stock StockMove stock-move-form conformitySelect Permet de spécifier si le produit (généralement reçu) est bien conforme à la demande.
3 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).
4 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).
5 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.
6 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.
7 axelor-stock Inventory inventory-form excludeOutOfStock Case à cocher permettant d'exclure des lignes d'inventaires les produits ayant un stock à 0.
8 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.
9 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.
10 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.
11 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.
12 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.
13 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)
14 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)
15 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.
16 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é".
17 axelor-stock StockConfig stock-config-form freightCarrierCustomerAccountNumberList Permet de rentrer son numéro de compte client chez ses transporteurs.
18 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é.

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