/* * 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.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_number) 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 Map<MovementReasonManager.MovementType, List> movementViewMap; public StockMovementViewHolder(View itemView) { super(itemView); InputFilter[] filters = new InputFilter[]{new InputFilterMinMax(Integer.MAX_VALUE)}; etReceived.setFilters(filters); etNegativeAdjustment.setFilters(filters); etPositiveAdjustment.setFilters(filters); etIssued.setFilters(filters); etRequested.setFilters(filters); initStockViewMap(); } private void initStockViewMap() { movementViewMap = new HashMap<>(); movementViewMap.put(MovementReasonManager.MovementType.ISSUE, asList(etIssued, etRequested)); 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()); 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); } 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 StockMovementViewHolderEditTextWatcher(this, etReceived, model, currentStockOnHand)); etNegativeAdjustment.removeTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etNegativeAdjustment, model, currentStockOnHand)); etPositiveAdjustment.removeTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etPositiveAdjustment, model, currentStockOnHand)); etIssued.removeTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etIssued, model, currentStockOnHand)); etRequested.removeTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etRequested, model, currentStockOnHand)); etDocumentNo.removeTextChangedListener(new StockMovementViewHolderEditTextWatcher(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 StockMovementViewHolderEditTextWatcher(this, etReceived, model, currentStockOnHand)); etNegativeAdjustment.addTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etNegativeAdjustment, model, currentStockOnHand)); etPositiveAdjustment.addTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etPositiveAdjustment, model, currentStockOnHand)); etIssued.addTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etIssued, model, currentStockOnHand)); etDocumentNo.addTextChangedListener(new StockMovementViewHolderEditTextWatcher(this, etDocumentNo, model, currentStockOnHand)); etRequested.addTextChangedListener(new StockMovementViewHolderEditTextWatcher(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); 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); etRequested.setEnabled(false); txMovementDate.setEnabled(false); txReason.setEnabled(false); } 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()); } } }