Accounting Software
Small Business Software Estimating Software
Inventory SoftwareInventory Tracking SoftwareInventory Control SoftwareInventory Management SoftwareConstruction Management SoftwareProject Management SoftwareBusiness Management Software

Payment Receipts (Source Code)

Link to: header | transactions directory

Copyright Turtle Creek Software 1996-2006. All Rights Reserved.

Comments

CPaymentReceipt

This class manages payment receipts for the Goldenseal accounting software,
small business management software, construction project management software and
construction accounting software.

Each record is a payment receipt. This class is totally optional-- it's only necessary
when the payee wants a printed receipt or there is some reason to use a
separate transaction to record payment (e.g. multiple payment methods).

This class actually has made income posting much more difficult, since it
interposes yet one more possible choice for payments.

SUPERCLASS = CReceiptTransaction

Constructor

/*********************************************************************************
constructor
*********************************************************************************/
CPaymentReceipt::CPaymentReceipt()
{
mTransactionRef = mMessage = 0;
mTransactionClass = id_BillingRecord;
mStatus = status_Received;
mPaymentMethod = method_cash; // eventually get this from prefs
mRevenueAccount = 0; // not used

mEndSafetyTag = tag_endsafetytag; // TCS 9/8/02
}

Source Code

/*********************************************************************************

CopyFrom

copy the data members from the passed object. This is used to implement
duplicate. source should be an object of the same class as this object

*********************************************************************************/
void CPaymentReceipt::CopyFrom(DB_PersistentObject *source, const UInt8 copyFlags)
{
THE_SUPERCLASS::CopyFrom(source, copyFlags);

CPaymentReceipt *src = TCS_SAFE_CAST(source, CPaymentReceipt);
TCS_FailNILMsg(src, TCS_GetErrString(errID_BadRecord));

TCS_BlockMove(&src->mMessage, &mMessage, cCopyFileLength);

if (HasLockedStatus()) // new TCS 2/2/99
mStatus = GetStarterStatus(); // rev TCS 3/8/00

mTransactionRef = 0; // TCS 4/27/01
}
/*********************************************************************************

GetFileLength

return the file length to reserve for this object

*********************************************************************************/
NeoSize CPaymentReceipt::GetFileLength(const CNeoFormat *aFormat) const
{
return THE_SUPERCLASS::GetFileLength(aFormat) +
cFileLength;
}
/*********************************************************************************

GetMemberValue

return the value of the member with the given tag

*********************************************************************************/
Boolean CPaymentReceipt::GetMemberValue(const TagType aTag, const TagType aType,
void *aValue) const
{
switch (aTag)
{
case tag_message:
return ConvertObjectIDMember(mMessage, id_Message, aValue, aType);
break;

case tag_transactionref:
if (DB_ClassDescriptor::IsTransaction(mTransactionClass))
return ConvertObjectIDMember(mTransactionRef, mTransactionClass,
aValue, aType);
else
return ConvertMember(&mTransactionRef, type_objectid, aValue, aType);
break;

case tag_status: // bugfix TCS 11/1/01
return ConvertEnumMember(mStatus, MENU_PaymentReceiptStatus, aValue, aType);
break;

case tag_transactionrefclass:
return ConvertEnumMember(mTransactionClass, MENU_PaymentReceiptConditions,
aValue, aType);
break;

case tag_breakdown:
return ConvertEnumMember(mBreakdownType, MENU_PmtReceiptBreakdown, aValue, aType);
break;

case tag_messagetext: // TCS 11/13/00
{
CTextString messageText = GetMessageText(mMessage);
return ConvertMember(&messageText, type_cstring, aValue, aType);
}
break;

case tag_name: // TCS 11/17/99
{
// if we have a valid transaction ref, fetch the name of the
// transaction being paid. Note that we may have a non-transaction
// class such as condition_OnAccount.
if (DB_ClassDescriptor::IsTransaction(mTransactionClass) && mTransactionRef)
{
CTextString itemName;

TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
DB_PersistentObject *source =
gDBFile->GetOneObject(mTransactionClass, mTransactionRef);
if (source)
{
DB_ObjectWatcher watcher(source);
itemName = source->GetName();
}
return ConvertMember(&itemName, type_cstring, aValue, aType);
}
else // no transaction, so let the superclass build the name TCS 3/2/01
return THE_SUPERCLASS::GetMemberValue(aTag, aType, aValue);
}
break;

case tag_undepositedfundsaccount: // currently a constant TCS 1/3/01
return ConvertObjectIDMember(account_UndepositedReceipts, id_UtilityAccount, aValue, aType);
break;

default:
return THE_SUPERCLASS::GetMemberValue(aTag, aType, aValue);
break;
}
}/*********************************************************************************

SetMemberValue

set the value of the member with the given tag

*********************************************************************************/
Boolean CPaymentReceipt::SetMemberValue(const TagType aTag, const TagType aType,
const void *aValue)
{
switch (aTag)
{
case tag_message:
return ConvertDataToObjectID(aValue, aType, &mMessage, id_Message);
break;

case tag_transactionrefclass:
return ConvertMember(aValue, aType, &mTransactionClass, type_objclass);
break;

case tag_status:
return ConvertMember(aValue, aType, &mStatus, type_enum);
break;

case tag_transactionref: // don't do ConvertDataToObjectID since
// it's not always a valid object class
return ConvertMember(aValue, aType, &mTransactionRef, type_objectid);
break;

case tag_undepositedfundsaccount:
case tag_messagetext: // no need to set TCS 11/13/00
return true;
break;

default:
return THE_SUPERCLASS::SetMemberValue(aTag, aType, aValue);
break;
}
}/*********************************************************************************

ReadObject

read the persistent object's data in from a stream
*********************************************************************************/
void CPaymentReceipt::ReadObject(CNeoStream *aStream, const TagType aTag)
{
TCS_FailNILMsg(aStream, TCS_GetErrString(errID_BadStream));

CNeoDebugImport checker(aStream, this, cCheckTooSmall); // TCS 2/24/00

THE_SUPERCLASS::ReadObject(aStream, aTag);

if (!IsIOValid()) // TCS 2/5/02
return;

/// aStream->ReadChunk(&mMessage, cFileLength);

mMessage = aStream->ReadID(); // mfs_sa rev 20feb2k3
mTransactionRef = aStream->ReadID();

mStatus = aStream->ReadChar();
mTransactionClass = aStream->ReadChar();

mEndSafetyTag = aStream->ReadEndSafetyTag(this);

if (!IsValidEndTag(mEndSafetyTag)) // TCS 9/8/02
ReportDamagedObject(GetDBClassID(), GetDBID());
}/*********************************************************************************

WriteObject

write the persistent object's data to a stream
*********************************************************************************/
void CPaymentReceipt::WriteObject(CNeoStream *aStream, const TagType aTag)
{
TCS_FailNILMsg(aStream, TCS_GetErrString(errID_BadStream));

// make sure we have valid data to write TCS 9/8/02
if (!IsValidEndTag(mEndSafetyTag))
{
ReportDamagedObject(GetDBClassID(), GetDBID());
mEndSafetyTag = tag_endsafetytag; // TCS 11/26/02
}

CNeoDebugExport checker(aStream, this, cCheckTooSmall);
THE_SUPERCLASS::WriteObject(aStream, aTag);

/// aStream->WriteChunk(&mMessage, cFileLength);

aStream->WriteID(mMessage); // mfs_sa rev 20feb2k3
aStream->WriteID(mTransactionRef);

aStream->WriteChar(mStatus);
aStream->WriteChar(mTransactionClass);

aStream->WriteEndSafetyTag(mEndSafetyTag, this);

}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

PostNewRecord TCS 11/21/03

post info for a new record
*********************************************************************************/
void CPaymentReceipt::PostNewRecord(const UInt8 creationType)
{
PostUseCount(id_Message, mMessage);

// let the superclass do any posting
THE_SUPERCLASS::PostNewRecord(creationType);
}
/*********************************************************************************

PostDeletion TCS 11/21/03

post info from a deleted record
*********************************************************************************/
void CPaymentReceipt::PostDeletion(const Boolean postAudit)
{
PostUseCount(id_Message, mMessage, cRemoveItem);

// let the superclass do any posting
THE_SUPERCLASS::PostDeletion(postAudit);
}
/*********************************************************************************

PostRecordActivated TCS 5/14/99

post info from a new or activated transaction
*********************************************************************************/
void CPaymentReceipt::PostRecordActivated(const UInt8 creationType)
{
// post payment info
PostPaymentReceived(mAmount);
UpdateJobCostIncome(mAmount);

PostUtilityAccounts(); // TCS 10/22/01

// let the superclass do any posting
THE_SUPERCLASS::PostRecordActivated(creationType);
}
/*********************************************************************************

PostRecordCancelled TCS 5/14/99

post info from a deleted or voided transaction. We need to cancel
the payment in the paid transaction
*********************************************************************************/
void CPaymentReceipt::PostRecordCancelled(const UInt8 cancelType)
{
// post payment info
PostPaymentReceived(-mAmount, cRemoveItem);
UpdateJobCostIncome(-mAmount, cRemoveItem);

PostUtilityAccounts(cRemoveItem); // TCS 10/22/01 rev 11/18/02

// let the superclass do any posting
THE_SUPERCLASS::PostRecordCancelled(cancelType);
}
/*********************************************************************************

PostRecordChanging TCS 5/14/99

a record will be changing. Post the change.
*********************************************************************************/
void CPaymentReceipt::PostRecordChanging(const Boolean accountChanging, const Boolean jobChanging)
{
// post payment info
PostPaymentReceived(-mAmount, cRemoveItem); // bugfix TCS 3/6/00
UpdateJobCostIncome(-mAmount, cRemoveItem);

PostUtilityAccounts(cRemoveItem); // TCS 10/22/01

PostUseCount(id_Message, mMessage, cRemoveItem); // TCS 11/21/03

THE_SUPERCLASS::PostRecordChanging(accountChanging, jobChanging);
}
/*********************************************************************************

PostRecordChanged TCS 1/5/98

a record has changed. Post it.
*********************************************************************************/
void CPaymentReceipt::PostRecordChanged(const CMoney &oldAmount, const Boolean accountChanged,
const Boolean jobChanged)
{
// post payment info
PostPaymentReceived(mAmount); // bugfix TCS 3/6/00
UpdateJobCostIncome(mAmount);

PostUtilityAccounts(); // TCS 10/22/01

PostUseCount(id_Message, mMessage); // TCS 11/21/03

THE_SUPERCLASS::PostRecordChanged(oldAmount, accountChanged, jobChanged);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

PostPaymentReceived TCS 5/14/99 rev 9/9/99 rev 3/3/00 rev 4/10/00

send out credit for an incoming payment. This also handles removal of
payments, when a transaction is voided or deleted. This is one of two places
where the chain of HandlePaymentReceived calls can start(the other is in deposits).
The chain can go many levels deep, with many variations depending on whether each
transaction uses a breakdown or a reference transaction.

Note that we just pass along inAmount at each step, since it's already negative
for a cancelled transaction(changed TCS 9/15/00)
*********************************************************************************/
void CPaymentReceipt::PostPaymentReceived(const CMoney &inAmount,
const Boolean removeItem)
{
// update the undeposited funds account moved & rev TCS 12/12/00
CUtilityAccount::AddToActiveArray(account_UndepositedReceipts, this, removeItem);

// let any breakdowns know know that a payment was received
if (mBreakdownType != breakdown_none)
{
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid breakdownID;
CBreakdownEntry *breakdownItem;

while (iterator.Next(breakdownID))
{
breakdownItem =
TCS_SAFE_CAST(gDBFile->GetOneObject(mBreakdownClassID, breakdownID),
CBreakdownEntry);
if (breakdownItem)
{
DB_ObjectWatcher watcher(breakdownItem);
breakdownItem->HandlePaymentReceived(inAmount, GetDBClassID(), GetDBID(), // bugfix TCS 8/29/01
id_PaymentReceipt, removeItem);
}
}
}
else if (mAmount.IsZero()) // if blank, no need to post
{
return;
}
else if (mTransactionClass == condition_OnAccount) // credit it 'on account'
{ // note that this may be an account
// or an owner added TCS 10/19/00
DB_PersistentObject *account = gDBFile->GetOneObject(mMainAccountClass,
mMainAccount);

if (account) // it's OK to not have account
{ // e.g. void transact(TCS 2/11/00)
DB_ObjectWatcher watcher(account);
account->PayOnAccount(inAmount);
account->AdjustBalance(-inAmount);
}
}
else
{
// no breakdown, so let the transaction ref know about the payment
CTransaction *transaction = nil;

if (DB_ClassDescriptor::IsTransaction(mTransactionClass) && mTransactionRef)
{
transaction = TCS_SAFE_CAST(gDBFile->GetOneObject(mTransactionClass,
mTransactionRef), CTransaction);
}

if (transaction) // transaction may not exist
{ // e.g. imported record
DB_ObjectWatcher watcher(transaction);

transaction->HandlePaymentReceived(inAmount, GetDBClassID(), GetDBID(),
id_PaymentReceipt, removeItem); // bugfix TCS 8/29/01
}
}
}
/*********************************************************************************

UpdateJobCostIncome TCS 4/3/00

update income for the job
*********************************************************************************/
void CPaymentReceipt::UpdateJobCostIncome(const CMoney &/*inAmount*/,
const Boolean removeItem)
{
// mark the transaction as paid
DB_Account *job = nil;

if (mMainAccount && mMainAccountClass)
{
job = TCS_SAFE_CAST(gDBFile->GetOneObject(mMainAccountClass,
mMainAccount), DB_Account);
}

if (job)
{
DB_ObjectWatcher watcher(job);

// removed TCS 5/9/01. We now post at the initial sale or billing record
//job->AddIncome(inAmount);

// removed TCS 4/30/01, restored 5/4/01. This used to be called
// in HandlePaymentReceived but that's now gone.
job->AddToIncomeArray(id_PaymentReceipt, GetDBID(), mTransactionClass, removeItem);
}
}
/*********************************************************************************

PostUtilityAccounts TCS 10/22/01

post info to revenue accounts
*********************************************************************************/
void CPaymentReceipt::PostUtilityAccounts(const Boolean removeIt)
{
DBid utilityAccount = 0;

if (GetPaymentType() == method_writeoff) // TCS 12/8/02
{
utilityAccount = account_Writeoffs;

// we also post writeoffs to the overhead account
PostWriteoff(removeIt);
}
else switch (mMainAccountClass)
{
case id_ProjectAccount:
utilityAccount = account_ProjectCashRevenue;
break;

case id_CustomerAccount:
utilityAccount = account_SalesCashRevenue;
break;

case id_Lease:
utilityAccount = account_RentalCashRevenue;
break;

case id_EmployeeAccount:
utilityAccount = account_LaborCashExpenses;
break;

case id_MaterialAccount:
utilityAccount = account_MaterialCashExpenses;
break;

case id_SubcontractorAccount:
utilityAccount = account_SubcontractorCashExpenses;
break;

case id_OtherCostAccount:
utilityAccount = account_OtherCashExpenses;
break;

case id_Owner:
utilityAccount = account_OwnerInvestments;
break;

default:
TCS_DebugAlert("Oops, bad case in CPaymentReceipt::PostUtilityAccounts!");
return;
break;
}

CUtilityAccount::AddToPermanentArray(utilityAccount, this, removeIt);
}
/*********************************************************************************

PostWriteoff TCS 12/8/02

post a payment that is just a writeoff. We also post it to the overhead
account.
*********************************************************************************/
void CPaymentReceipt::PostWriteoff(const Boolean /*removeIt*/)
{
CPaymentMethod *payMethod =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_PaymentMethod, mPaymentMethod),
CPaymentMethod);
if (payMethod)
{
DB_ObjectWatcher watcher (payMethod);
DBid accountID = payMethod->GetFeeAccount();

COverheadAccount *account =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_OverheadAccount, accountID),
COverheadAccount);
if (account)
{
DB_ObjectWatcher accountWatcher(account);

// not sure how we'll post this
}
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

GetAccountingAmount TCS 10/22/01

for refunds, we use a negative amount

*********************************************************************************/
CMoney CPaymentReceipt::GetAccountingAmount(const Boolean removeIt, const UInt8 type) const
{
if (type == account_LaborCashExpenses || type == account_MaterialCashExpenses ||
type == account_SubcontractorCashExpenses || type == account_OtherCashExpenses)
{
if (removeIt)
return GetAmount();
else
return -GetAmount();
}
else
{
if (removeIt)
return -GetAmount();
else
return GetAmount();
}
}
/*********************************************************************************

HandleDeposit TCS 9/14/99, rev TCS 11/18/99, rev 3/22/00 rev 6/8/00

deal with a deposit that references this transaction.

*********************************************************************************/
void CPaymentReceipt::HandleDeposit(const CMoney &inAmount, const UInt8 sourceClass,
const DBid sourceID, const Boolean removeItem,
const Boolean /*fromBillRecord*/)
{
// the superclass handles status, money and breakdown posting
THE_SUPERCLASS::HandleDeposit(inAmount, sourceClass, sourceID, removeItem);

// remove from the active array TCS 6/14/00
CUtilityAccount::AddToActiveArray(account_UndepositedReceipts, this, !removeItem);

// set the amount paid TCS 5/7/01
// inAmount will be negative for a cancelled deposit
SetAmountPaid(mAmountPaid + inAmount);

// also update the transaction that was paid
if (mBreakdownType == breakdown_none && mTransactionRef
&& DB_ClassDescriptor::IsTransaction(mTransactionClass)) // rev TCS 12/4/00
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
CTransaction *transaction =
TCS_SAFE_CAST(gDBFile->GetOneObject(mTransactionClass, mTransactionRef),
CTransaction);
if (transaction)
{
DB_ObjectWatcher watcher(transaction);
transaction->HandleDeposit(inAmount, sourceClass, sourceID, removeItem);
}
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

FinishBreakdownUpdate TCS 9/19/00

we override to fill in the payment method, since breakdowns don't store it on their own
*********************************************************************************/
void CPaymentReceipt::FinishBreakdownUpdate(CBreakdownEntry *breakdown, CBreakdownTable */*table*/)
{
TCS_FailNILMsg(breakdown, TCS_GetErrString(errID_BadBreakdown));

TCS_ASSERTMsg(breakdown->SetMemberValue(tag_paymentmethod, type_objectid, &mPaymentMethod),
TCS_SetValueErrString(tag_paymentmethod));
}
/*********************************************************************************

FillDepositInfo TCS 11/9/99

fill in information about the deposit of this payment receipt.
*********************************************************************************/
Boolean CPaymentReceipt::FillDepositInfo(SDepositInfo &depositInfo)
{
// skip void and deposited items. If we ever add more
// status choices, we may need to add them here.
if (mStatus == status_Void || mStatus == status_Deposited) // TCS 11/13/03
return false;

UInt8 paymentType = CPaymentMethod::GetPaymentType(mPaymentMethod);

// if this is a writeoff, we won't ever deposit it
if (paymentType == method_writeoff) // TCS 12/8/02
return false;

// fill in a struct
CSalesBranch::InitializeDepositInfo(depositInfo);
TCS_FailNILMsg(depositInfo.itemArray, TCS_GetErrString(errID_BadArray));

depositInfo.transactionID = GetDBID();
depositInfo.transactionClassID = id_PaymentReceipt;
depositInfo.accountID = mMainAccount; // rev TCS 8/9/02 (was 0)
depositInfo.accountClassID = mMainAccountClass;
CTextString payeeName = GetMainAccountName();

TCS_BufferFromText(depositInfo.itemName, payeeName);

depositInfo.dueAmount = GetAmount();
depositInfo.paidAmount = GetAmount();
depositInfo.paymentMethod = mPaymentMethod; // TCS 11/17/99

// fill in the payment type
switch (paymentType) // TCS rev 10/12/99
{
case method_cash:
depositInfo.cashAmount = depositInfo.paidAmount;
break;

case method_check:
depositInfo.checkAmount = depositInfo.paidAmount;
break;

case method_creditcard:
depositInfo.creditCardAmount = depositInfo.paidAmount;
break;

default:
depositInfo.otherAmount = depositInfo.paidAmount;
break;
}
return true;
}
/*********************************************************************************

FillDataReport TCS 9/6/02

fill in a diagnostic table that shows data field values.

*********************************************************************************/
void CPaymentReceipt::FillDataReport(CTCS_Table *table, CNeoStream *stream) const
{
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));
TCS_FailNILMsg(stream, TCS_GetErrString(errID_BadStream));

THE_SUPERCLASS::FillDataReport(table, stream);

FillFieldObjectIDRow(table, stream, tag_message, mMessage, id_Message);
FillFieldObjectIDRow(table, stream, tag_transactionref, mTransactionRef, mTransactionClass);

FillFieldEnumRow(table, stream, tag_status, mStatus, MENU_PaymentReceiptStatus);
FillFieldEnumRow(table, stream, tag_transactionrefclass, mTransactionClass, MENU_PaymentReceiptConditions);

FillEndSafetyTag(table, stream, mEndSafetyTag);
}