<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="com.google.common.base.Joiner" %> <%@ page import="com.google.common.base.Strings" %> <%@ page import="com.google.common.collect.Lists" %> <%@ page import="org.apache.commons.lang3.StringEscapeUtils" %> <%@ page import="org.jetbrains.annotations.Nullable" %> <%@ page import="resto.RestoException" %> <%@ page import="resto.back.store.AssemblyChart" %> <%@ page import="resto.back.store.AssemblyChartItem" %> <%@ page import="resto.back.store.AssemblyChartTable" %> <%@ page import="resto.back.store.Product" %> <%@ page import="resto.back.store.ProductScale" %> <%@ page import="resto.back.store.ProductSize" %> <%@ page import="resto.back.store.ProductType" %> <%@ page import="resto.back.store.alcohol.AlcoholType" %> <%@ page import="resto.back.store.recompute.ProductSizeAssemblyStrategy" %> <%@ page import="resto.core.RestoServiceLocator" %> <%@ page import="resto.db.EntityManager" %> <%@ page import="resto.db.Guid" %> <%@ page import="resto.licensing.LicenseService" %> <%@ page import="resto.utils.DecimalUtils" %> <%@ page import="resto.utils.log4j.RestoLogger" %> <%@ page import="resto.web.JspHelper" %> <%@ page import="java.io.PrintWriter" %> <%@ page import="java.math.BigDecimal" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.Date" %> <%@ page import="java.util.HashMap" %> <%@ page import="java.util.LinkedHashMap" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ page import="java.util.NavigableMap" %> <%@ taglib prefix="r" uri="http://iiko.ru/taglib/resto-tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% JspHelper.setCharacterEncodingMode(request); %> <%! /** * JSP генерирует файл для настройки фронтового плагина для продажи разливного пива через "Честный знак". *

* Начиная с 8.9 эта логика скопирована в метод сервера, через который фронт получает эти данные без .csv файла. * * @see "RMS-55880 Jsp скрипт для LT клиентов для выгрузки алкогольно-пивных напитков для плагина Честного знака" * @see resto.external.egais.service.AlcoholMarkingPluginService#getNomenclature */ private static final RestoLogger LOG = RestoLogger.getLogger("resto.service.maintance.generateAlcoholMarkingPluginConfig.jsp"); private static final String FIELD_RAW_PRODUCT_ID = "rawProductId"; private static final String FIELD_RAW_PRODUCT_SKU = "rawProductSku"; private static final String FIELD_PRODUCT_TYPE = "productType"; private static final String FIELD_NAME_ON_TAP = "nameOnTap"; private static final String FIELD_SALE_ITEM_ID = "saleItemId"; private static final String FIELD_SALE_ITEM_SIZE_ID = "saleItemSizeId"; private static final String FIELD_LITRES_IN_PORTION = "litresInPortion"; private static final String FIELD_EXPIRATION_DATE = "expirationDate"; private static final String FIELD_COMMENT = "comment"; private static final List FIELDS = Lists.newArrayList( FIELD_RAW_PRODUCT_ID, FIELD_RAW_PRODUCT_SKU, FIELD_PRODUCT_TYPE, FIELD_NAME_ON_TAP, FIELD_SALE_ITEM_ID, FIELD_SALE_ITEM_SIZE_ID, FIELD_LITRES_IN_PORTION, FIELD_EXPIRATION_DATE, FIELD_COMMENT ); %> <% final String paramAction = request.getParameter("action"); if (paramAction != null) { final EntityManager em = RestoServiceLocator.get(EntityManager.class); final List> result = new ArrayList>(); // Кешируем все шкалы-размеры, чтобы не проходиться по вообще всем размерам в каждом айтеме техкарты final Map> sizesByScale = new HashMap>(); for (ProductSize productSize : em.getAll(ProductSize.class)) { ProductScale scale = productSize.getProductScale(); if (sizesByScale.containsKey(scale)) { sizesByScale.get(scale).add(productSize); } else { sizesByScale.put(scale, Lists.newArrayList(productSize)); } } final Date today = new Date(); // Ищем товары в литрах for (Product product : em.getAllNotDeletedCopy(Product.class)) { if (product.getType() == ProductType.GOODS) { if (!isMainUnitLiter(product)) { // и только в ЛИТРАХ continue; } if (product.getAlcoholClass() != null) { Map dataRow = new CSVRecordBuilder() .setRawProductId(product.getId()) .setRawProductSku(product.getNum()) .setProductType(product.getAlcoholClass().getAlcoholClassGroup().getType()) .setNameOnTap(getNameOnTap(product)) .setSaleItemId(product.getId()) .setSaleItemSizeId(null) .setLitresInPortion(BigDecimal.ONE) .setExpirationDate(product.getExpirationDate()) .setComment("Sold as a product") .build(); result.add(dataRow); } } else if (product.mayHaveAssemblyCharts()) { // Ищем блюда с техкартой, в которой используется алкоголь final AssemblyChart ac = AssemblyChartTable.getInstance().getByDate(product, today); if (ac == null || ac.isDeleted()) { continue; } final Product dish = ac.getProduct(); for (AssemblyChartItem item : ac.getItems()) { if (item.getProduct().getAlcoholClass() == null || !isMainUnitLiter(item.getProduct())) { continue; } final AlcoholType itemAlcoholType = item.getProduct().getAlcoholClass().getAlcoholClassGroup().getType(); if (ac.getProductSizeAssemblyStrategy() == ProductSizeAssemblyStrategy.COMMON) { // "Отдельные техкарты для размеров" выключена. Учитываются коэффициенты. if (item.getProductSizeSpecification() != null) { // Итем относится к ранее сохраненной ТК для размеров. continue; } final boolean dishHasSizeFactors = dish.getProductScale() != null && dish.getProductSizeFactors() != null; if (dishHasSizeFactors) { // Пишем строку для каждого размера. Map> factorsMap = dish.getProductSizeFactors().getFactors(); for (ProductSize productSize : sizesByScale.get(dish.getProductScale())) { if (!productSize.getProductScale().equals(dish.getProductScale())) { continue; } final NavigableMap value = factorsMap.get(productSize); final BigDecimal factor = value == null ? BigDecimal.ONE : value.get(BigDecimal.ZERO); Map dataRow = new CSVRecordBuilder() .setRawProductId(item.getProduct().getId()) .setRawProductSku(item.getProduct().getNum()) .setProductType(itemAlcoholType) .setNameOnTap(getNameOnTap(item.getProduct())) .setSaleItemId(dish.getId()) .setSaleItemSizeId(productSize.getId()) .setLitresInPortion( DecimalUtils.MATH_ASSEMBLY_CHART_STRATEGY.multiply(factor, item.getAmountIn())) .setExpirationDate(item.getProduct().getExpirationDate()) .setComment(String.format("Dish: %s", dish.getName())) .build(); result.add(dataRow); } } else { // Одна строка из общей ТК без размеров и коэффициентов. Map dataRow = new CSVRecordBuilder() .setRawProductId(item.getProduct().getId()) .setRawProductSku(item.getProduct().getNum()) .setProductType(itemAlcoholType) .setNameOnTap(getNameOnTap(item.getProduct())) .setSaleItemId(dish.getId()) .setSaleItemSizeId(null) .setLitresInPortion(item.getAmountIn()) .setExpirationDate(item.getProduct().getExpirationDate()) .setComment(String.format("Dish: %s", dish.getName())) .build(); result.add(dataRow); } } else if (ac.getProductSizeAssemblyStrategy() == ProductSizeAssemblyStrategy.SPECIFIC) { // "Отдельные техкарты для размеров" включена. // Коэффициенты не учитываются. Значения берутся из ТК для размера. final ProductSize productSize = item.getProductSizeSpecification(); if (productSize == null) { // Итем относится к ранее сохраненной ТК без размеров. continue; } Map dataRow = new CSVRecordBuilder() .setRawProductId(item.getProduct().getId()) .setRawProductSku(item.getProduct().getNum()) .setProductType(itemAlcoholType) .setNameOnTap(getNameOnTap(item.getProduct())) .setSaleItemId(dish.getId()) .setSaleItemSizeId(productSize.getId()) .setLitresInPortion(item.getAmountIn()) .setExpirationDate(item.getProduct().getExpirationDate()) .setComment(String.format("Dish: %s", dish.getName())) .build(); result.add(dataRow); } else { LOG.warn.format("Skipping item with unsupported ProductSizeAssemblyStrategy %s", ac.getProductSizeAssemblyStrategy()); continue; } } } } response.setContentType("text/csv;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); String crmId = LicenseService.getLicenseCrmId(); String fileName = String.format("plugin_cfg_%s.csv", crmId); response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)); PrintWriter writer = response.getWriter(); writer.println(Joiner.on(",").join(FIELDS)); for (Map row : result) { for (int i = 0; i < FIELDS.size(); i++) { String field = FIELDS.get(i); final Object value = row.get(field); writer.print(value == null ? "" : StringEscapeUtils.escapeCsv(value.toString())); if (i != FIELDS.size() - 1) { writer.print(","); } } writer.println(); } writer.close(); LOG.info(String.format("Export finished for %d items", result.size())); response.setStatus(HttpServletResponse.SC_OK); } %> <%! private static boolean isMainUnitLiter(Product product) { return Guid.parseUUID("69859c74-db72-b006-cba5-326cf6f4fc6e").equals(product.getMainUnit().getId()); } private static String getNameOnTap(Product product) { return Strings.isNullOrEmpty(product.getNameKitchen()) ? product.getName() : product.getNameKitchen(); } private static class CSVRecordBuilder { private Map dataRow; public CSVRecordBuilder() { dataRow = new LinkedHashMap(); } public Map build() { return dataRow; } public CSVRecordBuilder setRawProductId(@Nullable Guid value) { dataRow.put(FIELD_RAW_PRODUCT_ID, value); return this; } public CSVRecordBuilder setRawProductSku(@Nullable String value) { dataRow.put(FIELD_RAW_PRODUCT_SKU, value); return this; } public CSVRecordBuilder setProductType(AlcoholType value) { AlcoholProductType jspAlcoholType; switch (value) { case BEER: jspAlcoholType = AlcoholProductType.BEER; break; case STRONG: jspAlcoholType = AlcoholProductType.LIQUOR; break; default: throw RestoException.format("AlcoholType enum value not implemented: %s", value); } dataRow.put(FIELD_PRODUCT_TYPE, jspAlcoholType); return this; } public CSVRecordBuilder setNameOnTap(@Nullable String value) { dataRow.put(FIELD_NAME_ON_TAP, value); return this; } public CSVRecordBuilder setSaleItemId(@Nullable Guid value) { dataRow.put(FIELD_SALE_ITEM_ID, value); return this; } public CSVRecordBuilder setSaleItemSizeId(@Nullable Guid value) { dataRow.put(FIELD_SALE_ITEM_SIZE_ID, value); return this; } public CSVRecordBuilder setLitresInPortion(@Nullable BigDecimal value) { dataRow.put(FIELD_LITRES_IN_PORTION, value); return this; } public CSVRecordBuilder setComment(@Nullable String value) { dataRow.put(FIELD_COMMENT, value); return this; } public CSVRecordBuilder setExpirationDate(@Nullable Long value) { dataRow.put(FIELD_EXPIRATION_DATE, value); return this; } } /** * Не используется существующий enum AlcoholType, * потому что на стороне web уже решили делать другое название для крепкого алкоголя. */ private enum AlcoholProductType { BEER, LIQUOR, // защита от реформаттера } %> Generate config for Alcohol Marking plugin Generate config for Alcohol Marking plugin