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

Breakdown Array Owners (Source Code)

Link to: header | transactions directory

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

Comments

CBreakdownArrayOwner

This class manages breakdowns for the Goldenseal estimating software,
small business management software, construction project management software and
construction estimating software.

It owns and manages a breakdown array. Several unrelated persistent
classes may manage a breakdown array, so this is a separate mix-in class.

SUPERCLASS = none

******************

Related Classes: CBreakdownTable is the table that appears in the layout
CBreakdownTransaction is the data object for most breakdowns
CBreakdownTransViewer is the displaying viewer for most breakdowns
CAssembly and CAssemblyViewer also contain breakdowns

Constructor

/*********************************************************************************
constructor
*********************************************************************************/
CBreakdownArrayOwner::CBreakdownArrayOwner()
{
mBreakdownType = breakdown_none;
mBreakdownClassID = 0;
mHasNewBreakdowns = true;
}

Source Code

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

GetMemberValue

return the value of the member with the given tag

*********************************************************************************/
Boolean CBreakdownArrayOwner::GetMemberValue(const TagType aTag,
const TagType aType,
void *aValue) const
{
switch (aTag)
{
case tag_breakdown:
return DB_PersistentObject::ConvertMember(&mBreakdownType, type_enum,
aValue, aType);
break;

case tag_breakdownclass:
return DB_PersistentObject::ConvertMember(&mBreakdownClassID, type_objclass,
aValue, aType);
break;

case tag_breakdowncount: // TCS 9/10/00
{
SInt32 count = GetBreakdownCount();
return DB_PersistentObject::ConvertMember(&count, type_long, aValue, aType);
}
break;

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

SetMemberValue

set the value of the member with the given tag

*********************************************************************************/
Boolean CBreakdownArrayOwner::SetMemberValue(const TagType aTag,
const TagType aType,
const void *aValue)
{
switch (aTag)
{
case tag_breakdown:
return DB_PersistentObject::ConvertMember(aValue, aType,
&mBreakdownType, type_enum);
break;

case tag_breakdownclass:
return DB_PersistentObject::ConvertMember(aValue, aType,
&mBreakdownClassID, type_objclass);
break;

case tag_breakdowncount: // calculated, no need to set
return true;
break;

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

ReadObject

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

ReadIDArrayFromStream(aStream, mBreakdownArray, cHasSafetyTag);

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

mBreakdownType = aStream->ReadChar(); // mfs_sa rev 20feb2k3
mBreakdownClassID = aStream->ReadChar();
}/*********************************************************************************

WriteObject

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

WriteIDArrayToStream(aStream, mBreakdownArray, cHasSafetyTag);

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

aStream->WriteChar(mBreakdownType ); // mfs_sa rev 20feb2k3
aStream->WriteChar(mBreakdownClassID);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

HasBreakdown TCS 8/20/98

return whether the given breakdown id is already included in this items' array
*********************************************************************************/
Boolean CBreakdownArrayOwner::HasBreakdown(const DBid breakdownID)
{
return mBreakdownArray.ContainsItem(breakdownID);
}
/*********************************************************************************

ClearBreakdownArray

clear the contents of a breakdown array

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. If you use this method
directly, first check that the object is not in the database. Right now this
method is only called from RemoveAllBreakdownEntries, which does check.

Note that we can't assert for database status here, since this is only a mix-in
class, and doesn't know about that.
*********************************************************************************/
void CBreakdownArrayOwner::ClearBreakdownArray(TObjectIDArray *breakdownArray,
const DBid breakdownClassID,
const Boolean removeFromDatabase)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// set up
DB_PersistentObject *entry = nil;
TObjectIDArrayIterator iterator(*breakdownArray); // rev TCS 11/28/00
DBid entryID;

// loop thru and remove each breakdown entry
while (iterator.Next(entryID))
{
TCS_TRY
{
// fetch the entry object
entry = gDBFile->GetOneObject(breakdownClassID, entryID);

if (entry)
{
DB_ObjectWatcher watcher(entry);

// we no longer post here- it's now handled by CBreakdownTransaction
// instead, via PostRecordCancelled in the breakdown entry TCS 4/17/00

// permanently remove it from the database rev TCS 4/20/00
// We don't need the menu & MU updating of FullyRemoveObject,
// but we do flag the deleted bit so it won't stop for errors.
if (removeFromDatabase)
{
entry->SetBeingDeleted(true); // TCS 10/23/03
gDBFile->TempRemoveObject(entry);
}
}
}
TCS_CATCH
{ // something is wrong but let's continue
}
}
// now clean up the array and set it dirty
breakdownArray->RemoveAllItems();
}/*********************************************************************************

DoForEachEntry

apply a function to each entry in the breakdown array
*********************************************************************************/
void CBreakdownArrayOwner::DoForEachEntry(EntryFunc func, void *param1,
void *param2, void *param3,
void *param4)
{
DB_PersistentObject *entry = nil;
TObjectIDArrayIterator iterator(mBreakdownArray); // rev TCS 11/28/00
DBid breakdownClassID = GetBreakdownClassID(),
entryID;
while (iterator.Next(entryID))
{
DB_PersistentObject *entry =
gDBFile->GetOneObject(breakdownClassID, entryID);
TCS_FailNILMsg(entry, TCS_GetErrString(errID_BadArray));

DB_ObjectWatcher watcher(entry);
(*func)(entry, param1, param2, param3, param4);
}
}
#if CAN_USE_MARK
#pragma mark -
#endif/*********************************************************************************

UpdateBreakdownsFromTable renamed TCS 4/17/00

create or update breakdowns from the breakdown table.
*********************************************************************************/
void CBreakdownArrayOwner::UpdateBreakdownsFromTable(CBreakdownTable *table,
const SInt32 breakdownType,
const DBid breakdownClassID,
DB_PersistentObject *owner)
{
// fill in breakdown entry objects using data from the table
switch (breakdownType)
{
case breakdown_estitem: // item breakdowns must have an item, but amount can be zero
case breakdown_costitem:
case breakdown_saleitem:
case breakdown_laboritem:
case breakdown_inventoryitem:
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, false, cAllowOffCheckmark);
break;

case breakdown_estcategory: // cat breakdowns can have blank name, but amount must be > 0
case breakdown_costcategory:
case breakdown_salecategory:
case breakdown_laborcategory:
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
false, cAllowBlankItem, cAllowOffCheckmark);
break;

case breakdown_purchase: // transaction breakdowns can have blank name or zero amount,
case breakdown_payment: // but they can't have a blank checkmark(rev TCS 10/23/01)
case breakdown_rental:
case breakdown_payrollrecord:
case breakdown_payrolltax: // TCS 6/21/99
case breakdown_salestax:
case breakdown_vendorwithholding:
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, cAllowBlankItem, false);
break;

case breakdown_draw: // project billing can have blank items or zero amt
case breakdown_progress: // or a blank checkmark TCS rev 9/7/99
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, cAllowBlankItem, cAllowOffCheckmark);
break;

case breakdown_allowance: // checkmarks are required TCS split 10/24/01
case breakdown_changeorder:
case breakdown_timeandmaterials:
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, cAllowBlankItem, false);
break;

case breakdown_assembly: // assemblies can have zero quantity but can't be blank
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, false, cAllowOffCheckmark);
break;

case breakdown_task: // inspections can have anything TCS 12/9/03
FillBreakdownEntriesFromTable(table, mBreakdownArray, breakdownClassID, owner,
cAllowZeroAmount, cAllowBlankItem, cAllowOffCheckmark);

case breakdown_none:
break;

default:
// don't know anything about other breakdowns
TCS_DebugAlert("Oops, attempted to read from an unknown breakdown type!");
break;
}
}/*********************************************************************************

FillBreakdownEntriesFromTable

create or update breakdowns objects from a breakdown table
*********************************************************************************/
void CBreakdownArrayOwner::FillBreakdownEntriesFromTable(CBreakdownTable *table,
TObjectIDArray &breakdownArray,
const DBid breakdownClassID,
DB_PersistentObject *owner,
const Boolean allowZeroAmount,
const Boolean allowBlankName,
const Boolean allowBlankCheckmark)
{
// sanity check
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// get the number of rows in the table and number of breakdowns
SInt32 numRows = table->GetNumRows(),
numBreakdowns = breakdownArray.GetCount();

// more prep work
SInt32 breakdownsCompleted = 0, // rev TCS 9/7/99
breakdownsDeleted = 0, // TCS 9/25/01
amountTestCol = table->GetMemberCol(table->GetAmountTestTag()),
nameTestCol = table->GetMemberCol(table->GetNameTestTag()),
checkmarkTestCol = table->GetMemberCol(table->GetCheckmarkTestTag()),
row;
DBid ownerID = owner ? owner->GetDBID() : 0;
DBClass ownerClassID = owner ? owner->GetDBClassID() : 0;

DBid ownerMainAccountID = owner ? owner->GetMainAccountID() : 0; // TCS 8/21/00
DBClass ownerMainAccountClass = owner ? owner->GetMainAccountClass() : 0;
CDate ownerDate = owner ? owner->GetDate() : CDate::Today(); // TCS 8/23/00

CBreakdownEntry *breakdown = nil; // nil added TCS 3/10/99, moved 9/7/99
CMoney oldAmount, currentAmount;
CTextString cellText;
DBid cellValue;
Boolean rowIsValid, isNewBreakdown;
DBid id;

// if we don't have the proper table columns, exit now.
if ((!allowZeroAmount && !amountTestCol) || (!allowBlankName && !nameTestCol) ||
(!allowBlankCheckmark && !checkmarkTestCol)) // rev TCS 2/23/00
{
return;
}

CProgressBar *progressBar = nil;

if (numRows > 200 || (gIsClient && numRows > 80)) // TCS 11/25/03
progressBar = CProgressBar::CreateBar(TCS_GetStockString(stockID_SavingBreakdowns));

if (progressBar)
{
progressBar->SetMaxValue(numRows);
progressBar->SetActionType(TCS_GetStockString(stockID_Row));
}

CProgressBarWatcher watcher (progressBar);
CCursorSpinner spinner;

// LOOP THROUGH THE TABLE ROWS
for (row = 1; row <= numRows; row++)
{
rowIsValid = true; // TCS 9/25/01
isNewBreakdown = false; // TCS 10/22/03

if (progressBar)
progressBar->Increment();
else
++spinner;

// skip entries with 0 test amount(unless zero is specifically allowed)
if (!allowZeroAmount) // rev TCS 2/23/00
{
currentAmount = CMoney(table->GetCellText(row, amountTestCol));
if (currentAmount.IsZero())
rowIsValid = false;
}

// skip entries with blank checkmark(unless specifically allowed)
if (!allowBlankCheckmark && rowIsValid) // TCS 10/23/01
{
rowIsValid = table->IsChecked(row, checkmarkTestCol);
}

// skip entries with blank test name (unless specifically allowed)
if (!allowBlankName && rowIsValid)
{
if (table->GetColType(row, nameTestCol) == coltype_cv) // rev TCS 3/15/99
{
cellValue = table->GetCellValue(row, nameTestCol);

if (!cellValue)
{ // it's a blank cv name field
if (!amountTestCol)
{ // If no amount column, we'll definitely skip it TCS rev 3/30/00
rowIsValid = false;
}
else
{ // otherwise we keep it if there's an amount
currentAmount = CMoney(table->GetCellText(row, amountTestCol));
if (currentAmount.IsZero())
rowIsValid = false;
}
}
}
else
{
cellText = table->GetCellText(row, nameTestCol); // rev TCS 3/30/00
if (cellText.IsBlank())
{ // it's a blank text field TCS rev 3/30/00
if (!amountTestCol)
{ // if no amount column, we definitely skip it
rowIsValid = false;
}
else
{ // otherwise we keep it if there's an amount
currentAmount = CMoney(table->GetCellText(row, amountTestCol));
if (currentAmount.IsZero())
rowIsValid = false;
}
}
}
}

if (rowIsValid)
{
// this row is OK, so increment the counter
breakdownsCompleted++;

// do we have an existing breakdown?
if (breakdownArray.FetchItemAt(row - breakdownsDeleted, id))
{
breakdown = TCS_SAFE_CAST(gDBFile->GetOneObject(breakdownClassID, id),
CBreakdownEntry);
// don't failnil or set a watcher yet- breakdown may not exist
}
else
breakdown = nil; // TCS 9/13/99

if (!breakdown)
{ // no existing breakdown, so create one
breakdown =
TCS_SAFE_CAST(gDBFile->CreateObjectFromID(breakdownClassID, flag_dontautoname),
CBreakdownEntry);

// we'd better have a breakdown by now
// DO NOT watch the breakdown yet- it's done later
TCS_FailNILMsg(breakdown, TCS_GetErrString(errID_BadBreakdown));
isNewBreakdown = true;

// allow the object to do any final setup. Not really needed
// but we have it here for consistency TCS 5/5/03
breakdown->FinishNonViewerCreate();

if (owner)
breakdown->SetCreationType(owner->GetCreationType()); // TCS 6/25/03
else
breakdown->SetCreationType(record_new);

// add it to the database. We now do this explicitely TCS 5/1/03
gDBFile->AddObject(breakdown);

breakdownArray.InsertItemAt(row - breakdownsDeleted, breakdown->GetDBID()); // rev TCS 10/24/01
}

// we used to post here, but TCS removed 3/21/00 and removed some prep code 6/13/00

// posting is now handled in CBreakdownTransaction::PostBreakdownsChanging

// update the breakdown. Note that we remove it from the db because its
// file length may change
DB_ObjectWatcher watcher(breakdown);

if (!gDBFile->TempRemoveObject(breakdown)) // TCS rev 8/26/03
continue;

TCS_TRY // TCS 12/18/01
{
if (owner)
{ // if we have an owner id, we should set the breakdown's owner
// id. Note that we don't care if SetMemberValue fails here;
// that just means the breakdown doesn't support an owner id
breakdown->SetMemberValue(tag_ownerobject, type_objectid, &ownerID);
breakdown->SetMemberValue(tag_ownerobjectclass, type_objclass, &ownerClassID);

// we also set a few values from the owner TCS 8/21/00
// it's possible the table will have different data that will
// replace this later
if (ownerMainAccountID) // bugfix TCS 9/13/01
breakdown->SetMemberValue(tag_mainaccount, type_objectid, &ownerMainAccountID);

if (ownerMainAccountClass)
breakdown->SetMemberValue(tag_mainaccountclass, type_objclass, &ownerMainAccountClass);

breakdown->SetMemberValue(tag_date, type_date, &ownerDate);
}

// now fill in data from the table TCS order change 8/21/00
table->UpdateDBObjectFromRow(row, breakdown);

// here's a chance for subclasses to add any additional data
// which isn't in the table itself
FinishBreakdownUpdate(breakdown, table); // TCS 12/4/98 rev TCS 2/18/04

breakdown->MakeDirty(); // TCS 6/10/03
}
TCS_CATCH {}

// now we can add it back to the database. Note that clients
// also do a direct update of this object to the server. TCS 4/14/03
gDBFile->TempAddObject(breakdown, cUpdateServer);

// we used to post here, but TCS removed it 1/26/00
// since it's already handled in CBreakdownTransaction
}
else // empty or incomplete row. TCS 9/25/01
{
// do we have an existing breakdown here?
if (breakdownArray.FetchItemAt(row - breakdownsDeleted, id))
{
breakdown = TCS_SAFE_CAST(gDBFile->GetOneObject(breakdownClassID, id),
CBreakdownEntry);

if (breakdown)
{ // we have an existing breakdown, so let's delete it.
// first watch the breakdown so it's dereferenced
DB_ObjectWatcher watcher(breakdown);

// allow the breakdown to tidy up before it dies. Note that
// as of 6/13/00, the only breakdown that actually does anything
// here is CSubAssembly. Most breakdowns have already done what
// they need via CBreakdownTransaction::PostBreakdownsChanging
breakdown->PostDeletion();

// permanently remove the breakdown from the database
gDBFile->FullyRemoveObject(breakdown); // rev TCS 4/14/03
}

// remove the breakdown id from the array
breakdownArray.RemoveItemAt(row - breakdownsDeleted); // bugfix TCS 10/24/01
}

breakdownsDeleted++;
}
}

// we're done updating the entries from the table.
// Now we must delete any excess entries, in case some were deleted.
// However we don't do that if a table was not filled due to user choice
// or lack of memory.
if (table->IsCompletelyFilled()) // TCS 8/21/01 rev TCS 9/25/01
RemoveExcessBreakdowns(breakdownArray, breakdownClassID, breakdownsCompleted);

// we used to save changes here, but that may cause problems by catching the parent
// in an inconsistent state. So we'll just wait for the SaveAllChanges call at the
// end of UpdateObjectFromFields TCS rev 9/28/01
}
/*********************************************************************************

RemoveExcessBreakdowns TCS split 5/2/00

remove any left over breakdowns
*********************************************************************************/
void CBreakdownArrayOwner::RemoveExcessBreakdowns(TObjectIDArray &breakdownArray,
const DBClass breakdownClassID, SInt32 breakdownsCompleted)
{
SInt32 numBreakdowns = breakdownArray.GetCount();

if (breakdownsCompleted < numBreakdowns)
{ // remove all subs between breakdownIndex and numBreakdowns
CBreakdownEntry *breakdown = nil;
DBid id;
TableIndexT row;

for (row = numBreakdowns; row > breakdownsCompleted; row--)
{
if (breakdownArray.FetchItemAt(breakdownsCompleted + 1, id)) // rev TCS 2/22/99, 9/7/99
{
breakdown = TCS_SAFE_CAST(gDBFile->GetOneObject(breakdownClassID, id),
CBreakdownEntry);
}
// does the breakdown exist?
if (breakdown)
{
// watch the breakdown so it's dereferenced (TCS 3/16/00)
DB_ObjectWatcher watcher(breakdown);

// allow the breakdown to tidy up before it dies. Note that
// as of 6/13/00, the only breakdown that actually does anything
// here is CSubAssembly. Most breakdowns have already done what
// they need via CBreakdownTransaction::PostBreakdownsChanging
breakdown->PostDeletion();

// permanently remove the breakdown from the database
gDBFile->TempRemoveObject(breakdown);
}
breakdownArray.RemoveItemsAt(1, row);
}
}
}
/*********************************************************************************

CopyBreakdowns TCS 5/10/00 rev 7/19/00 rev 11/1/01

copy breakdowns from the passed breakdown owner.
*********************************************************************************/
void CBreakdownArrayOwner::CopyBreakdowns(CBreakdownArrayOwner *source, const DBid newOwnerID)
{
TObjectIDArray sourceArray = source->GetBreakdownArray();

TObjectIDArrayIterator iterator(sourceArray);
DBid id;
DB_PersistentObject *sourceBreakdown,
*newBreakdown;

TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

while (iterator.Next(id))
{
sourceBreakdown = gDBFile->GetOneObject(mBreakdownClassID, id);

if (sourceBreakdown)
{
DB_ObjectWatcher watcher(sourceBreakdown);

if (CanCopyBreakdown(sourceBreakdown))
{
// create a breakdown
newBreakdown = gDBFile->CreateObjectFromAnother(sourceBreakdown, flag_dontautoname);

if (newBreakdown)
{
DB_ObjectWatcher newWatcher(newBreakdown);

// set the owner id right away TCS 11/1/01
newBreakdown->SetOwnerID(newOwnerID);
newBreakdown->MakeDirty();

// allow the object to do any final setup. Not really needed
// but we have it here for consistency TCS 5/5/03
newBreakdown->FinishNonViewerCreate();

// add to the database
gDBFile->AddObject(newBreakdown); // TCS 5/1/03

// add to the stored array in the owner transaction
AddBreakdown(newBreakdown->GetDBID());
}
}
}
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

WriteToReportTable TCS 1/3/00

write breakdowns to a report table (in print forms). This is a single method which
takes care of all the different breakdown types
*********************************************************************************/
Boolean CBreakdownArrayOwner::WriteToReportTable(CReportTable *table, const DBid breakdownClass,
TObjectIDArray &breakdownArray, DB_PersistentObject *source)
{
// sanity check
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));
table->CreatePrintFormReport(breakdownClass, breakdownArray, source);
return true;
}
/*********************************************************************************

FillTableFromBreakdowns

write breakdowns to a breakdown table. We simply call FillTableFromBreakdownArray
which uses the basic breakdown array to fetch values. Subclasses may override
if a different array is used.
*********************************************************************************/
Boolean CBreakdownArrayOwner::FillTableFromBreakdowns(CBreakdownTable *table,
const DBid breakdownClassID)
{
// sanity check
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));

return FillTableFromBreakdownArray(table, mBreakdownArray, breakdownClassID);
}
/*********************************************************************************

FillTableFromBreakdownArray

write breakdowns to a breakdown table. This is a single method which
takes care of all the different breakdown types, since writing is
a straightforward matter of pulling out the required members and putting
them into the table cells
*********************************************************************************/
Boolean CBreakdownArrayOwner::FillTableFromBreakdownArray(CBreakdownTable *table,
TObjectIDArray &breakdownArray,
const DBid breakdownClassID)
{
// sanity check
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));

Boolean needsUpdate = false; // rev TCS 12/25/00

// get the number of breakdowns
SInt32 numBreakdowns = breakdownArray.GetCount();

// first erase the table
table->MakeEmptyTable();

if (numBreakdowns)
{
// we used to start by setting row count to numBreakdowns, but that
// caused problems at page boundaries, so we now fill them one at a time

// if we have many rows, give a warning about possible memory problems,
// and allow users to show only a limited number of items TCS 8/8/01
if (numBreakdowns > cManyBreakdownItems)
{
CTextString errString = TCS_GetMsgString(msgID_WarnManyBreakdowns);
errString.ReplaceStdTokens(numBreakdowns, SInt32(cManyBreakdownItems));
if (TCS_YesNoDialog(errString))
{
numBreakdowns = cManyBreakdownItems;
table->SetIsCompletelyFilled(false); // TCS 8/21/01
}
}

// we may be loading a lot of records, so let's clear the cache TCS 11/25/03
gDBFile->ForcePurge();
table->SetMemoryIsShort(false);

// If we have more than a few breakdowns, show a progress window. TCS 8/8/01
CCursorSpinner spinner;
CProgressBar *progressDialog = nil;

if (numBreakdowns > cNeedsProgressCount || (gIsClient && numBreakdowns > 25))
{
progressDialog = CProgressBar::CreateBar(TCS_GetStockString(stockID_ShowingBreakdowns)); // rev TCS 12/27/00
}

if (progressDialog)
{
progressDialog->SetWindowTitle(TCS_GetStockString(stockID_ShowingBreakdowns));
progressDialog->SetTextStep(5);
progressDialog->SetMaxValue(numBreakdowns);
progressDialog->SetActionType(cEmptyString);
progressDialog->Refresh();
}

// watch the dialog so it's automatically deleted at the end of this scope
CProgressBarWatcher watcher(progressDialog);

// ok, now fill in each row
DB_PersistentObject *object;
DBid id;
SInt32 currentRow = 0;
UInt8 extraRows = 0;

for (SInt32 index = 1; index <= numBreakdowns; index++)
{
++spinner;

// clear the cache periodically TCS 11/25/03
if (index % 100 == 99)
{
gDBFile->ForcePurge();
table->SetMemoryIsShort(false);
}

if (!gIsClient && table->MemoryIsShort() && numBreakdowns >= cNeedsProgressCount)
{
// if we had to do a purge, ask the user if they'd like
// to stop filling the table. rev TCS 8/21/01 rev TCS 11/25/03
if (TCS_YesNoDialog(TCS_GetMsgString(msgID_InsufficientMemoryForTable)))
{
table->SetIsCompletelyFilled(false);
break;
}
else
table->SetMemoryIsShort(false); // keep trying
}

if (progressDialog)
{
if (progressDialog->Increment() != progress_continue)
break;
}
if (breakdownArray.FetchItemAt(index, id))
{
object = gDBFile->GetOneObject(breakdownClassID, id);
}

TCS_TRY
{
TCS_FailNILMsg(object, TCS_GetErrString(errID_BadBreakdown));

DB_ObjectWatcher watcher(object);

if (!object->IsValidForDisplay()) // TCS rev 12/15/00
{
TCS_ErrorAlert(errID_MissingBreakdown);
needsUpdate = true;
}
else if (table->IsPrinting()) // TCS rev 11/27/00
{
currentRow++;
extraRows = table->InsertPrintRow();

// was this at a page boundary? If so, update the row index
if (extraRows)
currentRow += extraRows; // rev TCS 6/3/02

table->UpdateRowFromDBObject(currentRow, object);
}
else
{
currentRow++;
table->InsertRow();
table->UpdateRowFromDBObject(currentRow, object);

// give subclasses a chance to do any non-printing setup
table->FinishRowFill(currentRow); // TCS 4/18/03
}
}
TCS_CATCH
{ // if a breakdown object is missing, we still continue
}
}

// give the table a chance to do any final setup TCS 3/20/01
table->FinishTableFill(progressDialog);
}
else
{ // no objects, the table gets one empty row. Tables that should just
// be blank when empty should override MakeSingleRowTable.
table->MakeSingleRowTable();
}

table->Refresh();

return needsUpdate; // rev TCS 12/25/00
}
/*********************************************************************************

BreakdownMatchesSelector TCS 12/17/01 rev 12/21/01

return whether one of our breakdowns matches the given selector. Note that
for breakdowns, we return true if any one of the breakdown rows matches the
selector.
*********************************************************************************/
Boolean CBreakdownArrayOwner::BreakdownMatchesSelectors(CTCS_Array &selectorArray,
const Boolean matchAny, const SInt32 selectorCount) const
{
// if no breakdowns, then we definitely don't match
if (mBreakdownArray.GetCount() == 0)
return false;

// get ready to loop through our breakdown items
TObjectIDArrayIterator iterator (mBreakdownArray);
DBid id;
DBClass breakdownClass = GetBreakdownClassID();
DB_PersistentObject *breakdown = nil;
Boolean itemMatches;

while (iterator.Next(id))
{
breakdown = gDBFile->GetOneObject(breakdownClass, id);

if (breakdown)
{
DB_ObjectWatcher watcher (breakdown);

itemMatches = breakdown->MatchesFindCriteria(selectorArray, matchAny, selectorCount);

if (itemMatches)
return true;
}
}

// if we get this far, there was no match
return false;
}