/*
* Copyright (C) 2016 Payworks GmbH (http://www.payworks.com)
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.mpos.ui.shared.view;
import android.app.Activity;
import android.app.Fragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.math.BigDecimal;
import java.util.List;
import io.mpos.paymentdetails.PaymentDetailsScheme;
import io.mpos.platform.LocalizationToolbox;
import io.mpos.transactions.RefundDetailsStatus;
import io.mpos.transactions.TransactionStatus;
import io.mpos.transactions.TransactionStatusDetailsCodes;
import io.mpos.transactions.TransactionType;
import io.mpos.ui.R;
import io.mpos.ui.shared.MposUi;
import io.mpos.ui.shared.model.MposUiAppearance;
import io.mpos.ui.shared.model.MposUiConfiguration;
import io.mpos.ui.shared.model.RefundTransactionDataHolder;
import io.mpos.ui.shared.model.TransactionDataHolder;
import io.mpos.ui.shared.model.TransactionHistoryItem;
import io.mpos.ui.shared.util.TransactionAmountUtil;
import io.mpos.ui.shared.util.TransactionHistoryHelper;
import io.mpos.ui.shared.util.UiHelper;
public class SummaryFragment extends Fragment {
public interface Interaction {
void onSummaryCaptureButtonClicked(String transactionIdentifier);
void onSummarySendReceiptButtonClicked(String transactionIdentifier);
void onSummaryRetryButtonClicked();
void onSummaryRefundButtonClicked(String transactionIdentifier);
void onSummaryPrintReceiptButtonClicked(String transactionIdentifier);
void onSummaryCloseButtonClicked();
}
public final static String TAG = "SummaryFragment";
private final static String SAVED_INSTANCE_STATE_REFUND_ENABLED = "io.mpos.ui.SummaryFragment.REFUND_ENABLED";
private final static String SAVED_INSTANCE_STATE_RETRY_ENABLED = "io.mpos.ui.SummaryFragment.RETRY_ENABLED";
private final static String SAVED_INSTANCE_STATE_CAPTURE_ENABLED = "io.mpos.ui.SummaryFragment.CAPTURE_ENABLED";
private final static String SAVED_INSTANCE_STATE_TRANSACTION_DATA_HOLDER = "io.mpos.ui.SummaryFragment.TRANSACTION_DATA_HOLDER";
private TransactionDataHolder mTransactionDataHolder;
private TransactionHistoryHelper mTransactionHistoryHelper;
private LocalizationToolbox mLocalizationToolbox;
private MposUiAppearance mMposUiAppearance;
private boolean mRetryEnabled;
private boolean mRefundEnabled;
private boolean mCaptureEnabled;
private boolean mSendEnabled = true;
public static SummaryFragment newInstance(boolean retryEnabled, boolean refundEnabled, boolean captureEnabled, TransactionDataHolder transactionDataHolder) {
SummaryFragment fragment = new SummaryFragment();
fragment.setTransactionDataHolder(transactionDataHolder);
fragment.setRetryEnabled(retryEnabled);
fragment.setRefundEnabled(refundEnabled);
fragment.setCaptureEnabled(captureEnabled);
return fragment;
}
public SummaryFragment() {
// Required empty public constructor
}
private Interaction mInteractionActivity;
private TextView mTransactionStatusView;
private TextView mTransactionStatusInformationView;
private TextView mAmountView;
private TextView mSubjectView;
private TextView mAccountNumberView;
private TextView mSchemeView;
private TextView mDateTimeView;
private Button mRetryButton;
private Button mRefundButton;
private Button mCaptureButton;
private Button mPrintReceiptButton;
private Button mSendReceiptButton;
private Button mCloseButton;
private View mHeaderViewContainer;
private LinearLayout mTransactionHistoryContainer;
//Dividers
private View mSchemeAccNoViewDivider;
private View mHeaderViewDivider;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
mMposUiAppearance = MposUi.getInitializedInstance().getConfiguration().getAppearance();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.mpu_fragment_summary, container, false);
mTransactionStatusView = (TextView) view.findViewById(R.id.mpu_summary_status_view);
mTransactionStatusInformationView = (TextView) view.findViewById(R.id.mpu_summary_status_information_view);
mAmountView = (TextView) view.findViewById(R.id.mpu_summary_amount_view);
mSubjectView = (TextView) view.findViewById(R.id.mpu_summary_subject_view);
mAccountNumberView = (TextView) view.findViewById(R.id.mpu_summary_account_number_view);
mSchemeView = (TextView) view.findViewById(R.id.mpu_summary_scheme_view);
mDateTimeView = (TextView) view.findViewById(R.id.mpu_summary_datetime_view);
mCaptureButton = (Button) view.findViewById(R.id.mpu_summary_capture_button);
mRefundButton = (Button) view.findViewById(R.id.mpu_summary_refund_button);
mRetryButton = (Button) view.findViewById(R.id.mpu_summary_retry_button);
mPrintReceiptButton = (Button) view.findViewById(R.id.mpu_summary_print_receipt_button);
mSendReceiptButton = (Button) view.findViewById(R.id.mpu_summary_send_receipt_button);
mCloseButton = (Button) view.findViewById(R.id.mpu_summary_close_button);
mTransactionHistoryContainer = (LinearLayout) view.findViewById(R.id.mpu_summary_tx_history_container);
mSchemeAccNoViewDivider = view.findViewById(R.id.mpu_summary_divider_scheme_accno_view);
mHeaderViewDivider = view.findViewById(R.id.mpu_summary_divider_header_view);
mHeaderViewContainer = view.findViewById(R.id.mpu_summary_header_container_view);
return view;
}
@Override
public void onResume() {
super.onResume();
getActivity().setTitle(R.string.MPUSummary);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mInteractionActivity = (Interaction) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement SummaryInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mInteractionActivity = null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable(SAVED_INSTANCE_STATE_TRANSACTION_DATA_HOLDER, mTransactionDataHolder);
outState.putBoolean(SAVED_INSTANCE_STATE_REFUND_ENABLED, mRefundEnabled);
outState.putBoolean(SAVED_INSTANCE_STATE_RETRY_ENABLED, mRetryEnabled);
outState.putBoolean(SAVED_INSTANCE_STATE_CAPTURE_ENABLED, mCaptureEnabled);
super.onSaveInstanceState(outState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
mTransactionDataHolder = savedInstanceState.getParcelable(SAVED_INSTANCE_STATE_TRANSACTION_DATA_HOLDER);
mRefundEnabled = savedInstanceState.getBoolean(SAVED_INSTANCE_STATE_REFUND_ENABLED);
mRetryEnabled = savedInstanceState.getBoolean(SAVED_INSTANCE_STATE_RETRY_ENABLED);
mCaptureEnabled = savedInstanceState.getBoolean(SAVED_INSTANCE_STATE_CAPTURE_ENABLED);
}
mLocalizationToolbox = MposUi.getInitializedInstance().getTransactionProvider().getLocalizationToolbox();
mTransactionHistoryHelper = new TransactionHistoryHelper(mTransactionDataHolder, mLocalizationToolbox);
showTransactionStatusAndAmount();
showSchemeAndAccountNumber();
showSubject();
showTransactionDateTime();
showTransactionHistory();
setupButtons(mTransactionDataHolder.getTransactionStatus());
setupClickListeners();
setupDividers();
}
private void setupButtons(TransactionStatus transactionStatus) {
if (TransactionStatus.APPROVED == transactionStatus) {
mRetryButton.setVisibility(View.GONE);
mCaptureButton.setVisibility(showCaptureButton() ? View.VISIBLE : View.GONE);
mRefundButton.setVisibility(showRefundButton() ? View.VISIBLE : View.GONE);
mPrintReceiptButton.setVisibility(showPrintReceiptButton() ? View.VISIBLE : View.GONE);
mSendReceiptButton.setVisibility(showSendReceiptButton() ? View.VISIBLE : View.GONE);
} else if (TransactionStatus.DECLINED == transactionStatus || TransactionStatus.ABORTED == transactionStatus) {
mCaptureButton.setVisibility(View.GONE);
mRefundButton.setVisibility(View.GONE);
mRetryButton.setVisibility(mRetryEnabled ? View.VISIBLE : View.GONE);
mPrintReceiptButton.setVisibility(showPrintReceiptButton() ? View.VISIBLE : View.GONE);
mSendReceiptButton.setVisibility(showSendReceiptButton() ? View.VISIBLE : View.GONE);
} else {
mRetryButton.setVisibility(mRetryEnabled ? View.VISIBLE : View.GONE);
mCaptureButton.setVisibility(View.GONE);
mRefundButton.setVisibility(View.GONE);
mPrintReceiptButton.setVisibility(View.GONE);
mPrintReceiptButton.setVisibility(View.GONE);
mSendReceiptButton.setVisibility(View.GONE);
mSendEnabled = false;
}
}
private void setupClickListeners() {
mRetryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mInteractionActivity.onSummaryRetryButtonClicked();
}
});
mCaptureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.MPUCaptureTransaction)
.setMessage(R.string.MPUCapturePrompt)
.setPositiveButton(R.string.MPUCapture, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mInteractionActivity.onSummaryCaptureButtonClicked(mTransactionDataHolder.getTransactionIdentifier());
}
})
.setNegativeButton(R.string.MPUAbort, null)
.show();
}
});
mRefundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.MPURefundTransaction)
.setMessage(R.string.MPURefundPrompt)
.setPositiveButton(R.string.MPURefund, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mInteractionActivity.onSummaryRefundButtonClicked(mTransactionDataHolder.getTransactionIdentifier());
}
})
.setNegativeButton(R.string.MPUAbort, null)
.show();
}
});
mSendReceiptButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mInteractionActivity.onSummarySendReceiptButtonClicked(getTransactionIdentifierForSendingAndPrinting());
}
});
mPrintReceiptButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mInteractionActivity.onSummaryPrintReceiptButtonClicked(getTransactionIdentifierForSendingAndPrinting());
}
});
mCloseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mInteractionActivity.onSummaryCloseButtonClicked();
}
});
}
private void showTransactionStatusAndAmount() {
setTransactionStatusText();
setEffectiveTotalAmountText();
}
private void setEffectiveTotalAmountText() {
BigDecimal effectiveTotalAmount = TransactionAmountUtil.calculateEffectiveTotalAmount(mTransactionDataHolder);
mAmountView.setText(mLocalizationToolbox.formatAmount(effectiveTotalAmount, mTransactionDataHolder.getCurrency()));
}
private void showSchemeAndAccountNumber() {
//Set scheme and masked account number
if (mTransactionDataHolder.getMaskedAccountNumber() == null) {
mAccountNumberView.setVisibility(View.GONE);
mSchemeAccNoViewDivider.setVisibility(View.GONE);
mSchemeView.setVisibility(View.GONE);
} else {
String maskedAccountNumber = mTransactionDataHolder.getMaskedAccountNumber();
if (maskedAccountNumber != null) {
mAccountNumberView.setText(UiHelper.formatAccountNumber(maskedAccountNumber));
} else { //account number is null! WHAT!
mAccountNumberView.setVisibility(View.GONE);
mSchemeAccNoViewDivider.setVisibility(View.GONE);
mSchemeView.setVisibility(View.GONE);
return;
}
PaymentDetailsScheme scheme = PaymentDetailsScheme.UNKNOWN;
if (mTransactionDataHolder.getPaymentDetailsScheme() != null)
scheme = mTransactionDataHolder.getPaymentDetailsScheme();
if (UiHelper.getDrawableIdForCardScheme(scheme) != -1) {
mSchemeView.setCompoundDrawablesWithIntrinsicBounds(UiHelper.getDrawableIdForCardScheme(scheme), 0, 0, 0);
} else if (scheme != null) {
mSchemeView.setText(scheme.toString());
//The compound drawable padding is 8dp(in the image) + padding from layout is 8dp = 16dp
//We change padding from 8dp tp 16dp because we dont set the drawable. Only text is visible.
mSchemeView.setPadding(getResources().getDimensionPixelSize(R.dimen.mpu_content_padding), 0, 0, 0);
} else { //scheme is null! WHAAT!
mAccountNumberView.setVisibility(View.GONE);
mSchemeAccNoViewDivider.setVisibility(View.GONE);
mSchemeView.setVisibility(View.GONE);
}
}
}
private void showSubject() {
//Set the subject
if (mTransactionDataHolder.getSubject() != null) {
String subject = mTransactionDataHolder.getSubject();
if (!TextUtils.isEmpty(subject)) {
mSubjectView.setText(subject);
} else {
mSubjectView.setVisibility(View.GONE);
}
} else {
mSubjectView.setVisibility(View.GONE);
}
}
private void showTransactionDateTime() {
//Set the date and time
if (mTransactionDataHolder.getCreatedTimestamp() == 0) {
mDateTimeView.setVisibility(View.GONE);
} else {
mDateTimeView.setText(DateUtils.formatDateTime(this.getActivity().getApplicationContext(), mTransactionDataHolder.getCreatedTimestamp(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR));
}
}
private void showTransactionHistory() {
if (mTransactionDataHolder.getRefundTransactions() == null) {
mTransactionHistoryContainer.setVisibility(View.GONE);
mHeaderViewDivider.setVisibility(View.GONE);
return;
}
mTransactionHistoryContainer.setVisibility(View.VISIBLE);
mHeaderViewDivider.setVisibility(View.VISIBLE);
List<TransactionHistoryItem> transactionHistoryItems = mTransactionHistoryHelper.createTransactionHistoryItems(getActivity().getApplicationContext());
for (TransactionHistoryItem transactionHistoryItem : transactionHistoryItems) {
mTransactionHistoryContainer.addView(createTransactionHistoryItemView(transactionHistoryItem));
}
}
private View createTransactionHistoryItemView(TransactionHistoryItem item) {
View historyItemView = LayoutInflater.from(getActivity().getApplicationContext()).inflate(R.layout.mpu_listitem_transaction_history, mTransactionHistoryContainer, false);
TextView statusView = (TextView) historyItemView.findViewById(R.id.mpu_summary_tx_history_list_status_view);
TextView timestampView = (TextView) historyItemView.findViewById(R.id.mpu_summary_tx_history_list_timestamp_view);
TextView partialCaptureView = (TextView) historyItemView.findViewById(R.id.mpu_summary_tx_history_list_partial_cp_view);
TextView amountView = (TextView) historyItemView.findViewById(R.id.mpu_summary_tx_history_list_amount_view);
statusView.setText(item.getStatusText());
amountView.setText(item.getAmountText());
timestampView.setText(item.getTimestampText());
switch (item.getType()) {
case CHARGE:
partialCaptureView.setVisibility(View.GONE);
amountView.setTextColor(mMposUiAppearance.getApprovedBackgroundColor());
break;
case PREAUTHORIZED:
partialCaptureView.setVisibility(View.GONE);
amountView.setTextColor(mMposUiAppearance.getPreAuthorizedBackgroundColor());
break;
case REFUND:
partialCaptureView.setVisibility(View.GONE);
amountView.setTextColor(mMposUiAppearance.getRefundedBackgroundColor());
break;
case PARTIAL_CAPTURE:
partialCaptureView.setVisibility(View.VISIBLE);
partialCaptureView.setText(item.getPartialCaptureHintText());
amountView.setTextColor(mMposUiAppearance.getApprovedBackgroundColor());
break;
}
return historyItemView;
}
public void setTransactionDataHolder(TransactionDataHolder transactionDataHolder) {
mTransactionDataHolder = transactionDataHolder;
}
public void setRetryEnabled(boolean retryEnabled) {
mRetryEnabled = retryEnabled;
}
public void setCaptureEnabled(boolean captureEnabled) {
mCaptureEnabled = captureEnabled;
}
public void setRefundEnabled(boolean refundEnabled) {
mRefundEnabled = refundEnabled;
}
private boolean showRefundButton() {
return mRefundEnabled // Allowed?
&& MposUi.getInitializedInstance().getConfiguration().getSummaryFeatures().contains(MposUiConfiguration.SummaryFeature.REFUND_TRANSACTION) // Feature enabled?
&& mTransactionDataHolder.getTransactionType() == TransactionType.CHARGE // Charge
&& mTransactionDataHolder.getTransactionStatus() == TransactionStatus.APPROVED // Approved?
&& mTransactionDataHolder.getRefundDetailsStatus() != RefundDetailsStatus.REFUNDED // Not refunded?
&& isTransactionRefundable();
}
private boolean showCaptureButton() {
return mCaptureEnabled // Allowed?
&& MposUi.getInitializedInstance().getConfiguration().getSummaryFeatures().contains(MposUiConfiguration.SummaryFeature.CAPTURE_TRANSACTION) // Feature enabled?
&& mTransactionDataHolder.getTransactionType() == TransactionType.CHARGE // Charge
&& mTransactionDataHolder.getTransactionStatus() == TransactionStatus.APPROVED // Approved?
&& mTransactionDataHolder.getRefundDetailsStatus() != RefundDetailsStatus.REFUNDED // Not refunded?
&& !mTransactionDataHolder.isCaptured(); // Not captured?
}
private boolean isTransactionRefundable() {
if (mTransactionDataHolder.getRefundDetailsStatus() == null) {
return false;
}
RefundDetailsStatus status = mTransactionDataHolder.getRefundDetailsStatus();
return status != null && (status == RefundDetailsStatus.REFUNDABLE_PARTIAL_AND_FULL || status == RefundDetailsStatus.REFUNDABLE_FULL_ONLY);
}
private boolean showPrintReceiptButton() {
return MposUi.getInitializedInstance().getConfiguration().getSummaryFeatures().contains(MposUiConfiguration.SummaryFeature.PRINT_RECEIPT);
}
private boolean showSendReceiptButton() {
return mSendEnabled && MposUi.getInitializedInstance().getConfiguration().getSummaryFeatures().contains(MposUiConfiguration.SummaryFeature.SEND_RECEIPT_VIA_EMAIL);
}
private void setTransactionStatusText() {
switch (mTransactionDataHolder.getTransactionStatus()) {
case APPROVED:
if (mTransactionDataHolder.getRefundTransactions() != null) {
setHeaderStatusText(R.string.MPUTotal, mMposUiAppearance.getApprovedBackgroundColor(), mMposUiAppearance.getApprovedTextColor(), false);
} else if (mTransactionDataHolder.isCaptured()) {
setHeaderStatusText(R.string.MPUApproved, mMposUiAppearance.getApprovedBackgroundColor(), mMposUiAppearance.getApprovedTextColor(), false);
} else {
setHeaderStatusText(R.string.MPUPreauthorized, mMposUiAppearance.getPreAuthorizedBackgroundColor(), mMposUiAppearance.getPreAuthorizedTextColor(), false);
}
if (mTransactionDataHolder.getTransactionType() == TransactionType.REFUND) {
setHeaderStatusText(R.string.MPURefunded, mMposUiAppearance.getRefundedBackgroundColor(), mMposUiAppearance.getRefundedTextColor(), false);
}
break;
case DECLINED:
setHeaderStatusText(R.string.MPUDeclined, mMposUiAppearance.getDeclinedBackgroundColor(), mMposUiAppearance.getDeclinedTextColor(), true);
break;
case ABORTED:
setHeaderStatusText(R.string.MPUAborted, mMposUiAppearance.getDeclinedBackgroundColor(), mMposUiAppearance.getDeclinedTextColor(), false);
break;
case ERROR:
setHeaderStatusText(R.string.MPUError, mMposUiAppearance.getDeclinedBackgroundColor(), mMposUiAppearance.getDeclinedTextColor(), false);
break;
case INCONCLUSIVE:
setHeaderStatusText(R.string.MPUInconclusive, mMposUiAppearance.getDeclinedBackgroundColor(), mMposUiAppearance.getDeclinedTextColor(), false);
break;
case INITIALIZED:
case PENDING:
setHeaderStatusText(R.string.MPUUnknown, mMposUiAppearance.getDeclinedBackgroundColor(), mMposUiAppearance.getDeclinedTextColor(), false);
break;
}
}
private void setHeaderStatusText(@StringRes int statusTextResource, int backgroundColor, int textColor, boolean showInformationText) {
mTransactionStatusView.setText(statusTextResource);
mHeaderViewContainer.setBackgroundColor(backgroundColor);
mTransactionStatusView.setTextColor(textColor);
mTransactionStatusInformationView.setTextColor(textColor);
mAmountView.setTextColor(textColor);
if (showInformationText) {
showTransactionStatusInformation();
}
}
private void showTransactionStatusInformation() {
TransactionStatusDetailsCodes code = mTransactionDataHolder.getTransactionStatusDetailsCode();
String information = mLocalizationToolbox.informationForTransactionStatusDetailsCode(code);
if (information == null) {
return;
}
mTransactionStatusInformationView.setVisibility(View.VISIBLE);
mTransactionStatusInformationView.setText(information);
}
private void setupDividers() {
if (mSubjectView.getVisibility() == View.GONE) {
mSchemeAccNoViewDivider.setVisibility(View.GONE);
}
if (mSchemeView.getVisibility() == View.GONE && mSubjectView.getVisibility() == View.GONE) {
mHeaderViewDivider.setVisibility(View.GONE);
}
}
private String getTransactionIdentifierForSendingAndPrinting() {
RefundTransactionDataHolder refundTransaction = mTransactionHistoryHelper.getLatestApprovedRefundTransaction();
if (refundTransaction != null) {
return refundTransaction.getTransactionIdentifier();
} else {
return mTransactionDataHolder.getTransactionIdentifier();
}
}
}