/* * This program is part of the OpenLMIS logistics management information * system platform software. * * Copyright © 2015 ThoughtWorks, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. This program is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. You should * have received a copy of the GNU Affero General Public License along with * this program. If not, see http://www.gnu.org/licenses. For additional * information contact info@OpenLMIS.org */ package org.openlmis.core.manager; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.support.annotation.NonNull; import android.util.DisplayMetrics; import android.util.Log; import com.google.inject.Inject; import com.google.inject.Singleton; import org.openlmis.core.LMISApp; import org.openlmis.core.R; import org.openlmis.core.exceptions.MovementReasonNotFoundException; import org.roboguice.shaded.goole.common.base.Optional; import org.roboguice.shaded.goole.common.base.Predicate; import org.roboguice.shaded.goole.common.collect.FluentIterable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import lombok.Data; @Singleton public final class MovementReasonManager { public static final String INVENTORY_POSITIVE = "INVENTORY_POSITIVE"; public static final String INVENTORY_NEGATIVE = "INVENTORY_NEGATIVE"; private static final String DEFAULT_PREFIX = "DEFAULT"; public static final String INVENTORY = "INVENTORY"; public static final String DEFAULT_ISSUE = "DEFAULT_ISSUE"; public static final String DEFAULT_RECEIVE = "DEFAULT_RECEIVE"; public static final String DEFAULT_NEGATIVE_ADJUSTMENT = "DEFAULT_NEGATIVE_ADJUSTMENT"; public static final String DEFAULT_POSITIVE_ADJUSTMENT = "DEFAULT_POSITIVE_ADJUSTMENT"; public static final String UNPACK_KIT = "UNPACK_KIT"; public static final String DONATION = "DONATION"; public static final String DDM = "DISTRICT_DDM"; public static final String RETURN_TO_DDM = "RETURN_TO_DDM"; Context context; public static final String RES_DIVIDER = "[|]"; List<MovementReason> currentReasonList; List<MovementType> typeList; private static MovementReasonManager instance; Map<String, ArrayList<MovementReason>> reasonCache; Map<String, ArrayList<MovementType>> typeCache; @Inject private MovementReasonManager(Context context){ this.context = context; reasonCache = new HashMap<>(); typeCache = new HashMap<>(); currentReasonList = initReasonList(this.context.getResources().getConfiguration().locale); typeList = initTypeList(this.context.getResources().getConfiguration().locale); } public static MovementReasonManager getInstance(){ if (instance == null){ instance = new MovementReasonManager(LMISApp.getContext()); } return instance; } public void refresh(){ instance = new MovementReasonManager(LMISApp.getContext()); } private ArrayList<MovementType> initTypeList(Locale locale) { if (typeCache.containsKey(locale.getLanguage())){ return typeCache.get(locale.getLanguage()); } ArrayList<MovementType> typeArrayList = new ArrayList<>(); MovementType.ISSUE.description = getResourceByLocal(locale).getString(R.string.ISSUE); MovementType.RECEIVE.description = getResourceByLocal(locale).getString(R.string.RECEIVE); MovementType.POSITIVE_ADJUST.description = getResourceByLocal(locale).getString(R.string.POSITIVE_ADJUST); MovementType.NEGATIVE_ADJUST.description = getResourceByLocal(locale).getString(R.string.NEGATIVE_ADJUST); typeArrayList.add(MovementType.ISSUE); typeArrayList.add(MovementType.RECEIVE); typeArrayList.add(MovementType.NEGATIVE_ADJUST); typeArrayList.add(MovementType.POSITIVE_ADJUST); typeCache.put(locale.getLanguage(), typeArrayList); return typeArrayList; } private ArrayList<MovementReason> initReasonList(Locale locale) { if (reasonCache.containsKey(locale.getLanguage())){ return reasonCache.get(locale.getLanguage()); } String[] reasonData = getResourceByLocal(locale).getStringArray(R.array.reason_data); ArrayList<MovementReason> reasonList = parseReasonListFromConfig(reasonData); reasonCache.put(locale.getLanguage(), reasonList); return reasonList; } @NonNull private static ArrayList<MovementReason> parseReasonListFromConfig(String[] reasonData) { ArrayList<MovementReason> reasonList = new ArrayList<>(); for (String s : reasonData) { String[] values = s.split(RES_DIVIDER); if (values.length < 3) { Log.d(MovementReasonManager.class.getSimpleName(), "Invalid Config =>" + s); continue; } MovementReason reason = new MovementReason(MovementType.valueOf(values[0]), values[1], values[2]); reasonList.add(reason); } return reasonList; } public List<MovementReason> buildReasonListForMovementType(final MovementType type){ return FluentIterable.from(currentReasonList).filter(new Predicate<MovementReason>() { @Override public boolean apply(MovementReason movementReason) { return movementReason.getMovementType() == type && movementReason.canBeDisplayOnMovementMenu(); } }).toList(); } public MovementReason queryByDesc(final String reason) throws MovementReasonNotFoundException{ return queryByDesc(reason, context.getResources().getConfiguration().locale); } public MovementReason queryByDesc(final String desc, Locale locale) throws MovementReasonNotFoundException{ ArrayList<MovementReason> reasonList = initReasonList(locale); Optional<MovementReason> matched = FluentIterable.from(reasonList).firstMatch(new Predicate<MovementReason>() { @Override public boolean apply(MovementReason movementReason) { return movementReason.getDescription().equalsIgnoreCase(desc); } }); if (!matched.isPresent()){ throw new MovementReasonNotFoundException(desc); } return matched.get(); } public MovementReason queryByCode(final String code) throws MovementReasonNotFoundException{ Optional<MovementReason> matched = FluentIterable.from(currentReasonList).firstMatch(new Predicate<MovementReason>() { @Override public boolean apply(MovementReason movementReason) { return movementReason.getCode().equalsIgnoreCase(code); } }); if (!matched.isPresent()){ throw new MovementReasonNotFoundException(code); } return matched.get(); } public Resources getResourceByLocal(Locale locale){ Resources standardResources = context.getResources(); AssetManager assets = standardResources.getAssets(); DisplayMetrics metrics = standardResources.getDisplayMetrics(); Configuration config = new Configuration(standardResources.getConfiguration()); config.locale = locale; return new Resources(assets, metrics, config); } public List<MovementType> getMovementTypes() { return typeList; } public enum MovementType { RECEIVE("RECEIVE"), ISSUE("ISSUE"), POSITIVE_ADJUST("POSITIVE_ADJUST"), NEGATIVE_ADJUST("NEGATIVE_ADJUST"), PHYSICAL_INVENTORY("PHYSICAL_INVENTORY"); private final String value; String description; MovementType(String value) { this.value = value; } public String getDescription() { return description; } @Override public String toString() { return value; } public boolean isNegative() { return this == ISSUE || this == NEGATIVE_ADJUST; } } @Data public static class MovementReason { MovementType movementType; String code; String description; public MovementReason(MovementType type, String code, String description) { this.movementType = type; this.code = code; this.description = description; } public boolean isInventoryAdjustment(){ return INVENTORY_NEGATIVE.equalsIgnoreCase(code) || INVENTORY_POSITIVE.equalsIgnoreCase(code); } public boolean isPhysicalInventory() { return MovementType.PHYSICAL_INVENTORY == movementType; } protected boolean canBeDisplayOnMovementMenu(){ return !(code.startsWith(DEFAULT_PREFIX) || code.equalsIgnoreCase(INVENTORY) || MovementReasonManager.UNPACK_KIT.equals(code) || MovementReasonManager.DONATION.equals(code)); } public boolean isIssueAdjustment() { return MovementType.ISSUE == movementType; } } }