Author: Bavencoff Date: 2013-05-28 14:09:11 +0200 (Tue, 28 May 2013) New Revision: 200 Url: http://forge.codelutin.com/projects/franciaflex-magalie/repository/revisions... Log: refs #2164 : storage transfer Added: trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/StorageTransferService.java trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferAction.java trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferLocationAction.java trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-input.jsp trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-location-input.jsp trunk/magalie-web/src/main/webapp/js/storage-transfer-input.js Modified: trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/LocationDao.java trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/StoredArticleDao.java trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/LocationJpaDao.java trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/StoredArticleJpaDao.java trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/RequestedArticleService.java trunk/magalie-web/src/main/webapp/WEB-INF/content/choose-activity.jsp trunk/magalie-web/src/main/webapp/js/magalie.js Modified: trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/LocationDao.java =================================================================== --- trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/LocationDao.java 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/LocationDao.java 2013-05-28 12:09:11 UTC (rev 200) @@ -36,4 +36,6 @@ List<Location> findAllWithoutReception(Building building); List<Location> findAllWithoutReception(Warehouse warehouse); + + Location findByBarCode(String barCode); } Modified: trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/StoredArticleDao.java =================================================================== --- trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/StoredArticleDao.java 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/StoredArticleDao.java 2013-05-28 12:09:11 UTC (rev 200) @@ -25,6 +25,7 @@ import com.franciaflex.magalie.persistence.entity.Article; import com.franciaflex.magalie.persistence.entity.Building; +import com.franciaflex.magalie.persistence.entity.Location; import com.franciaflex.magalie.persistence.entity.StoredArticle; import java.util.List; @@ -39,4 +40,6 @@ StoredArticle findDetachedById(String storedArticleId); + + List<StoredArticle> findAllByLocation(Location location); } Modified: trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/LocationJpaDao.java =================================================================== --- trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/LocationJpaDao.java 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/LocationJpaDao.java 2013-05-28 12:09:11 UTC (rev 200) @@ -87,4 +87,11 @@ public List<Location> findAllWithoutReception(Warehouse warehouse) { return findAllWithoutReception(null, warehouse); } + + @Override + public Location findByBarCode(String barCode) { + TypedQuery<Location> query = createQuery("from Location l where CONCAT(l.warehouse.code, l.code) = :barCode"); + query.setParameter("barCode", barCode); + return findUniqueOrNull(query); + } } Modified: trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/StoredArticleJpaDao.java =================================================================== --- trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/StoredArticleJpaDao.java 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-persistence/src/main/java/com/franciaflex/magalie/persistence/dao/jpa/StoredArticleJpaDao.java 2013-05-28 12:09:11 UTC (rev 200) @@ -27,6 +27,7 @@ import com.franciaflex.magalie.persistence.dao.StoredArticleDao; import com.franciaflex.magalie.persistence.entity.Article; import com.franciaflex.magalie.persistence.entity.Building; +import com.franciaflex.magalie.persistence.entity.Location; import com.franciaflex.magalie.persistence.entity.StoredArticle; import javax.persistence.EntityManager; @@ -82,6 +83,17 @@ } @Override + public List<StoredArticle> findAllByLocation(Location location) { + TypedQuery<StoredArticle> query = createQuery("from StoredArticle sa where sa.location = :location"); + query.setParameter("location", location); + List<StoredArticle> resultList = findAll(query); + for (StoredArticle storedArticle : resultList) { + entityManager.detach(storedArticle); + } + return resultList; + } + + @Override public StoredArticle findDetachedById(String storedArticleId) { StoredArticle storedArticle = findById(storedArticleId); entityManager.detach(storedArticle); Modified: trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/RequestedArticleService.java =================================================================== --- trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/RequestedArticleService.java 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/RequestedArticleService.java 2013-05-28 12:09:11 UTC (rev 200) @@ -60,6 +60,7 @@ protected MagalieServiceContext serviceContext; + @Override public void setServiceContext(MagalieServiceContext serviceContext) { this.serviceContext = serviceContext; } Added: trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/StorageTransferService.java =================================================================== --- trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/StorageTransferService.java (rev 0) +++ trunk/magalie-services/src/main/java/com/franciaflex/magalie/services/service/StorageTransferService.java 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,129 @@ +package com.franciaflex.magalie.services.service; + + +import com.franciaflex.magalie.persistence.MagaliePersistenceContext; +import com.franciaflex.magalie.persistence.dao.LocationDao; +import com.franciaflex.magalie.persistence.dao.StorageMovementDao; +import com.franciaflex.magalie.persistence.dao.StoredArticleDao; +import com.franciaflex.magalie.persistence.entity.Location; +import com.franciaflex.magalie.persistence.entity.MagalieUser; +import com.franciaflex.magalie.persistence.entity.StorageMovement; +import com.franciaflex.magalie.persistence.entity.StoredArticle; +import com.franciaflex.magalie.services.MagalieService; +import com.franciaflex.magalie.services.MagalieServiceContext; +import com.google.common.collect.Lists; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class StorageTransferService implements MagalieService { + + private static final Log log = LogFactory.getLog(StorageTransferService.class); + + protected MagalieServiceContext serviceContext; + + @Override + public void setServiceContext(MagalieServiceContext serviceContext) { + this.serviceContext = serviceContext; + } + + public Location getLocationByBarCode(String barCode) { + + MagaliePersistenceContext persistenceContext = serviceContext.getPersistenceContext(); + + LocationDao locationDao = persistenceContext.getLocationDao(); + + Location location = locationDao.findByBarCode(barCode); + + return location; + } + + public Location getLocationById(String id) { + + MagaliePersistenceContext persistenceContext = serviceContext.getPersistenceContext(); + + LocationDao locationDao = persistenceContext.getLocationDao(); + + Location location = locationDao.findById(id); + + return location; + } + + public List<StoredArticle> getStoredArticlesInLocation(Location location) { + + MagaliePersistenceContext persistenceContext = serviceContext.getPersistenceContext(); + + StoredArticleDao storedArticleDao = persistenceContext.getStoredArticleDao(); + + Iterable<StoredArticle > storedArticles = storedArticleDao.findAllByLocation(location); + + ArticleStorageService articleStorageService = serviceContext.newService(ArticleStorageService.class); + + StorageMovementDao storageMovementDao = persistenceContext.getStorageMovementDao(); + + List<StorageMovement> allImpactingStoredArticle = new LinkedList<StorageMovement>(); + + for (StoredArticle storedArticle : storedArticles) { + allImpactingStoredArticle.addAll(storageMovementDao.findAllImpactingStoredArticle(storedArticle)); + } + + storedArticles = articleStorageService.computeActualQuantities(storedArticles, allImpactingStoredArticle); + + return Lists.newArrayList(storedArticles); + } + + + public StoredArticle findStoredArticle(String storedArticleId) { + + MagaliePersistenceContext persistenceContext = serviceContext.getPersistenceContext(); + + StoredArticleDao storedArticleDao = persistenceContext.getStoredArticleDao(); + + StoredArticle storedArticle; + + try { + + storedArticle = storedArticleDao.findById(storedArticleId); + + } catch (Exception e) { + + storedArticle = null; + + } + + return storedArticle; + + } + + public void confirmStorageTransfer(MagalieUser magalieUser, StoredArticle storedArticle, double Quantity, Location destinationLocation ) { + + MagaliePersistenceContext persistenceContext = serviceContext.getPersistenceContext(); + + StorageMovementDao storageMovementDao = persistenceContext.getStorageMovementDao(); + + Date now = serviceContext.getNow(); + + StorageMovement storageMovement = new StorageMovement(); + + Location originLocation = storedArticle.getLocation(); + + storageMovement.setOriginLocation(originLocation); + + storageMovement.setDestinationLocation(destinationLocation); + + storageMovement.setMagalieUser(magalieUser); + + storageMovement.setArticle(storedArticle.getArticle()); + + storageMovement.setActualQuantity(Quantity); + + storageMovement.setConfirmDate(now); + + storageMovementDao.persist(storageMovement); + + persistenceContext.commit(); + } +} Added: trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferAction.java =================================================================== --- trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferAction.java (rev 0) +++ trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferAction.java 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,152 @@ +package com.franciaflex.magalie.web.action; + +import com.franciaflex.magalie.persistence.entity.Location; +import com.franciaflex.magalie.persistence.entity.StoredArticle; +import com.franciaflex.magalie.services.service.StorageTransferService; +import com.franciaflex.magalie.web.MagalieActionSupport; +import com.franciaflex.magalie.web.MagalieSession; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts2.convention.annotation.Result; +import org.apache.struts2.convention.annotation.Results; +import org.apache.struts2.json.JSONException; +import org.apache.struts2.json.JSONUtil; + +import java.util.List; + +/** + * @author Bavencoff + */ + +@Results({ + @Result(name="success", type="redirectAction", params = { "actionName", "storage-transfer-location!input"}) +}) +public class StorageTransferAction extends MagalieActionSupport { + + private static final Log log = LogFactory.getLog(StorageTransferAction.class); + + protected MagalieSession session; + + protected StorageTransferService service; + + protected String originId; + + protected Location origin; + + protected List<StoredArticle> storedArticles; + + protected String storedArticleId; + + protected String destinationBarCode; + + protected double quantity; + + public void setService(StorageTransferService service) { + this.service = service; + } + + public void setSession(MagalieSession session) { + this.session = session; + } + + public void setOriginId(String originId) { + this.originId = originId; + } + + @Override + public String input() { + + origin = service.getLocationById(originId); + + storedArticles = service.getStoredArticlesInLocation(origin); + + return INPUT; + } + + public Location getOrigin() { + return origin; + } + + public List<StoredArticle> getStoredArticles() { + return storedArticles; + } + + public String getModelAsJson() throws JSONException { + + String json = JSONUtil.serialize(getStoredArticles()); + + return json; + + } + + public String getStoredArticleId() { + return storedArticleId; + } + + public void setStoredArticleId(String storedArticleId) { + this.storedArticleId = storedArticleId; + } + + public void setDestinationBarCode(String destinationBarCode) { + this.destinationBarCode = destinationBarCode; + } + + public void setQuantity(double quantity) { + this.quantity = quantity; + } + + @Override + public String execute() { + + if (log.isDebugEnabled()) { + log.debug("originId : " + originId + "\n" + + "storedArticleId : " + storedArticleId + "\n" + + "quantity : " + quantity + "\n" + + "destinationBarCode : " + destinationBarCode); + } + + + origin = service.getLocationById(originId); + + storedArticles = service.getStoredArticlesInLocation(origin); + + if (storedArticleId == null) { + addFieldError("storedArticleId", "Sélectionnez un article"); + return INPUT; + } + + StoredArticle storedArticle = service.findStoredArticle(storedArticleId); + + if (quantity == 0) { + addFieldError("quantity", "la quantité ne doit pas être nulle"); + return INPUT; + } + + if (quantity > storedArticle.getQuantity()) { + addFieldError("quantity", "Stock insuffisant"); + return INPUT; + } + + if (destinationBarCode.isEmpty()) { + addFieldError("destinationBarCode", "L'emplacement de destination doit être renseigné"); + return INPUT; + } + + Location destination = service.getLocationByBarCode(destinationBarCode); + + if (destination == null) { + addFieldError("destinationBarCode", "Le code barre n'est pas un code valide"); + return INPUT; + } + + if (destination.equals(storedArticle.getLocation())) { + addFieldError("destinationBarCode", "la destination doit être différente de l'origine"); + return INPUT; + } + + service.confirmStorageTransfer(session.getMagalieUser(), storedArticle, quantity, destination); + + return SUCCESS; + } + +} Added: trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferLocationAction.java =================================================================== --- trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferLocationAction.java (rev 0) +++ trunk/magalie-web/src/main/java/com/franciaflex/magalie/web/action/StorageTransferLocationAction.java 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,77 @@ +package com.franciaflex.magalie.web.action; + +import com.franciaflex.magalie.persistence.entity.Location; +import com.franciaflex.magalie.persistence.entity.StoredArticle; +import com.franciaflex.magalie.services.service.StorageTransferService; +import com.franciaflex.magalie.web.MagalieActionSupport; +import com.franciaflex.magalie.web.MagalieSession; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts2.convention.annotation.Result; +import org.apache.struts2.convention.annotation.Results; + +import java.util.List; + +/** + * @author Bavencoff + */ + +@Results({ + @Result(name="success", type="redirectAction", params = { "actionName", "storage-transfer!input", "originId", "${originId}" }) +}) +public class StorageTransferLocationAction extends MagalieActionSupport { + + private static final Log log = LogFactory.getLog(StorageTransferAction.class); + + protected MagalieSession session; + + protected StorageTransferService service; + + protected String originBarCode; + + protected Location origin; + + public void setService(StorageTransferService service) { + this.service = service; + } + + public void setSession(MagalieSession session) { + this.session = session; + } + + public void setOriginBarCode(String originBarCode) { + this.originBarCode = originBarCode; + } + + @Override + public String execute() { + + if (log.isDebugEnabled()) { + log.debug("origin BarCode : " + originBarCode); + } + + origin = service.getLocationByBarCode(originBarCode); + + if (origin == null) { + addFieldError("originBarCode", "Le code barre n'est pas un code valide"); + return INPUT; + } + + List<StoredArticle> storedArticles = service.getStoredArticlesInLocation(origin); + + if (storedArticles.isEmpty()) { + addFieldError("originBarCode", "Aucun articles dans cet emplacement"); + return INPUT; + } + + return SUCCESS; + } + + public String getOriginId() { + String originId = ""; + if (origin != null) { + originId = origin.getId(); + } + return originId; + } +} Modified: trunk/magalie-web/src/main/webapp/WEB-INF/content/choose-activity.jsp =================================================================== --- trunk/magalie-web/src/main/webapp/WEB-INF/content/choose-activity.jsp 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-web/src/main/webapp/WEB-INF/content/choose-activity.jsp 2013-05-28 12:09:11 UTC (rev 200) @@ -33,8 +33,9 @@ bindKey('2', function(){ redirectTo($('#deliverRequestedArticleLink'));}); bindKey('3', function(){ redirectTo($('#prepareArticleReceptionLink'));}); bindKey('4', function(){ redirectTo($('#preparePreparedArticleReceptionLink'));}); + bindKey('5', function(){ redirectTo($('#storageTransferLink'));}); <s:if test="movementOrderResume" > - bindKey('5', function(){ redirectTo($('#movementOrderResumeLink'));}); + bindKey('6', function(){ redirectTo($('#movementOrderResumeLink'));}); </s:if> bindKey('Esc', function(){ redirectTo($('#logoutLink'));}); }); @@ -62,11 +63,14 @@ <s:url namespace="/" action="prepare-prepared-article-reception!input" id="preparePreparedArticleReceptionUrl"/> <s:a href="%{preparePreparedArticleReceptionUrl}" cssClass="btn btn-block" id="preparePreparedArticleReceptionLink" >Traiter les réceptions fournisseurs préparées (4)</s:a> +<s:url namespace="/" action="storage-transfer-location!input" id="storageTransferUrl"/> +<s:a href="%{storageTransferUrl}" cssClass="btn btn-block" id="storageTransferLink" >Transfert de stock (5)</s:a> + <s:if test="movementOrderResume" > <s:url namespace="/" action="withdraw-item!input" id="movementOrderResumeUrl"> <s:param name="storageMovementOrderId" value="%{movementOrderResume.id}" /> </s:url> - <s:a href="%{movementOrderResumeUrl}" cssClass="btn btn-block" id="movementOrderResumeLink" >Reprendre l'ordre de mouvement (5)</s:a> + <s:a href="%{movementOrderResumeUrl}" cssClass="btn btn-block" id="movementOrderResumeLink" >Reprendre l'ordre de mouvement (6)</s:a> </s:if> <s:url namespace="/" action="logout" id="logoutUrl"/> Added: trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-input.jsp =================================================================== --- trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-input.jsp (rev 0) +++ trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-input.jsp 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,38 @@ + +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + +<head> + <title>Transfert de stock</title> + <script type="text/javascript"> + var model = <s:property value="modelAsJson" escapeHtml="false" />; + </script> + <script src="<s:url value='/js/storage-transfer-input.js' />"></script> + <link rel="stylesheet" href="<s:url value='/css/magalie-ck3x-reduced.css' />"/> +</head> + +<s:url namespace="/" action="choose-activity" id="cancelUrl" /> + +<header> + <dl class="dl-horizontal"> + <dt>Source</dt> + <dd><s:property value="origin.warehouse.code + origin.code" /></dd> + </dl> +</header> + + +<s:form> + <s:hidden name="originId" value="%{origin.id}" /> + + <s:radio name="storedArticleId" list="storedArticles" listKey="id" listValue="article.description + ' (' + article.code + ')'" /> + + <s:textfield name="quantity" label="Qté" inputAppend="" type="number" step="any" min="0" cssClass="input-mini" /> + + <s:textfield key="destinationBarCode" label="Emplacement de destination" inputAppendIcon="barcode" cssClass="input-medium" /> + + + <div class="btn-group"> + <s:a href="%{cancelUrl}" cssClass="btn" id="CancelLink" >Quitter (Esc)</s:a> + <s:submit name="" value="Valider" cssClass="btn btn-primary" /> + </div> +</s:form> \ No newline at end of file Added: trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-location-input.jsp =================================================================== --- trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-location-input.jsp (rev 0) +++ trunk/magalie-web/src/main/webapp/WEB-INF/content/storage-transfer-location-input.jsp 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,24 @@ + +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + +<head> + <title>Transfert de stock</title> + <script type="text/javascript"> + $(document).ready(function () { + $("#storage-transfer-location_originBarCode").focus(); + bindKey('Esc', function(){ redirectTo($('#chooseActivityLink'));}); + }); + </script> +</head> + +<s:url namespace="/" action="choose-activity" id="chooseActivityUrl"/> + +<s:form> + <s:textfield key="originBarCode" label="Emplacement de prélévement" inputAppendIcon="barcode" cssClass="input-medium" /> + + <div class="btn-group"> + <s:a href="%{chooseActivityUrl}" cssClass="btn" id="chooseActivityLink" >Changer d'activité (Esc)</s:a> + <s:submit name="next" value="Suivant" cssClass="btn btn-primary" /> + </div> +</s:form> \ No newline at end of file Modified: trunk/magalie-web/src/main/webapp/js/magalie.js =================================================================== --- trunk/magalie-web/src/main/webapp/js/magalie.js 2013-05-27 21:29:10 UTC (rev 199) +++ trunk/magalie-web/src/main/webapp/js/magalie.js 2013-05-28 12:09:11 UTC (rev 200) @@ -74,7 +74,7 @@ function bindKey(key, handler) { $(document).keydown(function(event) { - if (event.which == KEYS[key]) { + if (event.which == KEYS[key] && !($(document.activeElement).is(":input") && SHORTCUTS.indexOf(KEYS[key]) >= O)) { event.preventDefault(); handler(); } Added: trunk/magalie-web/src/main/webapp/js/storage-transfer-input.js =================================================================== --- trunk/magalie-web/src/main/webapp/js/storage-transfer-input.js (rev 0) +++ trunk/magalie-web/src/main/webapp/js/storage-transfer-input.js 2013-05-28 12:09:11 UTC (rev 200) @@ -0,0 +1,52 @@ + +model.storedArticleIndex = 0; + +model.setArticle = function(StorageArticleId) { + for (var i = 0; i < this.length; i++) { + if (this[i].id == StorageArticleId) { + model.storedArticleIndex = i; + } + } + return this.storedArticleIndex; +}; + +model.getQuantity = function() { + return this[this.storedArticleIndex].quantity; +}; + +model.getUnit = function() { + return this[this.storedArticleIndex].article.unit; + +}; + +var view = { + + refreshQuantity : function() { + $('#storage-transfer_quantity').val(model.getQuantity()); + $('#storage-transfer_quantity').next('span').text(model.getUnit()); + }, + + +}; + +var controller = { + + onChangeArticle : function () { + var StoredArticleId = $('input[type=radio][name=storedArticleId]:checked').attr('value'); + model.setArticle(StoredArticleId); + view.refreshQuantity(); + } + +}; + +$(document).ready(function () { + var inputRadios = $("input[name='storedArticleId']") ; + $.each(inputRadios, function(index, radio) { + if (index < SHORTCUTS.length) { + $(radio).parent().append(" (" + SHORTCUTS[index] + ")") + bindKey(SHORTCUTS[index], function(){ $(radio).click();}); + } + }); + $('input[type=radio][name=storedArticleId]').change(controller.onChangeArticle); + bindKey('Esc', function(){ redirectTo($('#CancelLink'));}); +}); \ No newline at end of file