You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by sh...@apache.org on 2016/11/21 08:07:58 UTC

svn commit: r1770621 [4/5] - in /ofbiz/trunk/specialpurpose/pricat: ./ config/ data/ entitydef/ groovyScripts/ groovyScripts/pricat/ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/ofbiz/ src/main/jav...

Added: ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlReport.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlReport.java?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlReport.java (added)
+++ ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlReport.java Mon Nov 21 08:07:57 2016
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.pricat;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.ofbiz.htmlreport.AbstractHtmlReport;
+import org.apache.ofbiz.htmlreport.InterfaceReportThread;
+import org.apache.ofbiz.htmlreport.util.ReportStringUtil;
+
+/**
+ * Provides a html report for running parse a PriCat file.<p> 
+ * 
+ */
+public class PricatParseExcelHtmlReport extends AbstractHtmlReport {
+	
+	public static final String PRICAT_REPORT_CLASS = "PRICAT_HTML_REPORT";
+	
+    /**
+     * Public constructor with report variables.<p>
+     * 
+     * @param req the HttpServletRequest request
+     * @param res the HttpServletResponse response
+     */
+    public PricatParseExcelHtmlReport(HttpServletRequest request, HttpServletResponse response) {
+
+        super(request, response, true, true);
+    }
+    
+    public static PricatParseExcelHtmlReport getReport(HttpServletRequest request, HttpServletResponse response) {
+    	
+    	PricatParseExcelHtmlReport wp = (PricatParseExcelHtmlReport) request.getAttribute(PRICAT_REPORT_CLASS);
+    	if (wp == null) {
+    		wp = new PricatParseExcelHtmlReport(request, response);
+    		request.setAttribute(PRICAT_REPORT_CLASS, wp);
+    	}
+    	return wp;
+    }
+    
+    public InterfaceReportThread initializeThread(HttpServletRequest request, HttpServletResponse response, String name) {
+
+		if (name == null) {
+			name = "";
+		}
+        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+        int i = threadGroup.activeCount();
+        Thread[] threads = new Thread[i];
+        threadGroup.enumerate(threads, true);
+        InterfaceReportThread thread = null;
+        for (int j=0; j<threads.length; j++) {
+        	Thread threadInstance = threads[j];
+        	if (threadInstance instanceof PricatParseExcelHtmlThread) {
+        		thread = (PricatParseExcelHtmlThread) threadInstance;
+        		break;
+        	}
+        }
+        if (thread == null) {
+            thread = new PricatParseExcelHtmlThread(request, response, name);
+        }
+
+        return thread;
+    }
+
+    public static String checkButton(HttpServletRequest request, HttpServletResponse response) {
+    	String action = request.getParameter("action");
+    	if (ReportStringUtil.isNotEmpty(action)) {
+    		if (action.equalsIgnoreCase("ok")) {
+    			request.removeAttribute(PRICAT_REPORT_CLASS);
+    			request.removeAttribute(DIALOG_URI);
+    			return "ok";
+    		} else if (action.equalsIgnoreCase("cancel")) {
+    			request.removeAttribute(PRICAT_REPORT_CLASS);
+    			request.removeAttribute(DIALOG_URI);
+    			return "cancel";
+    		}
+    	}
+    	action = request.getParameter("ok");
+    	if (ReportStringUtil.isNotEmpty(action)) {
+    		if (action.equalsIgnoreCase("ok")) {
+    			request.removeAttribute(PRICAT_REPORT_CLASS);
+    			request.removeAttribute(DIALOG_URI);
+    			return "ok";
+    		}
+    	}
+        action = request.getParameter("cancel");
+        if (ReportStringUtil.isNotEmpty(action)) {
+        	if (action.equalsIgnoreCase("cancel")) {
+    			request.removeAttribute(PRICAT_REPORT_CLASS);
+    			request.removeAttribute(DIALOG_URI);
+        		return "cancel";
+        	}
+        }
+        
+    	return "success";
+    }
+
+    /**
+     * Performs the dialog actions depending on the initialized action.<p>
+     * 
+     * @throws IOException 
+     */
+    public void prepareDisplayReport(HttpServletRequest request, HttpServletResponse response, String name, String dialogUri) throws IOException {
+
+    	if (ReportStringUtil.isNotEmpty(dialogUri)) {
+    		setDialogRealUri(request, dialogUri);
+    	}
+    	
+        String action = getParamAction(request);
+        if (action == null) action = "";
+        if (action.equals("reportend") || action.equals("cancel")) {
+            setParamAction("reportend");
+            setDialogRealUri(request, dialogUri);
+        } else if (action.equals("reportupdate")) {
+            setParamAction("reportupdate");
+        } else {
+            InterfaceReportThread thread = initializeThread(request, response, name);
+            thread.start();
+            setParamAction("reportbegin");
+            setParamThread(thread.getUUID().toString());
+        }
+    }
+}

Added: ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlThread.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlThread.java?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlThread.java (added)
+++ ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/PricatParseExcelHtmlThread.java Mon Nov 21 08:07:57 2016
@@ -0,0 +1,359 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.pricat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.ofbiz.htmlreport.AbstractReportThread;
+import org.apache.ofbiz.htmlreport.InterfaceReport;
+import org.apache.ofbiz.pricat.sample.SamplePricatParser;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.FileUtil;
+import org.apache.ofbiz.base.util.UtilDateTime;
+import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilProperties;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.condition.EntityCondition;
+import org.apache.ofbiz.entity.condition.EntityOperator;
+import org.apache.ofbiz.entity.util.EntityUtil;
+import org.apache.ofbiz.service.LocalDispatcher;
+
+/**
+ * Thread for running pricat import excel html report.
+ * 
+ */
+public class PricatParseExcelHtmlThread extends AbstractReportThread {
+	
+	public static final String module = PricatParseExcelHtmlThread.class.getName();
+
+	public static final String PARSE_EXCEL = "parse_excel";
+	
+	public static final String CONFIRM = "confirm_action";
+	
+	public static final String[] messageLabels = new String[] {"FORMAT_DEFAULT", "FORMAT_WARNING", "FORMAT_HEADLINE", "FORMAT_NOTE", "FORMAT_OK", "FORMAT_ERROR", "FORMAT_THROWABLE"};
+	
+	public static final List<String> messages = Collections.unmodifiableList(Arrays.asList(messageLabels));
+	
+    public static final String FileDateTimePattern = "yyyyMMddHHmmss";
+    
+    public static final String defaultColorName = "DefaultColor";
+    
+    public static final String defaultDimensionName = "DefaultDimension";
+    
+    public static final String defaultCategoryName = "DefaultCategory";
+    
+    public static final String EXCEL_TEMPLATE_TYPE = "excelTemplateType";
+    
+    public static final String FACILITY_ID = "facilityId";
+    
+	private LocalDispatcher dispatcher;
+	
+	private Delegator delegator;
+	
+	private List<FileItem> fileItems;
+	
+	private File pricatFile;
+	
+	private String userLoginId;
+	
+	private GenericValue userLogin;
+	
+	private Map<String, String[]> facilities = new HashMap<String, String[]>();
+	
+    public static final String resource = "PricatUiLabels";
+    
+    private HttpSession session;
+    
+    public static final String PRICAT_FILE = "__PRICAT_FILE__";
+
+    public static final String DEFAULT_PRICAT_TYPE = "sample_pricat";
+    
+    private String selectedPricatType = DEFAULT_PRICAT_TYPE;
+    
+    private String selectedFacilityId;
+    
+    public static final Map<String, String> PricatTypeLabels = UtilMisc.toMap("sample_pricat", "SamplePricatTemplate",
+    		                                                                  "ofbiz_pricat", "OFBizPricatTemplate");
+    
+    private InterfacePricatParser pricatParser;
+    
+    private String thruReasonId = "EXCEL_IMPORT_SUCCESS";
+    
+	/**
+     * Constructor, creates a new html thread.
+	 * 
+	 * @param request
+	 * @param response
+	 * @param name
+	 */
+    public PricatParseExcelHtmlThread(HttpServletRequest request, HttpServletResponse response, String name) {
+
+        super(request, response, name);
+		dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
+		setDelegator(dispatcher.getDelegator());
+		userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
+		if (UtilValidate.isEmpty(userLogin)) {
+	        initHtmlReport(request, response, true, true);
+			getReport().println(UtilProperties.getMessage(resource, "PricatRequireUserLogin", getLocale()), InterfaceReport.FORMAT_ERROR);
+			return;
+		} else {
+			userLoginId = userLogin.getString("userLoginId");
+			session = request.getSession();
+		}
+		
+		long sequenceNum = addExcelImportHistory();
+    	File userFolder = FileUtil.getFile(InterfacePricatParser.tempFilesFolder + userLoginId + "/");
+    	if (!userFolder.exists()) {
+    		userFolder.mkdirs();
+    	}
+		String logFileName = InterfacePricatParser.tempFilesFolder + userLoginId + "/" + sequenceNum + ".log";
+        initHtmlReport(request, response, true, true, logFileName);
+		if (sequenceNum > 0) {
+			getReport().setSequenceNum(sequenceNum);
+			getReport().addLogFile(logFileName);
+		}
+        try {
+        	getReport().print(UtilProperties.getMessage(resource, "StartStoreExcel", getLocale()), InterfaceReport.FORMAT_HEADLINE);
+            ServletFileUpload dfu = new ServletFileUpload(new DiskFileItemFactory(10240, userFolder));
+        	fileItems = UtilGenerics.checkList(dfu.parseRequest(request));
+        } catch (FileUploadException e) {
+            getReport().addError(e);
+        }
+        if (UtilValidate.isEmpty(fileItems)) {
+        	getReport().println(UtilProperties.getMessage(resource, "NoFileUploaded", getLocale()), InterfaceReport.FORMAT_ERROR);
+        } else {
+        	getReport().println(UtilProperties.getMessage(resource, "ok", getLocale()), InterfaceReport.FORMAT_OK);
+        }
+    }
+
+    public String getReportUpdate() {
+
+        return getReport().getReportUpdate();
+    }
+
+    /**
+     * @see java.lang.Runnable#run()
+     */
+    public void run() {
+
+        try {
+            if (getName().startsWith(PARSE_EXCEL) && UtilValidate.isNotEmpty(fileItems)) {
+    			getReport().println();
+            	getReport().println(UtilProperties.getMessage(resource, "StartParsePricat", getLocale()), InterfaceReport.FORMAT_HEADLINE);
+            	if (prepareParse()) {
+                	if (selectedPricatType.equals(DEFAULT_PRICAT_TYPE)) {
+                		pricatParser = new SamplePricatParser(dispatcher, delegator, getLocale(), getReport(), facilities, pricatFile, userLogin);
+                	}
+                	if (UtilValidate.isEmpty(pricatParser)) {
+                		getReport().println(UtilProperties.getMessage(resource, "NoPricatParserFor", getLocale()), InterfaceReport.FORMAT_ERROR);
+                	} else {
+                    	pricatParser.parsePricatExcel();
+                    	getReport().println(UtilProperties.getMessage(resource, "PricatParseCompleted", getLocale()), InterfaceReport.FORMAT_HEADLINE);
+                	}
+            	}
+        	} else {
+            	getReport().println(getName(), InterfaceReport.FORMAT_ERROR);
+            	Debug.logError(getName(), module);
+            	thruReasonId = "EXCEL_IMPORT_ERROR";
+            }
+        } catch (Exception e) {
+        	getReport().println(e);
+        	Debug.logError(e, module);
+        	thruReasonId = "EXCEL_IMPORT_ERROR";
+        } finally {
+        	// wait 5 seconds to wait page output
+        	try {
+				sleep(5000);
+			} catch (InterruptedException e) {
+			}
+        	// call report update to make sure all messages are output to file
+        	getReport().getReportUpdate();
+        	String logFileName = getReport().closeLogFile();
+        	if (UtilValidate.isNotEmpty(pricatParser)) {
+        		if (thruReasonId.equals("EXCEL_IMPORT_SUCCESS") && pricatParser.hasErrorMessages()) {
+        			thruReasonId = "EXCEL_IMPORT_QUEST";
+        		}
+            	pricatParser.endExcelImportHistory(logFileName, thruReasonId);
+        	}
+        }
+    }
+
+    private boolean prepareParse() throws IOException {
+		// 1 get facilities belong to current userLogin
+		facilities = getCurrentUserLoginFacilities();
+		if (UtilValidate.isEmpty(facilities)) {
+			getReport().println(UtilProperties.getMessage(resource, "CurrentUserLoginNoFacility", new Object[]{userLoginId}, getLocale()), InterfaceReport.FORMAT_ERROR);
+			return false;
+		} else {
+			getReport().println(" ... " + UtilProperties.getMessage(resource, "ok", getLocale()), InterfaceReport.FORMAT_OK);
+			getReport().println();
+		}
+		
+		// 2. store the pricat excel file
+		if (!storePricatFile()) {
+			return false;
+		}
+		return true;
+	}
+
+	private boolean storePricatFile() throws IOException {
+        FileItem fi = null;
+        FileItem pricatFi = null;
+        byte[] pricatBytes = {};
+        // check excelTemplateType
+        for (int i = 0; i < fileItems.size(); i++) {
+        	fi = fileItems.get(i);
+        	String fieldName = fi.getFieldName();
+        	if (fi.isFormField() && UtilValidate.isNotEmpty(fieldName)) {
+        		if (fieldName.equals(EXCEL_TEMPLATE_TYPE)) {
+        			selectedPricatType = fi.getString();
+        		}
+        	}
+        }
+        getReport().print(UtilProperties.getMessage(resource, "ExcelTemplateTypeSelected", getLocale()), InterfaceReport.FORMAT_DEFAULT);
+        if (PricatTypeLabels.containsKey(selectedPricatType)) {
+            getReport().print(UtilProperties.getMessage(resource, PricatTypeLabels.get(selectedPricatType), getLocale()), InterfaceReport.FORMAT_DEFAULT);
+			getReport().println(" ... " + UtilProperties.getMessage(resource, "ok", getLocale()), InterfaceReport.FORMAT_OK);
+        } else {
+            getReport().println(UtilProperties.getMessage(resource, PricatTypeLabels.get(selectedPricatType), getLocale()), InterfaceReport.FORMAT_ERROR);
+        	return false;
+        }
+
+        // store the file
+        for (int i = 0; i < fileItems.size(); i++) {
+            fi = fileItems.get(i);
+            String fieldName = fi.getFieldName();
+            if (fieldName.equals("filename")) {
+                pricatFi = fi;
+                pricatBytes = pricatFi.get();
+                Path path = Paths.get(fi.getName());
+                pricatFile = new File(InterfacePricatParser.tempFilesFolder + userLoginId + "/" + path.getFileName().toString());
+                FileOutputStream fos = new FileOutputStream(pricatFile);
+                fos.write(pricatBytes);
+                fos.flush();
+                fos.close();
+                session.setAttribute(PRICAT_FILE, pricatFile.getAbsolutePath());
+            }
+        }
+        return true;
+	}
+
+	private Map<String, String[]> getCurrentUserLoginFacilities() {
+		getReport().println();
+		getReport().println(UtilProperties.getMessage(resource, "GetCurrentUserLoginFacility", getLocale()), InterfaceReport.FORMAT_DEFAULT);
+		Map<String, Object> context = new HashMap<String, Object>();
+		context.put("userLogin", userLogin);
+		context.put("locale", getLocale());
+		try {
+			List<EntityCondition> orgConditions = new LinkedList<EntityCondition>();
+			orgConditions.add(EntityCondition.makeCondition("onePartyIdFrom", EntityOperator.EQUALS, userLogin.getString("partyId")));
+			orgConditions.add(EntityCondition.makeCondition("twoRoleTypeIdFrom", EntityOperator.EQUALS, "INTERNAL_ORGANIZATIO"));
+			orgConditions.add(EntityCondition.makeCondition("twoRoleTypeIdTo", EntityOperator.EQUALS, "EMPLOYEE"));
+			orgConditions.add(EntityCondition.makeCondition("twoRoleTypeIdTo", EntityOperator.EQUALS, "EMPLOYEE"));
+			List<GenericValue> organizations = delegator.findList("PartyRelationshipToFrom", EntityCondition.makeCondition(orgConditions), null, null, null, false);
+			Timestamp now = UtilDateTime.nowTimestamp();
+			organizations = EntityUtil.filterByDate(organizations, now, "twoFromDate", "twoThruDate", true);
+			organizations = EntityUtil.filterByDate(organizations, now, "oneFromDate", "oneThruDate", true);
+			
+			List<EntityCondition> ownerPartyConditions = new LinkedList<EntityCondition>();
+			Set<String> orgPartyIds = new HashSet<String>();
+			for (GenericValue organization : organizations) {
+				String orgPartyId = organization.getString("onePartyIdTo");
+				if (!orgPartyIds.contains(orgPartyId)) {
+					ownerPartyConditions.add(EntityCondition.makeCondition("ownerPartyId", EntityOperator.EQUALS, orgPartyId));
+					orgPartyIds.add(orgPartyId);
+				}
+			}
+			if (UtilValidate.isEmpty(ownerPartyConditions)) {
+				return facilities;
+			}
+			
+			List<GenericValue> facilityValues = delegator.findList("Facility", EntityCondition.makeCondition(ownerPartyConditions, EntityOperator.OR), null, null, null, false);
+			if (UtilValidate.isNotEmpty(facilityValues)) {
+				int i = 1;
+				for (GenericValue facilityValue : facilityValues) {
+					if (UtilValidate.isNotEmpty(facilityValue)) {
+						String facilityId = facilityValue.getString("facilityId");
+						if (!facilities.containsKey(facilityId)) {
+							String facilityName = facilityValue.getString("facilityName");
+							facilities.put(facilityId, new String[] {facilityName, facilityValue.getString("ownerPartyId")});
+							getReport().println(UtilProperties.getMessage(resource, "FacilityFoundForCurrentUserLogin", new Object[]{String.valueOf(i), facilityName, facilityId}, getLocale()), InterfaceReport.FORMAT_NOTE);
+							i++;
+						}
+					}
+				}
+			}
+		}catch (GenericEntityException e) {
+			Debug.logError(e.getMessage(), module);
+		}
+		return facilities;
+	}
+
+	public Delegator getDelegator() {
+		return delegator;
+	}
+
+	public void setDelegator(Delegator delegator) {
+		this.delegator = delegator;
+	}
+
+	public synchronized long addExcelImportHistory() {
+		long latestId = 1;
+		try {
+			List<GenericValue> historyValues = delegator.findByAnd("ExcelImportHistory", UtilMisc.toMap("userLoginId", userLoginId), UtilMisc.toList("sequenceNum DESC"), false);
+			GenericValue latestHistoryValue = EntityUtil.getFirst(historyValues);
+			if (UtilValidate.isNotEmpty(latestHistoryValue)) {
+				latestId = latestHistoryValue.getLong("sequenceNum") + 1;
+			}
+			GenericValue newHistoryValue = delegator.makeValue("ExcelImportHistory", UtilMisc.toMap("sequenceNum", latestId, "userLoginId", userLoginId,
+																"fileName", pricatFile == null ? "" : pricatFile.getName(), "statusId", isAlive() ? "EXCEL_IMPORTING" : "EXCEL_IMPORTED", 
+																"fromDate", UtilDateTime.nowTimestamp(), "threadName", getName(), "logFileName", InterfacePricatParser.tempFilesFolder + userLoginId + "/" + latestId + ".log"));
+			newHistoryValue.create();
+		} catch (GenericEntityException e) {
+			Debug.logError(e, module);
+			return -1;
+		}
+		return latestId;
+	}
+}

Added: ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatEvents.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatEvents.java?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatEvents.java (added)
+++ ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatEvents.java Mon Nov 21 08:07:57 2016
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.pricat.sample;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jdom.JDOMException;
+import org.apache.ofbiz.base.location.ComponentLocationResolver;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilHttp;
+import org.apache.ofbiz.base.util.UtilProperties;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.pricat.PricatEvents;
+
+public class SamplePricatEvents extends PricatEvents {
+	
+	public static final String module = SamplePricatEvents.class.getName();
+	
+	public static final String PricatLatestVersion = UtilProperties.getPropertyValue("pricat", "pricat.latest.version", "V1.1");
+	
+	public static final String DemoPricatFileName = "SamplePricatTemplate_" + PricatLatestVersion + ".xlsx";
+	
+	public static final String DemoPricatPath = "component://pricat/webapp/pricatdemo/downloads/";
+	
+    /**
+	 * Download excel template.
+	 * 
+	 * @param request
+	 * @param response
+	 * @return
+	 * @throws IOException
+	 * @throws JDOMException
+	 */
+	public static String downloadExcelTemplate(HttpServletRequest request, HttpServletResponse response) {
+		String templateType = request.getParameter("templateType");
+		if (UtilValidate.isEmpty(templateType)) {
+			return "error";
+		}
+		try {
+			String path = ComponentLocationResolver.getBaseLocation(DemoPricatPath).toString();
+			String fileName = null;
+			if ("pricatExcelTemplate".equals(templateType)) {
+				fileName = DemoPricatFileName;
+			}
+			if (UtilValidate.isEmpty(fileName)) {
+				return "error";
+			}
+	        Path file = Paths.get(path + fileName);
+	        byte[] bytes = Files.readAllBytes(file);
+	        UtilHttp.streamContentToBrowser(response, bytes, "application/octet-stream", URLEncoder.encode(fileName, "UTF-8"));
+		} catch (MalformedURLException e) {
+			Debug.logError(e.getMessage(), module);
+			return "error";
+		} catch (IOException e) {
+			Debug.logError(e.getMessage(), module);
+			return "error";
+		}
+        return "success";
+    }
+}

Added: ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatParser.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatParser.java?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatParser.java (added)
+++ ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/ofbiz/pricat/sample/SamplePricatParser.java Mon Nov 21 08:07:57 2016
@@ -0,0 +1,665 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.pricat.sample;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.poi.POIXMLException;
+import org.apache.poi.hssf.usermodel.HSSFDataFormatter;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.ofbiz.pricat.AbstractPricatParser;
+import org.apache.ofbiz.htmlreport.InterfaceReport;
+import org.apache.ofbiz.order.finaccount.FinAccountHelper;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilProperties;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.transaction.GenericTransactionException;
+import org.apache.ofbiz.service.LocalDispatcher;
+import org.apache.ofbiz.service.ServiceUtil;
+
+/**
+ * Sample pricat excel parser.
+ * 
+ */
+public class SamplePricatParser extends AbstractPricatParser {
+	
+	public static final String module = SamplePricatParser.class.getName();
+
+    public static final Map<String, List<Object[]>> ColNamesList = UtilMisc.toMap("V1.1", genExcelHeaderNames("V1.1"));
+
+    public static final int headerRowNo = 4;
+    
+    private List<String> headerColNames = new ArrayList<String>();
+    
+    public SamplePricatParser(LocalDispatcher dispatcher, Delegator delegator, Locale locale, InterfaceReport report, Map<String, String[]> facilities, File pricatFile, GenericValue userLogin) {
+        super(dispatcher, delegator, locale, report, facilities, pricatFile, userLogin);
+    }
+
+	/**
+     * Parse pricat excel file in xlsx format.
+     * 
+     */
+	public void parsePricatExcel(boolean writeFile) {
+		XSSFWorkbook workbook = null;
+		try {
+			// 1. read the pricat excel file
+            FileInputStream is = new FileInputStream(pricatFile);
+			
+			// 2. use POI to load this bytes
+        	report.print(UtilProperties.getMessage(resource, "ParsePricatFileStatement", new Object[] { pricatFile.getName() }, locale), InterfaceReport.FORMAT_DEFAULT);
+			try {
+			    workbook = new XSSFWorkbook(is);
+				report.println(UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+			} catch(IOException e) {
+				report.println(e);
+				report.println(UtilProperties.getMessage(resource, "PricatSuggestion", locale), InterfaceReport.FORMAT_ERROR);
+				return;
+			} catch(POIXMLException e) {
+				report.println(e);
+				report.println(UtilProperties.getMessage(resource, "PricatSuggestion", locale), InterfaceReport.FORMAT_ERROR);
+				return;
+			}
+			
+			// 3. only first sheet will be parsed
+			// 3.1 verify the file has a sheet at least
+			formatter = new HSSFDataFormatter(locale);
+			isNumOfSheetsOK(workbook);
+			
+			// 3.2 verify the version is supported
+			XSSFSheet sheet = workbook.getSheetAt(0);
+			if (!isVersionSupported(sheet)) {
+				return;
+			}
+			
+			// 3.3 get currencyId
+			existsCurrencyId(sheet);
+
+			// 3.4 verify the table header row is just the same as column names, if not, print error and return
+			if (!isTableHeaderMatched(sheet)) {
+				return;
+			}
+			
+			// 3.5 verify the first table has 6 rows at least
+			containsDataRows(sheet);
+			
+			if (UtilValidate.isNotEmpty(errorMessages)) {
+				report.println(UtilProperties.getMessage(resource, "HeaderContainsError", locale), InterfaceReport.FORMAT_ERROR);
+				return;
+			}
+			
+	        // 4. parse data
+			// 4.1 parse row by row and store the contents into database
+			parseRowByRow(sheet);
+			if (UtilValidate.isNotEmpty(errorMessages)) {
+				report.println(UtilProperties.getMessage(resource, "DataContainsError", locale), InterfaceReport.FORMAT_ERROR);
+				if (writeFile) {
+					sequenceNum = report.getSequenceNum();
+					writeCommentsToFile(workbook, sheet);
+				}
+			}
+			
+			// 5. clean up the log files and commented Excel files
+			cleanupLogAndCommentedExcel();
+		} catch (IOException e) {
+			report.println(e);
+			Debug.logError(e, module);
+		} finally {
+            if (UtilValidate.isNotEmpty(fileItems)) {
+    			// remove tmp files
+                FileItem fi = null;
+                for (int i = 0; i < fileItems.size(); i++) {
+                    fi = fileItems.get(i);
+                    fi.delete();
+                }
+            }
+            if (workbook != null) {
+            	try {
+					workbook.close();
+				} catch (IOException e) {
+					Debug.logError(e, module);
+				}
+            }
+		}
+	}
+
+	public boolean existsCurrencyId(XSSFSheet sheet) {
+		report.print(UtilProperties.getMessage(resource, "StartCheckCurrencyId", locale), InterfaceReport.FORMAT_NOTE);
+		XSSFCell currencyIdCell = sheet.getRow(2).getCell(1);
+		currencyId = currencyIdCell.getStringCellValue().trim().toUpperCase();
+		if (UtilValidate.isEmpty(currencyId)) {
+			String errorMessage = UtilProperties.getMessage(resource, "CurrencyIdRequired", locale);
+			report.println(errorMessage, InterfaceReport.FORMAT_ERROR);
+			errorMessages.put(new CellReference(currencyIdCell), errorMessage);
+			return false;
+		} else {
+			try {
+				GenericValue currencyUom = delegator.findOne("Uom", UtilMisc.toMap("uomId", currencyId), false);
+				if (!"CURRENCY_MEASURE".equals(currencyUom.getString("uomTypeId"))) {
+					String errorMessage = UtilProperties.getMessage(resource, "CurrencyIdNotCurrency", new Object[] {currencyId}, locale);
+					report.println(errorMessage, InterfaceReport.FORMAT_ERROR);
+					errorMessages.put(new CellReference(currencyIdCell), errorMessage);
+					return false;
+				}
+			} catch(GenericEntityException e) {
+				String errorMessage = UtilProperties.getMessage(resource, "CurrencyIdNotFound", new Object[] {currencyId}, locale);
+				report.println(errorMessage, InterfaceReport.FORMAT_ERROR);
+				errorMessages.put(new CellReference(currencyIdCell), errorMessage);
+				return false;
+			}
+			report.print(UtilProperties.getMessage(resource, "CurrencyIdIs", new Object[] {currencyId}, locale), InterfaceReport.FORMAT_NOTE);
+			report.println(" ... " + UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+		}
+		return true;
+	}
+
+	public void parseRowByRow(XSSFSheet sheet) {
+		int rows = sheet.getPhysicalNumberOfRows();
+		List<Object[]> colNames = ColNamesList.get(pricatFileVersion);
+		int colNumber = colNames.size();
+
+		int emptyRowStart = -1;
+		int emptyRowEnd = -1;
+		for (int i = headerRowNo + 1; i < rows; i++) {
+			XSSFRow row = sheet.getRow(i);
+			if (UtilValidate.isEmpty(row) || isEmptyRow(row, colNumber, false)) {
+				if (emptyRowStart == -1) {
+					report.print("(" + (i + 1) + ") ", InterfaceReport.FORMAT_NOTE);
+					emptyRowStart = i;
+				} else {
+					emptyRowEnd = i;
+				}
+				continue;
+			} else {
+				if (emptyRowStart != -1) {
+					if (emptyRowEnd != -1) {
+						report.print(" - (" + (emptyRowEnd + 1) + ") ", InterfaceReport.FORMAT_NOTE);
+					}
+					report.print(UtilProperties.getMessage(resource, "ExcelEmptyRow", locale), InterfaceReport.FORMAT_NOTE);
+					report.println(" ... " + UtilProperties.getMessage(resource, "skipped", locale), InterfaceReport.FORMAT_NOTE);
+					emptyRowStart = -1;
+					emptyRowEnd = -1;
+				}
+			}
+			report.print("(" + (i + 1) + ") ", InterfaceReport.FORMAT_NOTE);
+			List<Object> cellContents = getCellContents(row, colNames, colNumber);
+			try {
+				if (parseCellContentsAndStore(row, cellContents)) {
+					report.println(" ... " + UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+				} else {
+					report.println(" ... " + UtilProperties.getMessage(resource, "skipped", locale), InterfaceReport.FORMAT_NOTE);
+				}
+			} catch (GenericTransactionException e) {
+				report.println(e);
+			}
+		}
+		if (emptyRowEnd != -1) {
+			report.print(" - (" + (emptyRowEnd + 1) + ") ", InterfaceReport.FORMAT_NOTE);
+			report.print(UtilProperties.getMessage(resource, "ExcelEmptyRow", locale), InterfaceReport.FORMAT_NOTE);
+			report.println(" ... " + UtilProperties.getMessage(resource, "skipped", locale), InterfaceReport.FORMAT_NOTE);
+		}
+	}
+
+	/**
+	 * Check data according to business logic. If data is ok, store it.
+	 * 
+	 * @param row
+	 * @param cellContents
+	 * @param document
+	 * @return
+	 * @throws GenericTransactionException 
+	 */
+	public boolean parseCellContentsAndStore(XSSFRow row, List<Object> cellContents) throws GenericTransactionException {
+		if (UtilValidate.isEmpty(cellContents)) {
+			return false;
+		}
+		switch(pricatFileVersion) {
+			case "V1.1":
+			default:
+				return parseCellContentsAndStoreV1_X(row, cellContents);
+		}
+	}
+	
+	private boolean parseCellContentsAndStoreV1_X(XSSFRow row, List<Object> cellContents) throws GenericTransactionException {
+		if (UtilValidate.isEmpty(cellContents)) {
+			return false;
+		}
+		// 1. check if facilityId is in the facilities belong to the user, or if the name is correct for the id
+		String facilityName = (String) getCellContent(cellContents, "Facility Name");
+		String facilityId = (String) getCellContent(cellContents, "FacilityId");
+		if (!isFacilityOk(row, facilityName, facilityId)) 
+			return false;
+		
+		// 2. get productCategoryId
+		String ownerPartyId = facilities.get(facilityId)[1];
+		String productCategoryId = getProductCategoryId(cellContents, ownerPartyId);
+		
+		// 3. get productFeatureId of brand
+		String brandName = (String) getCellContent(cellContents, "Brand");
+		String brandId = getBrandId(brandName, ownerPartyId);
+		if (UtilValidate.isEmpty(brandId)) {
+			return false;
+		}
+
+		// 4. get productId from brandId, model name
+		String modelName = (String) getCellContent(cellContents, "Style No");
+		String productName = (String) getCellContent(cellContents, "Product Name");
+		BigDecimal listPrice = (BigDecimal) getCellContent(cellContents, "List Price");
+		String productId = getProductId(row, brandId, modelName, productName, productCategoryId, ownerPartyId, listPrice);
+		if (UtilValidate.isEmpty(productId) || UtilValidate.isEmpty(listPrice)) {
+			return false;
+		}
+
+		// 5. update color and size if necessary
+		String color = (String) getCellContent(cellContents, "Color");
+		if (UtilValidate.isEmpty(color) || UtilValidate.isEmpty(color.trim())) {
+			color = defaultColorName;
+		}
+		String dimension = (String) getCellContent(cellContents, "Size");
+		if (UtilValidate.isEmpty(dimension) || UtilValidate.isEmpty(dimension.trim())) {
+			dimension = defaultDimensionName;
+		}
+		Map<String, Object> features = updateColorAndDimension(productId, ownerPartyId, color, dimension);
+		if (ServiceUtil.isError(features)) {
+			if (features.containsKey("index") && String.valueOf(features.get("index")).contains("0")) {
+				int cell = headerColNames.indexOf("Color");
+				XSSFCell colorCell = row.getCell(cell);
+				errorMessages.put(new CellReference(colorCell), UtilProperties.getMessage(resource, "PricatColorError", locale));
+			}
+			if (features.containsKey("index") && String.valueOf(features.get("index")).contains("1")) {
+				int cell = headerColNames.indexOf("Size");
+				XSSFCell colorCell = row.getCell(cell);
+				errorMessages.put(new CellReference(colorCell), UtilProperties.getMessage(resource, "PricatDimensionError", locale));
+			}
+			return false;
+		}
+		String colorId = (String) features.get("colorId");
+		String dimensionId = (String) features.get("dimensionId");
+		
+		// 6. update skuIds by productId
+		String barcode = (String) getCellContent(cellContents, "Barcode");
+		BigDecimal inventory = (BigDecimal) getCellContent(cellContents, "Stock Qty");
+		BigDecimal averageCost = (BigDecimal) getCellContent(cellContents, "Average Cost");
+		String skuId = updateSku(row, productId, ownerPartyId, facilityId, barcode, inventory, colorId, color, dimensionId, dimension, listPrice, averageCost);
+		if (UtilValidate.isEmpty(skuId)) {
+			return false;
+		}
+		
+		// 7. store prices
+		BigDecimal memberPrice = (BigDecimal) getCellContent(cellContents, "Member Price");
+		Map<String, Object> results = updateSkuPrice(skuId, ownerPartyId, memberPrice);
+		if (ServiceUtil.isError(results)) {
+			return false;
+		}
+		
+		return true;
+	}
+
+
+	public String updateSku(XSSFRow row, String productId, String ownerPartyId, String facilityId, String barcode, BigDecimal inventory,
+			String colorId, String color, String dimensionId, String dimension, BigDecimal listPrice, BigDecimal averageCost) {
+		return "sampleSkuId";
+	}
+
+	public String getProductId(XSSFRow row, String brandId, String modelName, String productName, String productCategoryId, String ownerPartyId, BigDecimal listPrice) {
+		return "sampleProductId";
+	}
+
+	public Object getCellContent(List<Object> cellContents, String colName) {
+		if (UtilValidate.isNotEmpty(headerColNames) && headerColNames.contains(colName)) {
+			return cellContents.get(headerColNames.indexOf(colName));
+		}
+		return null;
+	}
+
+	public String getProductCategoryId(List<Object> cellContents, String ownerPartyId) {
+		return "sampleProductCategoryId";
+	}
+
+	public boolean isFacilityOk(XSSFRow row, String facilityName, String facilityId) {
+		if (!facilities.containsKey(facilityId)) {
+			if (UtilValidate.isEmpty(facilityId) && facilities.keySet().size() == 1) {
+				if (UtilValidate.isEmpty(facilityName)) {
+					return true;
+				} else {
+					String theFacilityId = (String) facilities.keySet().toArray()[0];
+					String name = facilities.get(theFacilityId)[0];
+					if (!name.equals(facilityName)) {
+						String errorMessage = UtilProperties.getMessage(resource, "FacilityNameNotMatchId", new Object[]{theFacilityId, name, facilityName}, locale);
+						report.println();
+						report.print(errorMessage, InterfaceReport.FORMAT_ERROR);
+						XSSFCell cell = row.getCell(0);
+						errorMessages.put(new CellReference(cell), errorMessage);
+						return false;
+					}
+				}
+			} else {
+				String errorMessage = UtilProperties.getMessage(resource, "FacilityNotBelongToYou", new Object[]{facilityName, facilityId}, locale);
+				report.println();
+				report.print(errorMessage, InterfaceReport.FORMAT_ERROR);
+				XSSFCell cell = row.getCell(1);
+				errorMessages.put(new CellReference(cell), errorMessage);
+				return false;
+			}
+		} else {
+			String name = facilities.get(facilityId)[0];
+			if (!name.equals(facilityName)) {
+				String errorMessage = UtilProperties.getMessage(resource, "FacilityNameNotMatchId", new Object[]{facilityId, name, facilityName}, locale);
+				report.println();
+				report.print(errorMessage, InterfaceReport.FORMAT_ERROR);
+				XSSFCell cell = row.getCell(0);
+				errorMessages.put(new CellReference(cell), errorMessage);
+				return false;
+			}
+		}
+		return true;
+	}
+
+	public boolean isTableHeaderMatched(XSSFSheet sheet) {
+		List<Object[]> columnNames = ColNamesList.get(pricatFileVersion);
+		short cols = sheet.getRow(headerRowNo).getLastCellNum();
+		report.print(UtilProperties.getMessage(resource, "StartCheckHeaderColNum", new Object[] {pricatFileVersion}, locale), InterfaceReport.FORMAT_NOTE);
+		if (cols != columnNames.size()) {
+			report.print(UtilProperties.getMessage(resource, "HeaderColNumNotMatch", new Object[] {String.valueOf(cols), String.valueOf(columnNames.size())}, locale), InterfaceReport.FORMAT_WARNING);
+			if (cols < columnNames.size()) {
+				report.println(UtilProperties.getMessage(resource, "HeaderColNumShortThanRequired", new Object[] {String.valueOf(columnNames.size())}, locale), InterfaceReport.FORMAT_ERROR);
+				return false;
+			} else {
+				report.println(UtilProperties.getMessage(resource, "UseHeaderColNum", new Object[] {String.valueOf(columnNames.size())}, locale), InterfaceReport.FORMAT_WARNING);
+				cols = (short) columnNames.size();
+			}
+		} else {
+			report.println(UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+		}
+		
+		report.print(UtilProperties.getMessage(resource, "StartCheckHeaderColLabel", new Object[] {pricatFileVersion}, locale), InterfaceReport.FORMAT_NOTE);
+		boolean foundLabelNotMatch = false;
+		for (int i = 0; i < cols; i++) {
+			String coltext = sheet.getRow(headerRowNo).getCell(i).getStringCellValue().trim();
+			headerColNames.add(coltext);
+			Object[] versionColumn = columnNames.get(i);
+			if (!coltext.equals(versionColumn[0])) {
+				report.println(UtilProperties.getMessage(resource, "HeaderColLabelNotMatch", new Object[] {String.valueOf(headerRowNo + 1), String.valueOf(i + 1), coltext, versionColumn[0]}, locale), InterfaceReport.FORMAT_ERROR);
+				foundLabelNotMatch = true;
+			} else {
+				report.print(" " + coltext, InterfaceReport.FORMAT_NOTE);
+				if (i < cols - 1) {
+					report.print(",", InterfaceReport.FORMAT_NOTE);
+				}
+			}
+		}
+		if (foundLabelNotMatch) {
+			report.println();
+			return false;
+		}
+		report.println(" ... " + UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+		return true;
+	}
+
+	public boolean isVersionSupported(XSSFSheet sheet) {
+		report.print(UtilProperties.getMessage(resource, "StartCheckPricatVersion", locale), InterfaceReport.FORMAT_NOTE);
+		pricatFileVersion = sheet.getRow(2).getCell(0).getStringCellValue().trim();
+		if (ColNamesList.containsKey(pricatFileVersion)) {
+			report.print(" " + pricatFileVersion + " ... ", InterfaceReport.FORMAT_NOTE);
+			report.println(UtilProperties.getMessage(resource, "ok", locale), InterfaceReport.FORMAT_OK);
+		} else {
+			report.println(UtilProperties.getMessage(resource, "error", locale), InterfaceReport.FORMAT_ERROR);
+			report.println(UtilProperties.getMessage(resource, "PricatVersionNotSupport", new Object[] {pricatFileVersion}, locale), InterfaceReport.FORMAT_ERROR);
+			return false;
+		}
+		return true;
+	}
+
+	public boolean containsDataRows(XSSFSheet sheet) {
+		int rows = sheet.getPhysicalNumberOfRows();
+		if (rows > headerRowNo + 1) {
+			report.println(UtilProperties.getMessage(resource, "PricatTableRows", new Object[] {String.valueOf(headerRowNo + 1), String.valueOf(rows - headerRowNo - 1), sheet.getSheetName()}, locale), InterfaceReport.FORMAT_NOTE);
+		} else {
+			report.println(UtilProperties.getMessage(resource, "PricatNoDataRows", new Object[] {sheet.getSheetName()}, locale), InterfaceReport.FORMAT_ERROR);
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * The Object[] have 4 elements, they are:
+	 * 1. Header Label Name.
+	 * 2. Cell data type to return.
+	 * 3. Boolean value to indicate whether the column is required.
+	 * 4. Boolean value to indicate whether the column is a price when cell data type is BigDecimal, this element is optional.
+	 * 
+	 * @param version
+	 * @return List of Object[]
+	 */
+	private static List<Object[]> genExcelHeaderNames(String version){
+		switch (version) {
+			case "V1.1":
+			default:
+				return genExcelHeaderNamesV1_1();
+		}
+	}
+
+	/**
+	 * Get V1.1 pricat excel header names and attributes. 
+	 * 
+	 * @return list of Object[]
+	 */
+	private static List<Object[]> genExcelHeaderNamesV1_1() {
+		List<Object[]> listHeaderName = new ArrayList<Object[]>();
+		listHeaderName.add(new Object[] {"Facility Name", 
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"FacilityId",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Category L1",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Category L2",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Category L3",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Category L4",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Brand",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Style No",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Product Name",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Color",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Size",
+                                         XSSFCell.CELL_TYPE_STRING,
+				                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Barcode",
+                                         XSSFCell.CELL_TYPE_STRING,
+                                         Boolean.FALSE});
+		listHeaderName.add(new Object[] {"Stock Qty",
+                                         XSSFCell.CELL_TYPE_NUMERIC,
+                                         Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Average Cost",
+                                         XSSFCell.CELL_TYPE_NUMERIC,
+                                         Boolean.TRUE,
+					                     Boolean.TRUE});
+		listHeaderName.add(new Object[] {"List Price",
+										 XSSFCell.CELL_TYPE_NUMERIC,
+										 Boolean.TRUE,
+										 Boolean.TRUE});
+		listHeaderName.add(new Object[] {"Member Price",
+                                       	 XSSFCell.CELL_TYPE_NUMERIC,
+                                       	 Boolean.FALSE,
+                                       	 Boolean.TRUE});
+		return listHeaderName;
+	}
+
+	@Override
+	public void parsePricatExcel() {
+		parsePricatExcel(true);
+	}
+
+	/**
+	 * Get data by version definition.
+	 * 
+	 * @param row
+	 * @param colNames 
+	 * @param size 
+	 * @return
+	 */
+	public List<Object> getCellContents(XSSFRow row, List<Object[]> colNames, int size) {
+		List<Object> results = new ArrayList<Object>();
+		boolean foundError = false;
+		if (isEmptyRow(row, size, true)) {
+			return null;
+		}
+		
+		// check and get data
+		for (int i = 0; i < size; i++) {
+			XSSFCell cell = null;
+			if (row.getPhysicalNumberOfCells() > i) {
+				cell = row.getCell(i);
+			}
+			if (cell == null) {
+				if (((Boolean) colNames.get(i)[2]).booleanValue() && (facilities.keySet().size() > 1 || (facilities.keySet().size() == 1 && i >= 2))) {
+					report.print(UtilProperties.getMessage(resource, "ErrorColCannotEmpty", new Object[] {colNames.get(i)[0]}, locale), InterfaceReport.FORMAT_WARNING);
+					cell = row.createCell(i);
+					errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorColCannotEmpty", new Object[] {colNames.get(i)[0]}, locale));
+					foundError = true;
+					results.add(null);
+					continue;
+				} else {
+					cell = row.createCell(i);
+				}
+			}
+			int cellType = cell.getCellType();
+			String cellValue = formatter.formatCellValue(cell);
+			if (UtilValidate.isNotEmpty(cellValue) && UtilValidate.isNotEmpty(cellValue.trim())) {
+				if (cellType == XSSFCell.CELL_TYPE_FORMULA) {
+					try {
+						cellValue = BigDecimal.valueOf(cell.getNumericCellValue()).setScale(FinAccountHelper.decimals, FinAccountHelper.rounding).toString();
+					} catch (IllegalStateException e) {
+						try {
+							cellValue = cell.getStringCellValue();
+						} catch (IllegalStateException e1) {
+							// do nothing
+						}
+					}
+					report.print(((i == 0)?"":", ") + cellValue, InterfaceReport.FORMAT_NOTE);
+				} else {
+					report.print(((i == 0)?"":", ") + cellValue, InterfaceReport.FORMAT_NOTE);
+				}
+			} else {
+				report.print(((i == 0)?"":","), InterfaceReport.FORMAT_NOTE);
+			}
+			if (((Boolean) colNames.get(i)[2]).booleanValue() && UtilValidate.isEmpty(cellValue) && (facilities.keySet().size() > 1 || (facilities.keySet().size() == 1 && i >= 2))) {
+				report.print(UtilProperties.getMessage(resource, "ErrorColCannotEmpty", new Object[] {colNames.get(i)[0]}, locale), InterfaceReport.FORMAT_WARNING);
+				errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorColCannotEmpty", new Object[] {colNames.get(i)[0]}, locale));
+				foundError = true;
+				results.add(null);
+				continue;
+			}
+			if (((Boolean) colNames.get(i)[2]).booleanValue() && cellType != (int) colNames.get(i)[1]) {
+				// String warningMessage = "";
+				if ((int) colNames.get(i)[1] == XSSFCell.CELL_TYPE_STRING) {
+					if (UtilValidate.isNotEmpty(cellValue) && UtilValidate.isNotEmpty(cellValue.trim())) {
+						results.add(cellValue);
+					} else {
+						results.add(null);
+					}
+				} else if ((int) colNames.get(i)[1] == XSSFCell.CELL_TYPE_NUMERIC) {
+					if (cell.getCellType() != XSSFCell.CELL_TYPE_STRING) {
+						cell.setCellType(XSSFCell.CELL_TYPE_STRING);
+					}
+					try {
+						results.add(BigDecimal.valueOf(Double.parseDouble(cell.getStringCellValue())).setScale(FinAccountHelper.decimals, FinAccountHelper.rounding));
+					} catch (NumberFormatException e) {
+						results.add(null);
+						errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorParseValueToNumeric", locale));
+					}
+				}
+			} else {
+				if (UtilValidate.isEmpty(cellValue) || UtilValidate.isEmpty(cellValue.trim())) {
+					results.add(null);
+					continue;
+				}
+				if ((int) colNames.get(i)[1] == XSSFCell.CELL_TYPE_STRING) {
+					if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
+						cellValue = cell.getStringCellValue().trim();
+						results.add(cellValue);
+					} else {
+						results.add(cellValue.trim());
+					}
+				} else if ((int) colNames.get(i)[1] == XSSFCell.CELL_TYPE_NUMERIC) {
+					if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
+						try {
+							results.add(BigDecimal.valueOf(Double.valueOf(cell.getStringCellValue())));
+						} catch (NumberFormatException e) {
+							results.add(null);
+							errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorParseValueToNumeric", locale));
+						}
+					} else if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) {
+						try {
+							results.add(BigDecimal.valueOf(cell.getNumericCellValue()).setScale(FinAccountHelper.decimals, FinAccountHelper.rounding));
+						} catch (NumberFormatException e) {
+							results.add(null);
+							errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorParseValueToNumeric", locale));
+						}
+					} else {
+						try {
+							results.add(BigDecimal.valueOf(Double.valueOf(cellValue)).setScale(FinAccountHelper.decimals, FinAccountHelper.rounding));
+						} catch (NumberFormatException e) {
+							results.add(null);
+							errorMessages.put(new CellReference(cell), UtilProperties.getMessage(resource, "ErrorParseValueToNumeric", locale));
+						}
+					}
+				}
+			}
+		}
+		if (foundError) {
+			return null;
+		}
+		return results;
+	}
+
+	protected int getHeaderRowNo() {
+		return headerRowNo;
+	}
+}

Added: ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/poi/xssf/usermodel/OFBizPricatUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/poi/xssf/usermodel/OFBizPricatUtil.java?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/poi/xssf/usermodel/OFBizPricatUtil.java (added)
+++ ofbiz/trunk/specialpurpose/pricat/src/main/java/org/apache/poi/xssf/usermodel/OFBizPricatUtil.java Mon Nov 21 08:07:57 2016
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.poi.xssf.usermodel;
+
+import org.apache.poi.ss.util.CellReference;
+import com.microsoft.schemas.vml.CTShape;
+
+public final class OFBizPricatUtil {
+	public static void formatCommentShape(XSSFSheet sheet, CellReference cell) {
+        XSSFVMLDrawing vml = sheet.getVMLDrawing(true);
+        CTShape ctshape = vml.findCommentShape(cell.getRow(), cell.getCol());
+        ctshape.setType("#_x0000_t202");
+	}
+
+	public static void formatCommentShape(XSSFSheet sheet, int rowNum, short colNum) {
+        XSSFVMLDrawing vml = sheet.getVMLDrawing(true);
+        CTShape ctshape = vml.findCommentShape(rowNum, colNum);
+        ctshape.setType("#_x0000_t202");
+	}
+}

Added: ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/controller.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/controller.xml?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/controller.xml (added)
+++ ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/controller.xml Mon Nov 21 08:07:57 2016
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="http://ofbiz.apache.org/Site-Conf" xsi:schemaLocation="http://ofbiz.apache.org/Site-Conf http://ofbiz.apache.org/dtds/site-conf.xsd">
+    <include location="component://common/webcommon/WEB-INF/common-controller.xml"/>
+    <include location="component://common/webcommon/WEB-INF/portal-controller.xml"/>
+
+    <description>PriCat</description>
+
+    <!-- define -->
+    <request-map uri="main">
+        <security https="true" auth="true"/>
+        <response name="success" type="view" value="main"/>
+    </request-map>
+
+    <request-map uri="downloadCommentedExcel">
+        <security https="true" auth="true"/>
+        <event type="java" path="org.apache.ofbiz.pricat.PricatEvents" invoke="downloadCommentedExcel"/>
+        <response name="success" type="none" />
+        <response name="error" type="none" />
+    </request-map>
+
+    <request-map uri="viewExcelImportLog">
+        <security https="true" auth="true"/>
+        <response name="success" type="view" value="viewExcelImportLog"/>
+    </request-map>
+    
+	<view-map name="main" type="screen" page="component://pricat/widget/CommonScreens.xml#viewExcelImportHistory"/>
+	<view-map name="viewExcelImportLog" type="screen" page="component://pricat/widget/CommonScreens.xml#viewExcelImportLog"/>
+</site-conf>

Added: ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/web.xml?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/web.xml (added)
+++ ofbiz/trunk/specialpurpose/pricat/webapp/pricat/WEB-INF/web.xml Mon Nov 21 08:07:57 2016
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<web-app version="3.0">
+
+    <display-name>Apache OFBiz - PriCat</display-name>
+    <description>PriCat Application of the Apache OFBiz Project</description>
+
+    <context-param>
+        <description>A unique name used to identify/recognize the local dispatcher for the Service Engine</description>
+        <param-name>localDispatcherName</param-name><param-value>pricat</param-value>
+    </context-param>
+    <context-param>
+        <description>The Name of the Entity Delegator to use, defined in entityengine.xml</description>
+        <param-name>entityDelegatorName</param-name><param-value>default</param-value>
+    </context-param>
+    <context-param>
+        <description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
+        <param-name>mainDecoratorLocation</param-name>
+        <param-value>component://pricat/widget/SampleScreens.xml</param-value>
+    </context-param>
+    <context-param>
+        <description>Remove unnecessary whitespace from HTML output.</description>
+        <param-name>compressHTML</param-name>
+        <param-value>false</param-value>
+    </context-param>
+
+    <filter>
+        <display-name>ControlFilter</display-name>
+        <filter-name>ControlFilter</filter-name>
+        <filter-class>org.apache.ofbiz.webapp.control.ControlFilter</filter-class>
+        <init-param>
+            <param-name>allowedPaths</param-name>
+            <param-value>/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp:/images:/js:/ws:/includes</param-value>
+        </init-param>
+        <init-param>
+            <param-name>redirectPath</param-name>
+            <param-value>/control/main</param-value>
+        </init-param>
+    </filter>
+    <filter>
+        <display-name>ContextFilter</display-name>
+        <filter-name>ContextFilter</filter-name>
+        <filter-class>org.apache.ofbiz.webapp.control.ContextFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>ControlFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+    <filter-mapping>
+        <filter-name>ContextFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <listener><listener-class>org.apache.ofbiz.webapp.control.ControlEventListener</listener-class></listener>
+    <listener><listener-class>org.apache.ofbiz.webapp.control.LoginEventListener</listener-class></listener>
+    <!-- NOTE: not all app servers support mounting implementations of the HttpSessionActivationListener interface -->
+    <!-- <listener><listener-class>org.apache.ofbiz.webapp.control.ControlActivationEventListener</listener-class></listener> -->
+
+    <servlet>
+        <description>Main Control Servlet</description>
+        <display-name>ControlServlet</display-name>
+        <servlet-name>ControlServlet</servlet-name>
+        <servlet-class>org.apache.ofbiz.webapp.control.ControlServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping><servlet-name>ControlServlet</servlet-name><url-pattern>/control/*</url-pattern></servlet-mapping>
+
+    <session-config>
+        <session-timeout>60</session-timeout><!-- in minutes -->
+        <cookie-config>
+          <http-only>true</http-only>
+          <secure>true</secure>
+        </cookie-config>
+        <tracking-mode>COOKIE</tracking-mode>
+    </session-config>
+
+    <welcome-file-list>
+        <welcome-file>index.jsp</welcome-file>
+        <welcome-file>index.html</welcome-file>
+        <welcome-file>index.htm</welcome-file>
+    </welcome-file-list>
+</web-app>

Added: ofbiz/trunk/specialpurpose/pricat/webapp/pricat/error/error.jsp
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/webapp/pricat/error/error.jsp?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/webapp/pricat/error/error.jsp (added)
+++ ofbiz/trunk/specialpurpose/pricat/webapp/pricat/error/error.jsp Mon Nov 21 08:07:57 2016
@@ -0,0 +1,52 @@
+<%--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+--%>
+<%@ page import="org.apache.ofbiz.base.util.*" %>
+<html>
+<head>
+<title>OFBiz Message</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<% String errorMsg = (String) request.getAttribute("_ERROR_MESSAGE_"); %>
+
+<body bgcolor="#FFFFFF">
+<div align="center">
+  <br/>
+  <table width="100%" border="1" height="200">
+    <tr>
+      <td>
+        <table width="100%" border="0" height="200">
+          <tr bgcolor="#CC6666">
+            <td height="45">
+              <div align="center"><font face="Verdana, Arial, Helvetica, sans-serif" size="4" color="#FFFFFF"><b>:ERROR MESSAGE:</b></font></div>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <div align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><%=UtilFormatOut.replaceString(errorMsg, "\n", "<br/>")%></font></div>
+            </td>
+          </tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+</div>
+<div align="center"></div>
+</body>
+</html>

Added: ofbiz/trunk/specialpurpose/pricat/webapp/pricat/ftl/ExcelImportHistoryReport.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/pricat/webapp/pricat/ftl/ExcelImportHistoryReport.ftl?rev=1770621&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/pricat/webapp/pricat/ftl/ExcelImportHistoryReport.ftl (added)
+++ ofbiz/trunk/specialpurpose/pricat/webapp/pricat/ftl/ExcelImportHistoryReport.ftl Mon Nov 21 08:07:57 2016
@@ -0,0 +1,109 @@
+<#--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<div class="row">	
+	<div class="col-sm-12 m-b-xs text-left p-xs">
+		<label >${uiLabelMap.ExcelImportHistoryList}</label>
+		<span class="tooltip pad-left30 top">${uiLabelMap.OnlyYourOwnImportHistoryDisplayed}</span>
+	</div> 
+</div>
+
+<div id="loadBody" style="display:none">
+</div>
+<table id="productlist" name="productlist" class="table table-striped ms-table-primary">
+	<thead>
+		<th></th>
+		<th>${uiLabelMap.SerialNumber}</th>
+		<th>${uiLabelMap.Filename}</th>
+		<th>${uiLabelMap.FromDate}</th>
+		<th>${uiLabelMap.ThruDate}</th>
+		<th>${uiLabelMap.ImportStatus}</th>
+		<th>${uiLabelMap.ThruReasonId}</th>
+		<th>${uiLabelMap.Actions}</th>
+	</thead>
+	<tbody>
+		<#if (data?has_content)>
+			<#list data as historyEntry>
+				<tr name="${historyEntry.sequenceNum!}">
+					<td>
+					<#if historyEntry.statusId?exists && historyEntry.statusId == 'EXCEL_IMPORTING'>
+						<img src="/erptheme/images/blue_anime.gif" alt="${uiLabelMap.EXCE_IMPORTING}" tooltip="${uiLabelMap.EXCE_IMPORTING}">
+					<#elseif historyEntry.statusId?exists && historyEntry.statusId == 'EXCEL_IMPORTED' && historyEntry.thruReasonId?exists && historyEntry.thruReasonId?has_content>
+						<#if historyEntry.thruReasonId == 'EXCEL_IMPORT_SUCCESS'>
+						<button id="excel-import-status" data-tooltip="${uiLabelMap.get(historyEntry.thruReasonId)}"><i class="icon-ok-sign success-color">${uiLabelMap.ReasonOK}</i></button>
+						<#elseif historyEntry.thruReasonId == 'EXCEL_IMPORT_STOPPED'>
+						<button id="excel-import-status" data-tooltip="${uiLabelMap.get(historyEntry.thruReasonId)}"><i class="icon-remove-sign stopped-color">${uiLabelMap.ReasonStopped}</i></button>
+						<#elseif historyEntry.thruReasonId == 'EXCEL_IMPORT_ERROR'>
+						<button id="excel-import-status" data-tooltip="${uiLabelMap.get(historyEntry.thruReasonId)}"><i class="icon-exclamation-sign error-color"></i>${uiLabelMap.ReasonError}</button>
+						<#elseif historyEntry.thruReasonId == 'EXCEL_IMPORT_QUEST'>
+						<button id="excel-import-status" data-tooltip="${uiLabelMap.get(historyEntry.thruReasonId)}"><i class="icon-question-sign question-color"></i>${uiLabelMap.ReasonWarning}</button>
+						</#if>
+					</#if>
+					</td>
+					<td>${historyEntry.sequenceNum!}</td>
+  					<td style="text-align:right;">${historyEntry.fileName!}</td>
+					<td><#if historyEntry.fromDate?exists && historyEntry.fromDate?has_content>${historyEntry.fromDate?string("yyyy-MM-dd HH:mm:ss")}</#if></td>
+					<td><#if historyEntry.thruDate?exists && historyEntry.thruDate?has_content>${historyEntry.thruDate?string("yyyy-MM-dd HH:mm:ss")}</#if></td>
+					<td><#if historyEntry.statusId?exists && historyEntry.statusId?has_content>${uiLabelMap.get(historyEntry.statusId)}</#if></td>
+					<td><#if historyEntry.statusId?exists && historyEntry.statusId == "EXCEL_IMPORTED" && historyEntry.thruReasonId?exists && historyEntry.thruReasonId?has_content>${uiLabelMap.get(historyEntry.thruReasonId)}</#if></td>
+					<td>
+						<#assign buttons = 0 />
+						<#if historyEntry.logFileName?exists && historyEntry.logFileName?has_content>
+							<#if Static["org.apache.ofbiz.base.util.FileUtil"].getFile(historyEntry.logFileName).exists()>
+                    		<button id="excel-import-log" type="button" onclick="viewExcelImportLog(${historyEntry.sequenceNum});" data-tooltip="${uiLabelMap.ViewExcelImportLogContent}">
+					    		<i class="icon-comments icon-blue">${uiLabelMap.ViewPricatLog}</i>
+				    		</button>
+				    		<#assign buttons = buttons + 1 />
+					        </#if>
+                        </#if>
+						<#if buttons == 0>
+                    		<#--- <button id="excel-import-empty" type="button"></button> -->
+				    		<#assign buttons = buttons + 1 />
+						</#if>
+						<#if historyEntry.logFileName?exists && historyEntry.logFileName?has_content>
+							<#if Static["org.apache.ofbiz.pricat.AbstractPricatParser"].isCommentedExcelExists(request, historyEntry.sequenceNum)>
+                    		<button id="excel-import-download" type="button" onclick="downloadCommentedExcel(${historyEntry.sequenceNum});" data-tooltip="${uiLabelMap.DownloadCommentedExcel}">
+					    		<i class="icon-download icon-blue">${uiLabelMap.DownloadCommentedPricat}</i>
+				    		</button>
+				    		<#assign buttons = buttons + 1 />
+					        </#if>
+                        </#if>
+						<#if buttons == 1>
+                    		<#--- <button id="excel-import-empty" type="button"></button> -->
+						</#if>
+                    </td>
+				</tr>
+			</#list>
+		<#else>
+			<tr>
+				<td colspan="10" style="text-align:center;vertical-align:middle;height:60px;">
+					${uiLabelMap.ExcelImportTipNoData}
+				</td>
+			</tr>
+		</#if>
+	</tbody>
+</table>
+<script type="text/javascript" language="JavaScript">
+	function viewExcelImportLog(sequenceNum) {
+        document.location = "<@o...@ofbizUrl>?sequenceNum=" + sequenceNum;
+    }
+
+	function downloadCommentedExcel(sequenceNum) {
+        document.location = "<@o...@ofbizUrl>?sequenceNum=" + sequenceNum;
+    }
+</script>
\ No newline at end of file