ProductListService.java

package sk.iway.iwcm.components.basket.rest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.json.JSONObject;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;

import sk.iway.iwcm.Constants;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.common.CloudToolsForCore;
import sk.iway.iwcm.doc.DocDetails;
import sk.iway.iwcm.doc.DocDetailsRepository;
import sk.iway.iwcm.doc.GroupDetails;
import sk.iway.iwcm.doc.GroupsDB;
import sk.iway.iwcm.editor.facade.EditorFacade;
import sk.iway.iwcm.editor.rest.GetAllItemsDocOptions;
import sk.iway.iwcm.editor.service.WebpagesService;
import sk.iway.iwcm.editor.service.GroupsService;
import sk.iway.iwcm.system.datatable.DatatablePageImpl;
import sk.iway.iwcm.system.datatable.json.LabelValueInteger;
import sk.iway.iwcm.system.datatable.json.LabelValue;

public class ProductListService {

    private ProductListService() { /*private constructor to hide the implicit public one*/ }

    public enum AddingStatus {
        SUCCESS, ALREADY_EXIST, ERROR
    }

    private static Specification<DocDetails> hasGroupIdIn(List<Integer> groupIds) {
        return (Root<DocDetails> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
            return root.get("groupId").in(groupIds);
        };
    }

    private static Specification<DocDetails> fieldStartsWithDigit(String fieldName) {
        return (Root<DocDetails> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
            Path<String> fieldPath = root.get(fieldName);
            // Create predicates for each digit using a loop
            List<Predicate> digitPredicates = new ArrayList<>();
            for (int i = 0; i <= 9; i++) {
                digitPredicates.add(criteriaBuilder.like(fieldPath, i + "%"));
            }

            // Combine all predicates with OR
            Predicate startsWithDigit = criteriaBuilder.or(digitPredicates.toArray(new Predicate[0]));
            return startsWithDigit;
        };
    }

    public static DatatablePageImpl<DocDetails> getAllItems(GetAllItemsDocOptions options) {
        List<Integer> groupIds = getGroupTreeIds( options.getGroupId(), options.getDocDetailsRepository() );
        //So no error will be returned about wrong sql query
        if(groupIds.isEmpty()) return new DatatablePageImpl<>(new ArrayList<>());

        String priceField = Constants.getString("basketPriceField");

        Specification<DocDetails> spec = Specification.where(hasGroupIdIn(groupIds))
                                                      .and(fieldStartsWithDigit(priceField));
        Page<DocDetails> page = options.getDocDetailsRepository().findAll(spec, options.getPageable());

        String currentCurrency =  Constants.getString("basketProductCurrency");

        String wantedCurrency;
        //Safety check
        if(isCurrencySupported(options.getRequest().getParameter("currency")) == false) wantedCurrency = currentCurrency;
        else wantedCurrency = options.getRequest().getParameter("currency");

        DatatablePageImpl<DocDetails> pageImpl = WebpagesService.preparePage(page, options);
        pageImpl.get().forEach(doc -> {
            //Set currency
            doc.setFieldI(currentCurrency);

            if(currentCurrency.equals(wantedCurrency)) {
                doc.setFieldH( doc.getPriceVat().toString() );
                doc.setFieldK( doc.getPrice().toString() );
            } else {
                doc.setFieldH( doc.getLocalPriceVat(options.getRequest(), wantedCurrency).toString() );
                doc.setFieldK( doc.getLocalPrice(options.getRequest(), wantedCurrency).toString() );
            }
        });

        return pageImpl;
    }

    public static List<Integer> getGroupTreeIds(int rootGroupId, DocDetailsRepository repo) {
        if(rootGroupId < 1) {
            //Get default option
            List<LabelValueInteger> allOptions = getListOfProductsGroups(repo);
            if(allOptions.isEmpty()) return new ArrayList<>();

            rootGroupId = allOptions.get(0).getValue();
        }

        List<Integer> groupIds = new ArrayList<>();
        List<GroupDetails> groupsTree = GroupsDB.getInstance().getGroupsTree(rootGroupId, true, true);
        for(GroupDetails group : groupsTree) { groupIds.add(group.getGroupId()); }
        return groupIds;
    }

    public static AddingStatus addProductGroup(Identity currentUser, String customData, EditorFacade editorFacade) {
        if(!currentUser.isEnabledItem("cmp_basket")) return AddingStatus.ERROR;

        int groupId = -1;
        String newGroupName = "";
        if(customData != null && !customData.isEmpty()) {
            try {
                JSONObject jsonObject = new JSONObject(customData);
                groupId = Tools.getIntValue((String) jsonObject.get("groupId"), -1);
                newGroupName = (String) jsonObject.get("newGroupName");
            } catch (Exception ex) {
                sk.iway.iwcm.Logger.error(ex);
                return AddingStatus.ERROR;
            }
        }

        if(Tools.isEmpty(newGroupName) || groupId < 1) return AddingStatus.ERROR;

        //Check if this product category (for this parent) already exist - case insensitive
        GroupsDB groupsDB = GroupsDB.getInstance();
        List<GroupDetails> childGroups = groupsDB.getGroups(groupId);
        for(GroupDetails childGroup : childGroups)
            if(childGroup.getGroupName().equalsIgnoreCase(newGroupName)) return AddingStatus.ALREADY_EXIST;

        //All is ok, create group
        GroupDetails newGroup = createGroup(newGroupName, currentUser.getLogin(), GroupsDB.getInstance().getGroup(groupId));
        if (newGroup != null) {
            createGroupNewsDoc(editorFacade, newGroup.getGroupId(), newGroup.getGroupName(), currentUser.getUserId());
            return AddingStatus.SUCCESS;
        }
        return AddingStatus.ERROR;
    }

    private static GroupDetails createGroup(String groupName, String userLogin, GroupDetails parentGroup) {
        //Check if group already exist
        GroupDetails newGroup = GroupsDB.getInstance().getGroup(userLogin, parentGroup.getGroupId());
        if(newGroup != null) return null;

        //Create new root group
        newGroup = new GroupDetails();
        newGroup.setParentGroupId( parentGroup.getGroupId() );
        newGroup.setGroupName( groupName );
        newGroup.setTempId( parentGroup.getTempId() );
        newGroup.setDomainName( parentGroup.getDomainName() );
        GroupsDB.getInstance().setGroup(newGroup);
        return newGroup;
    }

    private static void createGroupNewsDoc(EditorFacade editorFacade, int rootGroupId, String docTitle, int authorId) {
        DocDetails groupDoc = editorFacade.getDocForEditor(-1, -1, rootGroupId);

        //Set doc
        groupDoc.setTitle(docTitle);
        groupDoc.setNavbar(docTitle);
        groupDoc.setData("");
		groupDoc.setAuthorId(authorId);
		groupDoc.setAvailable(true);
		groupDoc.setSearchable(true);
		groupDoc.setCacheable(false);
        groupDoc.setPerexGroup(new String[]{"5"}); //5 - kategoria
        groupDoc.setData(
            "<section>\r\n" + //
            "!INCLUDE(/components/eshop/shop/modules/md-category-header.jsp)!\r\n" + //
            "\r\n" + //
            "!INCLUDE(/components/eshop/shop/modules/md-subcategory-selector.jsp)!\r\n" + //
            "</section>\r\n" + //
            "\r\n" + //
            "!INCLUDE(/components/eshop/shop/modules/md-product-list.jsp)!"
        );

        //Save doc
        editorFacade.save(groupDoc);
    }

    public static List<LabelValueInteger> getListOfProductsGroups(DocDetailsRepository docDetailsRepository) {
        List<LabelValueInteger> groupsList = new ArrayList<>();
        List<GroupDetails> correctGroups = new ArrayList<>();

        List<Integer> groupIds = docDetailsRepository.getDistGroupIdsByDataLike("%!INCLUDE(/components/eshop/%", "%!INCLUDE(/components/basket/%", "%product-list.jsp%", "%products.jsp%");
        if(groupIds == null || groupIds.isEmpty()) return groupsList;

        GroupsDB groupsDB = GroupsDB.getInstance();
        String domainName = CloudToolsForCore.getDomainName();
        for(Integer groupId : groupIds) {
            GroupDetails group = groupsDB.getGroup(groupId);
            if (group == null) continue;
            if (group.getFullPath().startsWith("/System")) continue;
            //Only current domain and not deleted groups
            if(Tools.isNotEmpty(group.getDomainName()) && domainName.equals(group.getDomainName())==false) continue;

            correctGroups.add(group);
        }

        if(correctGroups.isEmpty()) return groupsList;

        //Sort it, then push it into map
        for(GroupDetails group : GroupsService.sortItIntoTree(correctGroups)) groupsList.add( new LabelValueInteger(group.getFullPath(), group.getGroupId()) );

        return groupsList;
    }

    public static boolean isCurrencySupported(String currency) {
        if(Tools.isEmpty(currency) == true) return false;
        List<String> supportedCurrencies = Arrays.asList( Constants.getString("supportedCurrencies").split(",") );
        return supportedCurrencies.contains(currency);
    }

    public static List<LabelValue> getListOfSupportedCurrencies() {
        List<String> supportedCurrencies = Arrays.asList( Constants.getString("supportedCurrencies").split(",") );
        Collections.sort(supportedCurrencies);

        List<LabelValue> groupsList = new ArrayList<>();
        for (String curr: supportedCurrencies)
            groupsList.add( new LabelValue(curr, curr) );

        return groupsList;
    }
}