All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.client.billing.Transaction Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ Platform.
 * Copyright (C) 2000-2013, 2016, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-client 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-client.  If not, see .
 */
package com.aoindustries.aoserv.client.billing;

import com.aoindustries.aoserv.client.AOServConnector;
import com.aoindustries.aoserv.client.CachedObjectIntegerKey;
import com.aoindustries.aoserv.client.account.Account;
import com.aoindustries.aoserv.client.account.Administrator;
import com.aoindustries.aoserv.client.account.User;
import com.aoindustries.aoserv.client.payment.Payment;
import com.aoindustries.aoserv.client.payment.PaymentType;
import com.aoindustries.aoserv.client.payment.Processor;
import com.aoindustries.aoserv.client.schema.AoservProtocol;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.collections.IntList;
import com.aoindustries.io.stream.StreamableInput;
import com.aoindustries.io.stream.StreamableOutput;
import com.aoindustries.math.SafeMath;
import com.aoindustries.sql.SQLUtility;
import com.aoindustries.sql.UnmodifiableTimestamp;
import com.aoindustries.util.InternUtils;
import com.aoindustries.util.i18n.Money;
import com.aoindustries.validation.ValidationException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Each {@link Account} has an account of all the
 * charges and payments processed.  Each entry in this
 * account is a Transaction.
 *
 * @see  Account
 *
 * @author  AO Industries, Inc.
 */
final public class Transaction extends CachedObjectIntegerKey {

	static final int
		COLUMN_TRANSID = 1,
		COLUMN_ACCOUNTING = 2,
		COLUMN_SOURCE_ACCOUNTING = 3,
		COLUMN_ADMINISTRATOR = 4
	;
	static final String COLUMN_TIME_name = "time";
	static final String COLUMN_TRANSID_name = "transid";
	static final String COLUMN_SOURCE_ACCOUNTING_name = "source_accounting";

	/**
	 * Represents not being assigned for a field of the int type.
	 */
	public static final int UNASSIGNED = -1;

	private UnmodifiableTimestamp time;
	private Account.Name accounting;
	private Account.Name source_accounting;
	private User.Name username;
	private String type;
	private String description;

	/**
	 * The quantity in 1000th's of a unit
	 */
	private int quantity;

	private Money rate;

	private String payment_type, payment_info, processor;
	private int creditCardTransaction;

	/**
	 * Payment confirmation.
	 */
	public static final byte WAITING_CONFIRMATION = 0, CONFIRMED = 1, NOT_CONFIRMED = 2;

	/**
	 * The text to display for different confirmation statuses.
	 */
	private static final String[] paymentConfirmedLabels = { "Pending", "Confirmed", "Failed" };

	public static final int NUM_PAYMENT_CONFIRMATION_STATES = 3;
	static {
		assert paymentConfirmedLabels.length == NUM_PAYMENT_CONFIRMATION_STATES;
	}

	private byte payment_confirmed;

	/**
	 * @param  paymentInfo  (Optional) The card info may have been updated during the transaction.
	 */
	public void approved(final int creditCardTransaction, final String paymentInfo) throws IOException, SQLException {
		table.getConnector().requestUpdate(
			true,
			AoservProtocol.CommandID.TRANSACTION_APPROVED,
			new AOServConnector.UpdateRequest() {
				IntList invalidateList;

				@Override
				public void writeRequest(StreamableOutput out) throws IOException {
					out.writeCompressedInt(pkey);
					out.writeCompressedInt(creditCardTransaction);
					out.writeNullUTF(paymentInfo);
				}

				@Override
				public void readResponse(StreamableInput in) throws IOException, SQLException {
					int code=in.readByte();
					if(code==AoservProtocol.DONE) invalidateList=AOServConnector.readInvalidateList(in);
					else {
						AoservProtocol.checkResult(code, in);
						throw new IOException("Unexpected response code: "+code);
					}
				}

				@Override
				public void afterRelease() {
					table.getConnector().tablesUpdated(invalidateList);
				}
			}
		);
	}

	/**
	 * @deprecated  Please provide updated cardInfo via {@link #approved(int, java.lang.String)}.
	 *
	 * @see  #approved(int, java.lang.String)
	 */
	@Deprecated
	public void approved(int creditCardTransaction) throws IOException, SQLException {
		approved(creditCardTransaction, null);
	}

	/**
	 * @param  paymentInfo  (Optional) The card info may have been updated during the transaction.
	 */
	public void declined(final int creditCardTransaction, final String paymentInfo) throws IOException, SQLException {
		table.getConnector().requestUpdate(
			true,
			AoservProtocol.CommandID.TRANSACTION_DECLINED,
			new AOServConnector.UpdateRequest() {
				IntList invalidateList;

				@Override
				public void writeRequest(StreamableOutput out) throws IOException {
					out.writeCompressedInt(pkey);
					out.writeCompressedInt(creditCardTransaction);
					out.writeNullUTF(paymentInfo);
				}

				@Override
				public void readResponse(StreamableInput in) throws IOException, SQLException {
					int code=in.readByte();
					if(code==AoservProtocol.DONE) invalidateList=AOServConnector.readInvalidateList(in);
					else {
						AoservProtocol.checkResult(code, in);
						throw new IOException("Unexpected response code: "+code);
					}
				}

				@Override
				public void afterRelease() {
					table.getConnector().tablesUpdated(invalidateList);
				}
			}
		);
	}

	/**
	 * @deprecated  Please provide updated cardInfo via {@link #declined(int, java.lang.String)}.
	 *
	 * @see  #declined(int, java.lang.String)
	 */
	@Deprecated
	public void declined(int creditCardTransaction) throws IOException, SQLException {
		declined(creditCardTransaction, null);
	}

	/**
	 * @param  paymentInfo  (Optional) The card info may have been updated during the transaction.
	 */
	public void held(final int creditCardTransaction, final String paymentInfo) throws IOException, SQLException {
		table.getConnector().requestUpdate(
			true,
			AoservProtocol.CommandID.TRANSACTION_HELD,
			new AOServConnector.UpdateRequest() {
				IntList invalidateList;

				@Override
				public void writeRequest(StreamableOutput out) throws IOException {
					out.writeCompressedInt(pkey);
					out.writeCompressedInt(creditCardTransaction);
					out.writeNullUTF(paymentInfo);
				}

				@Override
				public void readResponse(StreamableInput in) throws IOException, SQLException {
					int code=in.readByte();
					if(code==AoservProtocol.DONE) invalidateList=AOServConnector.readInvalidateList(in);
					else {
						AoservProtocol.checkResult(code, in);
						throw new IOException("Unexpected response code: "+code);
					}
				}

				@Override
				public void afterRelease() {
					table.getConnector().tablesUpdated(invalidateList);
				}
			}
		);
	}

	/**
	 * @deprecated  Please provide updated cardInfo via {@link #held(int, java.lang.String)}.
	 *
	 * @see  #held(int, java.lang.String)
	 */
	@Deprecated
	public void held(int creditCardTransaction) throws IOException, SQLException {
		held(creditCardTransaction, null);
	}

	/**
	 * @deprecated  Please directly access via {@link #getPayment()}.
	 *              Beware that {@link #getPayment()} might return {@code null}.
	 *
	 * @see  #getPayment()
	 * @see  Payment#getAuthorizationApprovalCode()
	 */
	@Deprecated
	public String getAprNum() throws SQLException, IOException {
		Payment cct = getPayment();
		return cct==null ? null : cct.getAuthorizationApprovalCode();
	}

	public Account.Name getAccount_name() {
		return accounting;
	}

	public Account getAccount() throws SQLException, IOException {
		Account business = table.getConnector().getAccount().getAccount().get(accounting);
		if (business == null) throw new SQLException("Unable to find Account: " + accounting);
		return business;
	}

	public Account.Name getSourceAccount_name() {
		return source_accounting;
	}

	public Account getSourceAccount() throws SQLException, IOException {
		Account business = table.getConnector().getAccount().getAccount().get(source_accounting);
		if (business == null) throw new SQLException("Unable to find Account: " + source_accounting);
		return business;
	}

	public User.Name getAdministrator_username() {
		return username;
	}

	public Administrator getAdministrator() throws SQLException, IOException {
		User un = table.getConnector().getAccount().getUser().get(username);
		// May be filtered
		if(un == null) return null;
		Administrator administrator = un.getAdministrator();
		if (administrator == null) throw new SQLException("Unable to find Administrator: " + username);
		return administrator;
	}

	@Override
	protected Object getColumnImpl(int i) {
		switch(i) {
			case 0: return time;
			case COLUMN_TRANSID: return pkey;
			case COLUMN_ACCOUNTING: return accounting;
			case COLUMN_SOURCE_ACCOUNTING: return source_accounting;
			case COLUMN_ADMINISTRATOR: return username;
			case 5: return type;
			case 6: return description;
			case 7: return quantity;
			case 8: return rate;
			case 9: return payment_type;
			case 10: return payment_info;
			case 11: return processor;
			case 12: return getPayment_id();
			case 13: return payment_confirmed == CONFIRMED ? "Y" : payment_confirmed == NOT_CONFIRMED ? "N" : "W";
			default: throw new IllegalArgumentException("Invalid index: " + i);
		}
	}

	public String getDescription() {
		return description;
	}

	public String getProcessor_providerId() {
		return processor;
	}

	public Processor getProcessor() throws SQLException, IOException {
		if(processor == null) return null;
		Processor creditCardProcessor = table.getConnector().getPayment().getProcessor().get(processor);
		if(creditCardProcessor == null) throw new SQLException("Unable to find CreditCardProcessor: " + processor);
		return creditCardProcessor;
	}

	public Integer getPayment_id() {
		return creditCardTransaction == -1 ? null : creditCardTransaction;
	}

	public Payment getPayment() throws SQLException, IOException {
		if (creditCardTransaction == -1) return null;
		Payment cct = table.getConnector().getPayment().getPayment().get(creditCardTransaction);
		if (cct == null) throw new SQLException("Unable to find CreditCardTransaction: " + creditCardTransaction);
		return cct;
	}

	public byte getPaymentConfirmed() {
		return payment_confirmed;
	}

	public static String getPaymentConfirmedLabel(int index) {
		return paymentConfirmedLabels[index];
	}

	public String getPaymentInfo() {
		return payment_info;
	}

	public String getPaymentType_name() {
		return payment_type;
	}

	public PaymentType getPaymentType() throws SQLException, IOException {
		if(payment_type == null) return null;
		PaymentType paymentType = table.getConnector().getPayment().getPaymentType().get(payment_type);
		if(paymentType == null) throw new SQLException("Unable to find PaymentType: " + payment_type);
		return paymentType;
	}

	/**
	 * Gets the effective amount of quantity * rate.
	 */
	public Money getAmount() {
		return rate.multiply(BigDecimal.valueOf(quantity, 3), RoundingMode.HALF_UP);
	}

	public int getQuantity() {
		return quantity;
	}

	public Money getRate() {
		return rate;
	}

	@Override
	public Table.TableID getTableID() {
		return Table.TableID.TRANSACTIONS;
	}

	public UnmodifiableTimestamp getTime() {
		return time;
	}

	public int getTransid() {
		return pkey;
	}

	public String getType_name() {
		return type;
	}

	public TransactionType getType() throws SQLException, IOException {
		TransactionType tt = table.getConnector().getBilling().getTransactionType().get(type);
		if (tt == null) throw new SQLException("Unable to find TransactionType: " + type);
		return tt;
	}

	@Override
	public void init(ResultSet result) throws SQLException {
		try {
			time = UnmodifiableTimestamp.valueOf(result.getTimestamp("time"));
			pkey = result.getInt("transid");
			accounting = Account.Name.valueOf(result.getString("accounting"));
			source_accounting = Account.Name.valueOf(result.getString("source_accounting"));
			username = User.Name.valueOf(result.getString("username"));
			type = result.getString("type");
			description = result.getString("description");
			quantity = SQLUtility.parseDecimal3(result.getString("quantity"));
			rate = MoneyUtil.getMoney(result, "rate.currency", "rate.value");
			payment_type = result.getString("payment_type");
			payment_info = result.getString("payment_info");
			processor = result.getString("processor");
			creditCardTransaction = result.getInt("credit_card_transaction");
			if(result.wasNull()) creditCardTransaction = -1;
			String typeString = result.getString("payment_confirmed");
			if("Y".equals(typeString)) payment_confirmed=CONFIRMED;
			else if("N".equals(typeString)) payment_confirmed=NOT_CONFIRMED;
			else if("W".equals(typeString)) payment_confirmed=WAITING_CONFIRMATION;
			else throw new SQLException("Unknown payment_confirmed '" + typeString + "' for transid=" + pkey);
		} catch(ValidationException e) {
			throw new SQLException(e);
		}
	}

	@Override
	public void read(StreamableInput in, AoservProtocol.Version protocolVersion) throws IOException {
		try {
			time = in.readUnmodifiableTimestamp();
			pkey = in.readCompressedInt();
			accounting = Account.Name.valueOf(in.readCompressedUTF()).intern();
			source_accounting = Account.Name.valueOf(in.readCompressedUTF()).intern();
			username = User.Name.valueOf(in.readCompressedUTF()).intern();
			type = in.readCompressedUTF().intern();
			description = in.readCompressedUTF();
			quantity = in.readCompressedInt();
			rate = MoneyUtil.readMoney(in);
			payment_type = InternUtils.intern(in.readNullUTF());
			payment_info = in.readNullUTF();
			processor = InternUtils.intern(in.readNullUTF());
			creditCardTransaction = in.readCompressedInt();
			payment_confirmed = in.readByte();
		} catch(ValidationException e) {
			throw new IOException(e);
		}
	}

	@Override
	public String toStringImpl() {
		return
			pkey
			+ "|"
			+ accounting
			+ '|'
			+ source_accounting
			+ '|'
			+ type
			+ '|'
			+ SQLUtility.formatDecimal3(quantity)
			+ '×'
			+ rate
			+ '|'
			+ (
				payment_confirmed == CONFIRMED ? 'Y'
				: payment_confirmed == NOT_CONFIRMED ? 'N'
				: 'W'
			)
		;
	}

	@Override
	public void write(StreamableOutput out, AoservProtocol.Version protocolVersion) throws IOException {
		if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_83_0) < 0) {
			out.writeLong(time.getTime());
		} else {
			out.writeTimestamp(time);
		}
		out.writeCompressedInt(pkey);
		out.writeCompressedUTF(accounting.toString(), 0);
		out.writeCompressedUTF(source_accounting.toString(), 1);
		out.writeCompressedUTF(username.toString(), 2);
		out.writeCompressedUTF(type, 3);
		out.writeCompressedUTF(description, 4);
		out.writeCompressedInt(quantity);
		if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_83_0) < 0) {
			if(rate != null && rate.getCurrency() == Currency.USD && rate.getScale() == 2) {
				out.writeCompressedInt(SafeMath.castInt(rate.getUnscaledValue()));
			} else {
				out.writeCompressedInt(-1);
			}
		} else {
			MoneyUtil.writeMoney(rate, out);
		}
		out.writeNullUTF(payment_type);
		out.writeNullUTF(payment_info);
		if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_29)<0) {
			out.writeNullUTF(null);
		} else {
			out.writeNullUTF(processor);
			out.writeCompressedInt(creditCardTransaction);
		}
		if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_0_A_128)<0) {
			out.writeCompressedInt(-1);
		} else if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_29)<0) {
			out.writeNullUTF(null);
		}
		out.writeByte(payment_confirmed);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy