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

Breakdown Transactions (Source Code)

Link to: header | source 2 | source 3 | transactions directory

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

Comments

CBreakdownTransaction

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

a breakdown transaction is a transaction which includes a list of details,
which are displayed in a breakdown table. Each line in the table is stored
in a separate breakdown entry object. The breakdown transaction keeps an
array of breakdowns that it contains.

SUPERCLASS = CTransaction

RELATED CLASSES: CBreakdownArrayOwner handles many breakdown functions.
CAccountTransaction, CExpenseTransaction, CReceiptTransaction, CTimeLog are
important subclasses. CBreakdownTable handles the actual table.

Constructor

/*********************************************************************************
constructor
*********************************************************************************/
CBreakdownTransaction::CBreakdownTransaction()
{
mAmount = 0;
}

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 CBreakdownTransaction::CopyFrom(DB_PersistentObject *source, const UInt8 copyFlags)
{
THE_SUPERCLASS::CopyFrom(source, copyFlags);

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

// we don't copy the payment array, since payments don't apply to the copy.

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

// copy from CBreakdownArrayOwner moved TCS 7/19/00
mBreakdownType = source->GetBreakdownType();
mBreakdownClassID = source->GetBreakdownClassID();

UInt8 type = src->GetBreakdownType();

// we don't copy breakdowns when it's a shallow copy
if (copyFlags & flag_shallowcopy) // TCS 7/19/00
return;

// only some breakdowns can be copied TCS 5/10/00, bugfix 4/4/01
if (CanCopyBreakdowns(type))
{
CopyBreakdowns(src, GetDBID()); // TCS rev 11/1/01
}
else
{ // fill in some data, if subclass allows it TCS 7/19/00 rev TCS 4/27/01
if (!CopySelectedBreakdowns(src))
{
// if we couldn't fill in breakdown, set to no breakdown
SetBreakdownType(breakdown_none);
}
}
}
/*********************************************************************************

GetFileLength

return the file length to reserve for this object

*********************************************************************************/
NeoSize CBreakdownTransaction::GetFileLength(const CNeoFormat *aFormat) const
{
return THE_SUPERCLASS::GetFileLength(aFormat) +
CBreakdownArrayOwner::GetFileLength(aFormat) +
ARRAY_FILE_SIZE(mPaymentArray) +
cFileLength;
}/*********************************************************************************

GetMemberValue

return the value of the member with the given tag

*********************************************************************************/
Boolean CBreakdownTransaction::GetMemberValue(const TagType aTag,
const TagType aType,
void *aValue) const
{
if (CBreakdownArrayOwner::GetMemberValue(aTag, aType, aValue))
return true;

DBClass accountClass;
DBid accountID;
switch (aTag)
{
case tag_amount:
return ConvertMember(&mAmount, type_money, aValue, aType);
break;

case tag_taxratepercent: // TCS 2/12/01
{
CMoney percent = GetTaxPercent();
return ConvertMember(&percent, type_percent, aValue, aType);
}
break;

case tag_paidbyclass: // we calculate payment transaction info
case tag_datepaid: // these apply to expenses and income TCS 2/15/01
case tag_paidbyrefnum:
case tag_paidbyamount:
case tag_paidbyaccount:
case tag_paidbytrans:
{
DBClass pmtClass = GetPaymentTransactionClass();
DBid pmtID = GetPaymentTransactionID();

if (aTag == tag_paidbyclass)
return ConvertEnumMember(pmtClass, MENU_IOTransactionTypes, aValue, aType);
else if (aTag == tag_paidbytrans)
return ConvertObjectIDMember(pmtID, pmtClass, aValue, aType);
else
{
if (pmtClass && pmtID)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
CTransaction *payment = TCS_SAFE_CAST(gDBFile->GetOneObject(pmtClass, pmtID),
CTransaction);

if (payment)
{
DB_ObjectWatcher watcher(payment);

if (aTag == tag_datepaid)
{
CDate date = payment->GetDate();
return ConvertMember(&date, type_date, aValue, aType);
}
else if (aTag == tag_paidbyaccount) // TCS 2/21/01
{
if (DB_ClassDescriptor::IsBankTransaction(pmtClass))
{
accountClass = payment->GetBankAccountClass();
accountID = payment->GetBankAccountID();
}
else
{
accountClass = payment->GetMainAccountClass();
accountID = payment->GetMainAccountID();
}
return ConvertObjectIDMember(accountID, accountClass, aValue, aType);
}
else if (aTag == tag_paidbyamount) // TCS 2/20/01
{
CMoney amount = payment->GetAmount();
return ConvertMember(&amount, type_money, aValue, aType);
}
else
{
CTextString refNumString = payment->GetRefNumString();
return ConvertMember(&refNumString, type_cstring, aValue, aType);
}
}
}
// if we get this far, there is no payment
if (aTag == tag_datepaid)
{
CDate never = CDate::Never();
return ConvertMember(&never, type_date, aValue, aType);
}
else if (aTag == tag_paidbyaccount) // TCS 2/21/01
{ // we just return zero or blank
return ConvertObjectIDMember(0, id_CheckingAccount, aValue, aType);
}
else if (aTag == tag_paidbyamount) // TCS 2/20/01
{
CMoney zeroDollars = 0;
return ConvertMember(&zeroDollars, type_money, aValue, aType);
}
else
{
CTextString blankString;
blankString.MakeNull();
return ConvertMember(&blankString, type_cstring, aValue, aType);
}
}

}
break;

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

SetMemberValue

set the value of the member with the given tag

*********************************************************************************/
Boolean CBreakdownTransaction::SetMemberValue(const TagType aTag,
const TagType aType,
const void *aValue)
{
if (CBreakdownArrayOwner::SetMemberValue(aTag, aType, aValue))
return true;
else switch (aTag)
{
case tag_amount:
return ConvertMember(aValue, aType, &mAmount, type_money);
break;

case tag_taxratepercent: // calculated, no need to set
case tag_paidbyclass: // TCS 12/15/03
case tag_datepaid:
case tag_paidbyrefnum:
case tag_paidbyamount:
case tag_paidbyaccount:
case tag_paidbytrans:
return true;
break;

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

ReadObject

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

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

THE_SUPERCLASS::ReadObject(aStream, aTag);

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

CBreakdownArrayOwner::ReadObject(aStream, aTag);

ReadInfoArrayFromStream(aStream, mPaymentArray, cHasSafetyTag);


/// aStream->ReadChunk(&mAmount, cFileLength);
mAmount.ReadFromStream(aStream); // mfs_sa rev 20feb2k3
}/*********************************************************************************

WriteObject

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

CNeoDebugExport checker(aStream, this);

THE_SUPERCLASS::WriteObject(aStream, aTag);
CBreakdownArrayOwner::WriteObject(aStream, aTag);

WriteInfoArrayToStream(aStream, mPaymentArray, cHasSafetyTag);

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

mAmount.WriteToStream(aStream); // mfs_sa rev 20feb2k3
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

IsValidBreakdown

return whether the passed breakdown type is valid. Subclasses that allow
other breakdown types should override.
*********************************************************************************/
Boolean CBreakdownTransaction::IsValidBreakdown(const UInt8 breakdownType)
{
return CBreakdownTransaction::UsesBreakdown(GetDBClassID(), breakdownType);
}
/*********************************************************************************

UsesBreakdown (static) TCS 6/23/99 rev 1/23/00

return whether a given class includes the given breakdown class.
This static method replaces the former IsValidBreakdown methods which were
scattered thru each transaction object.

For another reference that links breakdown types to breakdown class id's,
see CBreakdownArrayOwner::GetBreakdownEntryClass

*********************************************************************************/
Boolean CBreakdownTransaction::UsesBreakdown(const DBid classID, const DBid breakdownType,
const Boolean inPrintForm)
{
switch (breakdownType)
{
case breakdown_none: // the 'none' breakdown is always valid TCS 6/28/99
return true;
break;
case breakdown_category: // this is the generic form that covers
switch (classID) // all cat and item breakdowns
{
case id_Allowance: // TCS 3/14/02
case id_BankPayment:
case id_BankCheck:
case id_Bid:
case id_ChangeOrder:
case id_CostTransfer:
case id_EquipmentHours:
case id_Estimate:
case id_InventoryTransfer:
case id_InventoryUsed:
case id_LaborHours:
case id_OtherCost:
case id_MaterialPurchase:
case id_PurchaseWorkOrder:
case id_Sale:
case id_SubcontractorLog:
return true;
break;

default:
return false;
break;
}
break;

case breakdown_estcategory: // TCS 7/22/99
case breakdown_estitem:
return (classID == id_Estimate);
break;

case breakdown_costcategory: // TCS 7/22/99
case breakdown_costitem:
switch (classID)
{
case id_Allowance: // bugfix TCS 4/25/01
case id_Bid:
case id_ChangeOrder:
case id_CostTransfer:
case id_InventoryUsed:
case id_OtherCost:
case id_MaterialPurchase:
case id_PurchaseWorkOrder:
//case id_SubcontractorLog:
return true;
break;

default:
return false;
break;
}
break;

case breakdown_laborcategory: // TCS 1/6/00
case breakdown_laboritem:
switch (classID)
{
case id_EquipmentHours:
case id_LaborHours:
case id_SubcontractorLog:
return true;
break;

default:
return false;
break;
}
break;

case breakdown_salecategory: // TCS 7/22/99
return (classID == id_Sale);
break;

case breakdown_saleitem:
return (classID == id_Sale);
break;

case breakdown_inventoryitem: // TCS 1/7/01
return (classID == id_InventoryTransfer || classID == id_Manufacture);
break;

case breakdown_rental:
case breakdown_draw:
case breakdown_progress:
case breakdown_timeandmaterials:
case breakdown_allowance:
case breakdown_changeorder:
case breakdown_chargeback:
return (classID == id_BillingRecord);
break;

case breakdown_payment:
return (classID == id_BankDeposit || classID == id_PaymentReceipt ||
classID == id_BillingRecord || classID == id_BillingStatement); // bugfix TCS 12/24/99
break;

case breakdown_payrollrecord:
case breakdown_purchase:
return (classID == id_BankCheck || classID == id_BankPayment ||
(inPrintForm && DB_ClassDescriptor::IsBankTransaction(classID))); // TCS 11/8/00
break;

case breakdown_wages:
case breakdown_deduction:
case breakdown_commission:
case breakdown_benefit:
return (classID == id_PayrollRecord ||
(inPrintForm && (classID == id_BankCheck || // rev TCS 1/23/00
classID == id_BankPayment ||
DB_ClassDescriptor::IsBankTransaction(classID)))); // TCS 11/8/00
break;

case breakdown_categorytax:
case breakdown_vacation:
case breakdown_employertax:
return (classID == id_PayrollRecord); // TCS 11/8/00
break;

case breakdown_payrolltax:
case breakdown_salestax:
case breakdown_vendorwithholding:
return (classID == id_OtherCost || classID == id_EscrowTransaction); // rev TCS 10/21/02
break;

case breakdown_task: // TCS 12/9/03
return classID == id_Inspection;
break;

default:
return false;
break;
}
}
/*********************************************************************************

IsValidBreakdownClass (static)

return whether the given ID is a valid breakdown owner

*********************************************************************************/
Boolean CBreakdownTransaction::IsValidBreakdownClass(const DBid inClassID)
{
switch (inClassID)
{
case id_Allowance: // TCS 3/14/02
case id_Assembly:
case id_Bid:
case id_BillingRecord:
case id_BillingStatement: // TCS 12/22/03
case id_ChangeOrder:
case id_CostTransfer:
case id_EquipmentHours:
case id_Estimate:
case id_Inspection: // TCS 12/22/03
case id_InventoryTransfer:
case id_InventoryUsed:
case id_LaborHours:
case id_Manufacture: // TCS 12/22/03
case id_MaterialPurchase:
case id_OtherCost:
case id_PaymentReceipt:
case id_PayrollRecord:
case id_PurchaseWorkOrder:
case id_Sale:
case id_SubcontractorLog:
return true;
break;

case id_CashTransaction: // rev TCS 6/10/00
case id_CheckingTransaction:
case id_CreditCardTransaction:
case id_EscrowTransaction:
case id_InvestmentTransaction:
case id_LoanTransaction:
case id_SavingsTransaction:
return true;
break;

default:
return false;
break;
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

RemoveFromDatabase

remove this object from the db. We override since we also need to dispose of
any breakdown items associated with this object

*********************************************************************************/
Boolean CBreakdownTransaction::RemoveFromDatabase(const UInt32 clientID)
{
// do we have any breakdowns?
if (HasBreakdowns()) // bugfix TCS 6/14/02
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

DB_ObjectTempRemover remover (this); // TCS 8/26/03

if (remover.WasRemoved())
RemoveAllBreakdownEntries();
}
// now let the superclass do the actual deletion of this object
return THE_SUPERCLASS::RemoveFromDatabase(clientID);
}
/*********************************************************************************

ExportBreakdowns TCS 8/21/03

export breakdowns for this item

*********************************************************************************/
void CBreakdownTransaction::ExportBreakdowns(CTextOutputStream &stream)
{
CDataTranslator::ExportRecordArray(GetBreakdownClassID(), mBreakdownArray, stream);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

MatchesFindCriteria TCS 12/17/01 rev 12/21/01

return whether this object matches the given find criteria. selectorArray
must be an array of pointers to selector objects.

We override completely, since we need to check for table criteria
*********************************************************************************/
Boolean CBreakdownTransaction::MatchesFindCriteria(CTCS_Array &selectorArray,
const Boolean matchAny, const SInt32 selectorCount) const
{
DB_MemberSelector *selector = nil;
CTCS_ArrayIterator iterator (selectorArray);

Boolean matchesThis;
Boolean hasCheckedTable = false;

// loop through each selector, and see where it came from
while (iterator.Next(&selector))
{
TCS_FailNILMsg(selector, TCS_GetErrString(errID_BadSelector));

if (selector->IsFromTable())
{
if (!hasCheckedTable)
{
matchesThis = BreakdownMatchesSelectors(selectorArray, matchAny, selectorCount);
hasCheckedTable = true;
}
}
else
matchesThis = MatchesSelector(selector);

if (matchesThis)
{ // for 'any match', we exit at the first match. However we
// skip group class tags (so it's possible to find two specific
// accounts in an 'any match' search) TCS rev 1/11/03
if (matchAny && (selectorCount < 3 || !DB_ClassDescriptor::ControlsMultipleClasses(selector->getSelectTag())))
return true;
}
else
{ // for regular find, we exit at the first non-match
if (!matchAny)
return false;
}
}

// if we get this far, the match status depends on the search type
if (matchAny)
return false; // nothing matched
else
return true; // nothing mis-matched
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

ImportMember

import the given member from a cstring TCS 8/21/98
*********************************************************************************/
SInt32 CBreakdownTransaction::ImportMember(const TagType tag,
const CTextString &inString)
{
switch (tag)
{
case tag_breakdown:
{ // for breakdown type, we need to do some special processing
UInt8 breakdownType = inString.ParseID();

// if we already have breakdown members, skip it
if (mBreakdownArray.GetCount() > 0)
return import_Problems;

// make sure this is a valid breakdown type
if (!IsValidBreakdown(breakdownType))
{
mBreakdownType = breakdown_none;
mBreakdownClassID = 0;
return import_Problems;
}
// looks OK, so update the breakdown member and breakdown class id
mBreakdownType = breakdownType;
mBreakdownClassID = DB_ClassDescriptor::GetBreakdownEntryClass(breakdownType); // TCS 4/20/00
return import_OK;
}
break;

default:
return THE_SUPERCLASS::ImportMember(tag, inString);
break;
}
}/*********************************************************************************

FinishBreakdownImport TCS 12/24/98 rev 8/21/00

do any finish work needed after importing a breakdown. This is the place to
do any tidying. As a default, we set some data in the breakdown object
(we do this mainly to fill in data that might be missing due to changes in
breakdown data composition as of 8/21/00).
*********************************************************************************/
void CBreakdownTransaction::FinishBreakdownImport(DB_PersistentObject *breakdown)
{
DBid mainAccountID = 0;
if (breakdown->GetMemberValue(tag_mainaccount, type_objectid, &mainAccountID))
{
if (!mainAccountID)
breakdown->SetMemberValue(tag_mainaccount, type_objectid, &mMainAccount);
}

DBClass mainAccountClass = 0;
if (breakdown->GetMemberValue(tag_mainaccountclass, type_objclass, &mainAccountClass))
{
if (!mainAccountClass)
breakdown->SetMemberValue(tag_mainaccount, type_objclass, &mMainAccountClass);
}

CDate date; // TCS 8/23/00
if (breakdown->GetMemberValue(tag_date, type_date, &date))
{
if (!date.IsValidDate())
{
date = GetDate();
breakdown->SetMemberValue(tag_date, type_date, &date); // bugfix TCS 2/8/01
}
}
}
/*********************************************************************************

FillMemberFromAnother TCS 1/3/03

if substituting breakdown type, we also add breakdowns from the source.

Right now this happens only for sales.
*********************************************************************************/
Boolean CBreakdownTransaction::FillMemberFromAnother(DB_PersistentObject *source,
const TagType memberTag,
const MemberType memberType)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, FillMemberFromAnother called while in database!");

if (memberTag == tag_breakdown)
{
CBreakdownTransaction *src = TCS_SAFE_CAST(source, CBreakdownTransaction);
TCS_FailNILMsg(src, TCS_GetErrString(errID_BadTransaction));

UInt8 sourceType = src->GetBreakdownType();

if (sourceType == breakdown_none)
{
mBreakdownType = breakdown_none;
mBreakdownClassID = 0;

RemoveAllBreakdownEntries(); // TCS 1/23/03
return true;
}
else if (CanCopyBreakdowns(sourceType))
{
// only some breakdowns can be copied
mBreakdownType = sourceType;
mBreakdownClassID = src->GetBreakdownClassID();

RemoveAllBreakdownEntries(); // TCS 1/23/03
CopyBreakdowns(src, GetDBID());
return true;
}
else
return false;
}
else
{
return THE_SUPERCLASS::FillMemberFromAnother(source, memberTag, memberType);
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

AddBreakdown moved TCS 5/9/01

add a breakdown to the breakdown array. This routine doesn't check
whether the id is valid for the current breakdown type; the caller
is expected to ensure that
*********************************************************************************/
void CBreakdownTransaction::AddBreakdown(const DBid breakdownID)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, AddBreakdown called while in database!"); // TCS 5/9/01
mBreakdownArray.Append(breakdownID);
}/*********************************************************************************

RemoveAllBreakdownEntries TCS added param 4/20/00, moved 5/9/01

remove all the breakdowns.

WARNING- because this method changes an object's file length, it should only be
used on an object that has been removed from the database.

*********************************************************************************/
void CBreakdownTransaction::RemoveAllBreakdownEntries(const Boolean removeFromDatabase)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, RemoveAllBreakdownEntries called while in database!"); // TCS 5/9/01
ClearBreakdownArray(&mBreakdownArray, GetBreakdownClassID(), removeFromDatabase);
}