org.openlmis.core.view.holder.StockMovementViewHolder.java Source code

Java tutorial

Introduction

Here is the source code for org.openlmis.core.view.holder.StockMovementViewHolder.java

Source

/*
 * 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.view.holder;

import android.app.DatePickerDialog;
import android.graphics.Color;
import android.text.InputFilter;
import android.text.TextUtils;
import android.view.View;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TextView;

import org.apache.commons.lang3.StringUtils;
import org.openlmis.core.LMISApp;
import org.openlmis.core.R;
import org.openlmis.core.googleAnalytics.TrackerActions;
import org.openlmis.core.googleAnalytics.TrackerCategories;
import org.openlmis.core.manager.MovementReasonManager;
import org.openlmis.core.model.StockCard;
import org.openlmis.core.model.StockMovementItem;
import org.openlmis.core.utils.DateUtil;
import org.openlmis.core.utils.ToastUtil;
import org.openlmis.core.view.adapter.StockMovementAdapter;
import org.openlmis.core.view.viewmodel.StockMovementViewModel;
import org.openlmis.core.view.widget.InputFilterMinMax;
import org.openlmis.core.view.widget.MovementTypeDialog;

import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import roboguice.inject.InjectView;

import static java.util.Arrays.asList;

public class StockMovementViewHolder extends BaseViewHolder {

    @InjectView(R.id.tx_date)
    TextView txMovementDate;

    @InjectView(R.id.tx_reason)
    TextView txReason;

    @InjectView(R.id.et_document_no)
    EditText etDocumentNo;

    @InjectView(R.id.et_received)
    EditText etReceived;

    @InjectView(R.id.et_negative_adjustment)
    EditText etNegativeAdjustment;

    @InjectView(R.id.et_positive_adjustment)
    EditText etPositiveAdjustment;

    @InjectView(R.id.et_issued)
    EditText etIssued;

    @InjectView(R.id.et_requested)
    EditText etRequested;

    @InjectView(R.id.tx_stock_on_hand)
    TextView txStockExistence;

    @InjectView(R.id.tx_signature)
    TextView txSignature;

    private StockMovementAdapter.MovementChangedListener movementChangeListener;
    private Map<MovementReasonManager.MovementType, List> movementViewMap;

    public StockMovementViewHolder(View itemView,
            StockMovementAdapter.MovementChangedListener movementChangeListener) {
        super(itemView);
        this.movementChangeListener = movementChangeListener;

        InputFilter[] filters = new InputFilter[] { new InputFilterMinMax(Integer.MAX_VALUE) };
        etReceived.setFilters(filters);
        etNegativeAdjustment.setFilters(filters);
        etPositiveAdjustment.setFilters(filters);
        etIssued.setFilters(filters);
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.setFilters(filters);
        }
        initStockViewMap();
    }

    private void initStockViewMap() {
        movementViewMap = new HashMap<>();
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            movementViewMap.put(MovementReasonManager.MovementType.ISSUE, asList(etIssued, etRequested));
        } else {
            movementViewMap.put(MovementReasonManager.MovementType.ISSUE, asList(etIssued));
        }

        movementViewMap.put(MovementReasonManager.MovementType.RECEIVE, asList(etReceived));
        movementViewMap.put(MovementReasonManager.MovementType.NEGATIVE_ADJUST, asList(etNegativeAdjustment));
        movementViewMap.put(MovementReasonManager.MovementType.POSITIVE_ADJUST, asList(etPositiveAdjustment));
    }

    public void populate(final StockMovementViewModel model, StockCard stockCard) {
        removeTextChangeListeners(model, stockCard.getStockOnHand());

        disableLine();
        hideUnderline();

        txMovementDate.setText(model.getMovementDate());
        etDocumentNo.setText(model.getDocumentNo());
        etReceived.setText(model.getReceived());
        etNegativeAdjustment.setText(model.getNegativeAdjustment());
        etPositiveAdjustment.setText(model.getPositiveAdjustment());
        etIssued.setText(model.getIssued());
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.setText(model.getRequested());
        }
        txStockExistence.setText(model.getStockExistence());
        txSignature.setText(model.getSignature());
        if (model.getReason() != null) {
            txReason.setText(model.getReason().getDescription());
        } else {
            txReason.setText(StringUtils.EMPTY);
        }

        setItemViewTextColor(model);

        if (model.isDraft()) {
            setInitialDraftStyle(model);
        } else {
            itemView.setBackgroundColor(Color.TRANSPARENT);
        }

        addClickListeners(model, getPreviousMovementDate(stockCard));

        addTextChangedListeners(model, stockCard.getStockOnHand());
    }

    private void setInitialDraftStyle(final StockMovementViewModel model) {
        txMovementDate.setEnabled(true);
        txReason.setEnabled(true);
        setEditableQuantityField(model);
        if (!TextUtils.isEmpty(model.getMovementDate())) {
            highLightAndShowBottomBtn();
        }
    }

    private void setEditableQuantityField(StockMovementViewModel model) {
        if (model.getReason() != null) {
            enableAndUnderlineEditText(etDocumentNo);
            resetStockEditText(model.getReason().getMovementType());
        }
    }

    private void resetStockEditText(MovementReasonManager.MovementType type) {
        for (Map.Entry<MovementReasonManager.MovementType, List> movementView : movementViewMap.entrySet()) {
            if (movementView.getKey().equals(type)) {
                for (Object view : movementView.getValue()) {
                    enableAndUnderlineEditText((EditText) view);
                }
            } else {
                for (Object view : movementView.getValue()) {
                    disableAndRemoveUnderlineEditText((EditText) view);
                }
            }
        }
    }

    public void highLightAndShowBottomBtn() {
        itemView.setBackgroundResource(R.color.color_primary_50);

        if (movementChangeListener != null) {
            movementChangeListener.movementChange();
        }
    }

    private void enableAndUnderlineEditText(EditText editText) {
        editText.setEnabled(true);
        editText.setBackground(new EditText(context).getBackground());
    }

    private void disableAndRemoveUnderlineEditText(EditText editText) {
        editText.setText(StringUtils.EMPTY);
        editText.setEnabled(false);
        editText.setBackground(null);
    }

    private void removeTextChangeListeners(StockMovementViewModel model, long currentStockOnHand) {
        etReceived.removeTextChangedListener(new EditTextWatcher(this, etReceived, model, currentStockOnHand));
        etNegativeAdjustment.removeTextChangedListener(
                new EditTextWatcher(this, etNegativeAdjustment, model, currentStockOnHand));
        etPositiveAdjustment.removeTextChangedListener(
                new EditTextWatcher(this, etPositiveAdjustment, model, currentStockOnHand));

        etIssued.removeTextChangedListener(new EditTextWatcher(this, etIssued, model, currentStockOnHand));
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested
                    .removeTextChangedListener(new EditTextWatcher(this, etRequested, model, currentStockOnHand));
        }

        etDocumentNo.removeTextChangedListener(new EditTextWatcher(this, etDocumentNo, model, currentStockOnHand));
    }

    private void addClickListeners(final StockMovementViewModel model, final Date previousMovementDate) {
        txReason.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (model.isDraft()) {
                    new MovementTypeDialog(context, new MovementSelectListener(model)).show();
                    trackStockMovementEvent(TrackerActions.SelectReason);
                }
            }
        });

        txMovementDate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (model.isDraft()) {
                    showDatePickerDialog(model, previousMovementDate);
                    trackStockMovementEvent(TrackerActions.SelectMovementDate);
                }
            }
        });
    }

    private void trackStockMovementEvent(TrackerActions action) {
        LMISApp.getInstance().trackEvent(TrackerCategories.StockMovement, action);
    }

    private void showDatePickerDialog(final StockMovementViewModel model, final Date previousMovementDate) {
        final Calendar today = GregorianCalendar.getInstance();

        DatePickerDialog dialog = new DatePickerDialog(context, DatePickerDialog.BUTTON_NEUTRAL,
                new MovementDateListener(model, previousMovementDate), today.get(Calendar.YEAR),
                today.get(Calendar.MONTH), today.get(Calendar.DAY_OF_MONTH));
        dialog.show();
    }

    private void addTextChangedListeners(StockMovementViewModel model, long currentStockOnHand) {
        etReceived.addTextChangedListener(new EditTextWatcher(this, etReceived, model, currentStockOnHand));
        etNegativeAdjustment
                .addTextChangedListener(new EditTextWatcher(this, etNegativeAdjustment, model, currentStockOnHand));
        etPositiveAdjustment
                .addTextChangedListener(new EditTextWatcher(this, etPositiveAdjustment, model, currentStockOnHand));
        etIssued.addTextChangedListener(new EditTextWatcher(this, etIssued, model, currentStockOnHand));
        etDocumentNo.addTextChangedListener(new EditTextWatcher(this, etDocumentNo, model, currentStockOnHand));

        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.addTextChangedListener(new EditTextWatcher(this, etRequested, model, currentStockOnHand));
        }
    }

    private void setItemViewTextColor(StockMovementViewModel model) {
        if (model.isIssuedReason()) {
            setRowFontColor(context.getResources().getColor(R.color.color_black));
        } else {
            setRowFontColor(context.getResources().getColor(R.color.color_red));
        }
    }

    private void setRowFontColor(int color) {
        txMovementDate.setTextColor(color);
        txReason.setTextColor(color);
        etDocumentNo.setTextColor(color);
        etReceived.setTextColor(color);
        etPositiveAdjustment.setTextColor(color);
        etNegativeAdjustment.setTextColor(color);
        txStockExistence.setTextColor(color);
        txSignature.setTextColor(color);
    }

    private void hideUnderline() {
        etDocumentNo.setBackground(null);
        etIssued.setBackground(null);
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.setBackground(null);
        }
        etNegativeAdjustment.setBackground(null);
        etPositiveAdjustment.setBackground(null);
        etReceived.setBackground(null);
    }

    private void disableLine() {
        etDocumentNo.setEnabled(false);
        etReceived.setEnabled(false);
        etNegativeAdjustment.setEnabled(false);
        etPositiveAdjustment.setEnabled(false);
        etIssued.setEnabled(false);
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.setEnabled(false);
        }
        txMovementDate.setEnabled(false);
        txReason.setEnabled(false);
    }

    public void resetLine() {
        txMovementDate.setText(StringUtils.EMPTY);
        txReason.setText(StringUtils.EMPTY);
        txStockExistence.setText(StringUtils.EMPTY);

        etDocumentNo.setText(StringUtils.EMPTY);
        etReceived.setText(StringUtils.EMPTY);
        etNegativeAdjustment.setText(StringUtils.EMPTY);
        etPositiveAdjustment.setText(StringUtils.EMPTY);
        etIssued.setText(StringUtils.EMPTY);
        if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_add_requested_in_stock_movement)) {
            etRequested.setText(StringUtils.EMPTY);
        }
        disableLine();
        hideUnderline();
    }

    protected Date getPreviousMovementDate(StockCard stockCard) {
        List<StockMovementItem> stockMovements = stockCard.getStockMovementItemsWrapper();
        if (stockMovements != null) {
            if (!stockMovements.isEmpty()) {
                Collections.sort(stockMovements, new Comparator<StockMovementItem>() {
                    public int compare(StockMovementItem item1, StockMovementItem item2) {
                        return item1.getMovementDate().compareTo(item2.getMovementDate());
                    }
                });
                return stockMovements.get(stockMovements.size() - 1).getMovementDate();
            }
        }
        return null;
    }

    class MovementSelectListener implements MovementTypeDialog.OnMovementSelectListener {

        private StockMovementViewModel model;

        public MovementSelectListener(StockMovementViewModel model) {
            this.model = model;
        }

        @Override
        public void onComplete(MovementReasonManager.MovementReason reason) {
            txReason.setText(reason.getDescription());
            model.setReason(reason);

            setMovementDate();

            clearQuantityAndDocumentNoField();
            setEditableQuantityField(model);
            highLightAndShowBottomBtn();
            setItemViewTextColor(model);
        }

        private void setMovementDate() {
            if (StringUtils.EMPTY.equals(txMovementDate.getText().toString())) {
                String movementDate = DateUtil.formatDate(new Date());
                txMovementDate.setText(movementDate);
                model.setMovementDate(movementDate);
            }
        }
    }

    private void clearQuantityAndDocumentNoField() {
        etDocumentNo.setText(StringUtils.EMPTY);
        etReceived.setText(StringUtils.EMPTY);
        etNegativeAdjustment.setText(StringUtils.EMPTY);
        etPositiveAdjustment.setText(StringUtils.EMPTY);
        etIssued.setText(StringUtils.EMPTY);
    }

    class MovementDateListener implements DatePickerDialog.OnDateSetListener {

        private Date previousMovementDate;
        private StockMovementViewModel model;

        public MovementDateListener(StockMovementViewModel model, Date previousMovementDate) {
            this.previousMovementDate = previousMovementDate;
            this.model = model;
        }

        @Override
        public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

            Date chosenDate = new GregorianCalendar(year, monthOfYear, dayOfMonth).getTime();
            if (validateStockMovementDate(previousMovementDate, chosenDate)) {
                txMovementDate.setText(DateUtil.formatDate(chosenDate));
                model.setMovementDate(DateUtil.formatDate(chosenDate));
                highLightAndShowBottomBtn();
            } else {
                ToastUtil.show(R.string.msg_invalid_stock_movement_date);
            }
        }

        private boolean validateStockMovementDate(Date previousMovementDate, Date chosenDate) {
            Calendar today = GregorianCalendar.getInstance();

            return previousMovementDate == null
                    || !previousMovementDate.after(chosenDate) && !chosenDate.after(today.getTime());
        }
    }

}