Link to: header | source code | source
2 | source 4 | tables
directory
Copyright Turtle Creek Software 1996-2006. All Rights Reserved.
Source Code
This class manages accounting report tables for the Goldenseal
estimating software,
small business management software, construction
project management software and
construction estimating software.
/*********************************************************************************
CreateSubcatItemReport TCS 7/9/02 rev 1/25/03
create a report that shows items broken down by category AND subcategory.
If the source has subcategories, this is a "three level" report with
categories, subcategories and items.
*********************************************************************************/
void CReportTable::CreateSubcatItemReport(const DBid reportID, TReportRowArray *rowArray)
{
DBid accountID = GetMatchValue();
DBClass classID = GetGroup();
if (DB_ClassDescriptor::IsAccount(classID) || classID == id_Estimate)
{
// the source can be a job account or estimate, so we need a
// generic source
DB_PersistentObject *source = gDBFile->GetOneObject(classID, accountID);
if (source)
{
// watch the account so it doesn't leak TCS bugfix 11/12/01
DB_ObjectWatcher watcher(source);
if (source->HasSubcategories())
{
// set up a two level array of categories, subcategories and items
TTwoLevelArray *twoLevelArray = NEW TTwoLevelArray;
TCS_FailNILMsg(twoLevelArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
// the account will fill in data of the given type
source->FillCatSubcatArray(reportID, twoLevelArray, mHideOverhead,
mSoftCostAmount, mHardCostAmount);
// finally, fill in the row info array from the breakdown data
FillRowsFromTwoLevelArray(rowArray, twoLevelArray);
}
TCS_CATCH {}
// tidy up the allocated array
ClearTwoLevelArray(twoLevelArray);
return;
}
else
{ // set up an array of categories and items
TReportGroupArray *catArray = NEW TReportGroupArray;
TCS_FailNILMsg(catArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
// the account will fill in data of the given type
source->FillCatItemArray(reportID, catArray);
// finally, fill in the row info array from the breakdown data
FillRowsFromCatItemArray(rowArray, catArray);
}
TCS_CATCH {}
// tidy up the allocated array
ClearItemCategoryArray(catArray);
return;
}
}
}
// if we get this far we had problems
TCS_TokenErrorAlert(errID_MissingReportAccount,
DB_ClassDescriptor::GetClassName(classID));
}
/*********************************************************************************
CreateCatItemReport TCS 7/9/02 rev 1/25/03
create a report that shows items broken down by category and item or
location and item. This is a "two level" report, even if the source
includes subcategories.
*********************************************************************************/
void CReportTable::CreateCatItemReport(const DBid reportID, TReportRowArray *rowArray)
{
DBid accountID = GetMatchValue();
DBClass classID = GetGroup();
if (DB_ClassDescriptor::IsAccount(classID) || classID == id_Estimate)
{
// the source can be a job account or estimate, so we need a
// generic source
DB_PersistentObject *source = gDBFile->GetOneObject(classID, accountID);
if (source)
{
// watch the account so it doesn't leak TCS bugfix 11/12/01
DB_ObjectWatcher watcher(source);
// set up an array of categories and items
TReportGroupArray *catArray = NEW TReportGroupArray;
TCS_FailNILMsg(catArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
// the account will fill in data of the given type
source->FillCatItemArray(reportID, catArray);
// finally, fill in the row info array from the breakdown data
FillRowsFromCatItemArray(rowArray, catArray);
}
TCS_CATCH {}
// tidy up the allocated array
ClearItemCategoryArray(catArray);
return;
}
}
// if we get this far we had problems
TCS_TokenErrorAlert(errID_MissingReportAccount,
DB_ClassDescriptor::GetClassName(classID));
}
/*********************************************************************************
CreateDeductionReport TCS 3/4/02
create a payroll deduction report. These are two-level breakdowns:
id_DeductByEmpReport-- employee, then specific taxes, then individual breakdowns
id_DeductByTaxReport-- tax item, then employee, then individual breakdowns
*********************************************************************************/
void CReportTable::CreateDeductionReport(const DBid reportID, TReportRowArray *rowArray)
{
SReportInfo reportInfo = GetReportInfo();
TTwoLevelArray *twoLevelArray = NEW TTwoLevelArray;
TCS_FailNILMsg(twoLevelArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
// first, fill in a two-level shell structure
if (reportID == id_DeductByEmpReport)
FillTwoLevelArray(twoLevelArray, id_EmployeeAccount, id_TaxItem);
else if (reportID == id_DeductByTaxReport)
FillTwoLevelArray(twoLevelArray, id_TaxItem, id_EmployeeAccount);
// next, loop thru each payroll record and fill in specific data
TObjectIDArray transArray = DB_ListManager::GetIDArray(id_PayrollRecord);
TObjectIDArrayIterator iterator (transArray);
DBid id;
CPayrollRecord *record = nil;
while (iterator.Next(id))
{
record = TCS_SAFE_CAST(gDBFile->GetOneObject(id_PayrollRecord, id),
CPayrollRecord);
if (record)
{
// watch the account so it doesn't leak TCS bugfix 11/12/01
DB_ObjectWatcher watcher(record);
// the transaction will fill in data of the given type
record->FillDeductionReport(reportID, twoLevelArray, reportInfo);
}
else
{
TCS_TokenErrorAlert(errID_MissingReportAccount,
DB_ClassDescriptor::GetClassName(id_ProjectAccount));
}
}
// finally, fill in the row info array from the breakdown data
FillRowsFromTwoLevelArray(rowArray, twoLevelArray);
}
TCS_CATCH {}
ClearTwoLevelArray(twoLevelArray);
}
/*********************************************************************************
CreateTaxPaymentReport TCS 11/16/02
create a payroll tax payment report. These are two-level breakdowns,
based on the breakdowns in a single other cost transactions (depending on the
other cost record, it can show payroll taxes, sales tax or vendor withholding)
id_TaxPmtByAcctReport-- employee, then specific taxes, then individual breakdowns
id_TaxPmtByTaxReport-- tax item, then employee, then individual breakdowns
*********************************************************************************/
void CReportTable::CreateTaxPaymentReport(const DBid reportID, TReportRowArray *rowArray)
{
SReportInfo reportInfo = GetReportInfo();
DBid accountID = GetMatchValue();
// fetch the record
COtherCost *record =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_OtherCost, accountID), COtherCost);
if (record == nil)
{
TCS_TokenErrorAlert(errID_MissingReportAccount,
DB_ClassDescriptor::GetClassName(id_OtherCost));
return;
}
// we have a valid record, so let's watch it and continue
DB_ObjectWatcher watcher (record);
UInt8 breakdownType = record->GetBreakdownType();
// this report only works for payroll and vendor taxes
if (breakdownType != breakdown_payrolltax && breakdownType != breakdown_vendorwithholding)
return;
TTwoLevelArray *breakdownArray = NEW TTwoLevelArray;
TCS_FailNILMsg(breakdownArray, TCS_GetErrString(errID_BadArray));
TCS_TRY
{
// first, fill in a two-level shell structure
if (reportID == id_TaxPmtByAcctReport)
{
if (breakdownType == breakdown_payrolltax)
FillTwoLevelArray(breakdownArray, id_EmployeeAccount, id_TaxItem, true);
else if (breakdownType == breakdown_vendorwithholding)
FillTwoLevelArray(breakdownArray, id_SubcontractorAccount, id_VendorWithholding, true);
}
else
{
if (breakdownType == breakdown_payrolltax)
FillTwoLevelArray(breakdownArray, id_TaxItem, id_EmployeeAccount, true);
else if (breakdownType == breakdown_vendorwithholding)
FillTwoLevelArray(breakdownArray, id_VendorWithholding, id_SubcontractorAccount, true);
}
// next, have the record fill in specific data
record->FillTaxPaymentReport(breakdownArray, reportInfo);
// finally, fill in the row info array from the breakdown data
FillRowsFromTwoLevelArray(rowArray, breakdownArray);
}
TCS_CATCH {}
ClearTwoLevelArray(breakdownArray);
}
/*********************************************************************************
CreateIncomeTaxReport TCS 4/10/02
create an income tax report
*********************************************************************************/
void CReportTable::CreateIncomeTaxReport(TReportRowArray */*rowArray*/)
{
}
/*********************************************************************************
CreateInventoryReport TCS 4/10/02
create an inventory report
*********************************************************************************/
void CReportTable::CreateInventoryReport(TReportRowArray */*rowArray*/)
{
}
/*********************************************************************************
CreateSalesTaxReport TCS 11/18/02
create a sales tax report
*********************************************************************************/
void CReportTable::CreateSalesTaxReport(TReportRowArray *rowArray)
{
SReportInfo reportInfo = GetReportInfo();
DBid accountID = GetMatchValue();
// fetch the record
COtherCost *record =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_OtherCost, accountID), COtherCost);
if (record == nil)
{
TCS_TokenErrorAlert(errID_MissingReportAccount,
DB_ClassDescriptor::GetClassName(id_OtherCost));
return;
}
// we have a valid record, so let's watch it and continue
DB_ObjectWatcher watcher (record);
UInt8 breakdownType = record->GetBreakdownType();
// this report only works for sales taxes
if (breakdownType != breakdown_salestax)
return;
TObjectIDArray breakdownArray = record->GetBreakdownArray();
CreateBreakdownReportArray(rowArray, tag_taxid, id_JobSalesTax,
id_SalesTaxBreakdownEntry, &breakdownArray);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillTwoLevelArray TCS 4/18/02 rev TCS 11/17/02
fill in a starting two-level breakdown array. The basic array contains
a subarray pointer, which in turn includes members with a subarray pointer.
This allows us to create reports with a 3 level breakdown-- for example,
category/subcategory/item, or employee/payroll tax/breakdown items.
*********************************************************************************/
void CReportTable::FillTwoLevelArray(TTwoLevelArray *breakdownArray, const DBClass mainClass,
const DBClass subClass, const Boolean includeUnallocated)
{
TCS_FailNILMsg(breakdownArray, TCS_GetErrString(errID_BadArray));
TObjectIDArray mainArray = DB_ListManager::GetIDArray(mainClass);
TObjectIDArray subArray = DB_ListManager::GetIDArray(subClass);
TObjectIDArrayIterator iterator (mainArray);
TObjectIDArrayIterator subIterator (subArray);
DBid mainID, subID;
STwoLevelInfo info;
info.groupArray = nil;
info.classID = mainClass;
info.hasItems = false; // TCS 4/23/02
info.groupArray = nil;
SReportGroupInfo subInfo;
subInfo.itemArray = nil;
subInfo.classID = subClass;
subInfo.itemArray = nil;
while (iterator.Next(mainID))
{
info.itemID = mainID;
info.groupArray = NEW TReportGroupArray;
TCS_FailNILMsg(info.groupArray, TCS_GetErrString(errID_BadArray));
subIterator.ResetTo(iter_from_start);
while (subIterator.Next(subID))
{
subInfo.itemID = subID;
subInfo.itemArray = NEW TObjectInfoArray;
TCS_FailNILMsg(subInfo.itemArray, TCS_GetErrString(errID_BadArray));
info.groupArray->Append(subInfo);
}
if (includeUnallocated) // TCS 11/17/02
{
subInfo.itemID = 0;
subInfo.itemArray = NEW TObjectInfoArray;
TCS_FailNILMsg(subInfo.itemArray, TCS_GetErrString(errID_BadArray));
info.groupArray->Append(subInfo);
}
breakdownArray->Append(info);
}
if (includeUnallocated) // TCS 11/17/02
{
info.itemID = 0;
info.groupArray = NEW TReportGroupArray;
TCS_FailNILMsg(info.groupArray, TCS_GetErrString(errID_BadArray));
subInfo.itemID = 0;
subInfo.itemArray = NEW TObjectInfoArray;
TCS_FailNILMsg(subInfo.itemArray, TCS_GetErrString(errID_BadArray));
info.groupArray->Append(subInfo);
breakdownArray->Append(info);
}
}
/*********************************************************************************
FillRowsFromBreakdownArray moved TCS 1/4/99 rev TCS 4/22/02
fill a report row array from a breakdown array. This is where we take the
data, and convert it to the actual table format.
*********************************************************************************/
void CReportTable::FillRowsFromBreakdownArray(TReportRowArray *rowArray,
TBreakdownInfoArray *breakdownArray, const UInt8 breakdownType)
{
TCS_FailNILMsg(rowArray, TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(breakdownArray, TCS_GetErrString(errID_BadArray));
Boolean isDate = (breakdownType == breakdown_month || breakdownType == breakdown_week ||
breakdownType == breakdown_day || breakdownType == breakdown_hour);
// initialize row structs
SBreakdownInfo breakdownInfo;
SReportRowInfo rowInfo,
blankRowInfo; // rev TCS 4/22/02
SObjectInfo objectInfo; // TCS 4/17/02
// prep the stock rows rev TCS 1/4/00
InitializeRowInfo(rowInfo, rowtype_data); // TCS 4/17/02
InitializeRowInfo(blankRowInfo, rowtype_blank);
// get ready to iterate thru breakdown array
TBreakdownInfoArrayIterator iterator(*breakdownArray);
while (iterator.Next(breakdownInfo)) // ************ LOOP THROUGH BREAKDOWNS
{
TCS_FailNILMsg(breakdownInfo.itemArray, TCS_GetErrString(errID_BadArray));
// should we include this item?
if (ShowZeroItems() || breakdownInfo.itemArray->GetCount() > 0)
{
// add the subtitle row
if (!mCondensed) // TCS 5/30/01 rev 4/15/02
{
if (isDate)
{ // Note that we just do a raw cast of the dates, and
// store them in SInt32's that are not needed otherwise rev TCS 7/31/00
rowInfo.itemID = breakdownInfo.startDate.GetIntegerValue();
breakdownInfo.endDate.AddDays(-1);
rowInfo.matchValue = (UInt32) breakdownInfo.endDate.GetIntegerValue();
}
else if (breakdownType == breakdown_mixed)
{
rowInfo.itemID = breakdownInfo.itemID; // object ID or menu ID
if (breakdownInfo.classID != cUnallocatedItem)
rowInfo.classID = TCS_ABS(breakdownInfo.classID); // object class or menu cmd #
}
else
{
rowInfo.itemID = breakdownInfo.itemID; // object ID or menu ID
rowInfo.classID = breakdownInfo.classID; // object class or menu cmd #
}
rowInfo.rowType = rowtype_subtitle;
rowArray->Append(rowInfo);
}
if (breakdownInfo.itemArray->GetCount() > 0)
{ // we have data rows, so add them
TObjectInfoArrayIterator itemIterator(*breakdownInfo.itemArray);
while (itemIterator.Next(objectInfo)) // ************* LOOP THROUGH ITEM ROWS
{
rowInfo.itemID = objectInfo.itemID; // rev TCS 4/18/02
rowInfo.classID = objectInfo.classID;
rowInfo.isSoftCost = objectInfo.transactionType; // TCS 2/23/04
if (mCondensed) // TCS 5/30/01 rev 4/15/02
rowInfo.rowType = rowtype_condenseddata;
else
rowInfo.rowType = rowtype_data;
rowArray->Append(rowInfo);
} // END OF ITEM LOOP
}
else if (!mCondensed)
{ // if no items, add a 'no items' row
blankRowInfo.rowType = rowtype_noitems;
rowArray->Append(blankRowInfo);
}
// if needed, add a subtotal row after each data set
if (mCondensed) // TCS 5/30/01
{
if (isDate) // TCS 5/31/01
{
rowInfo.itemID = breakdownInfo.startDate.GetIntegerValue();
breakdownInfo.endDate.AddDays(-1);
rowInfo.matchValue = (UInt32) breakdownInfo.endDate.GetIntegerValue();
}
else if (breakdownType == breakdown_mixed) // TCS 4/4/04
{
rowInfo.itemID = breakdownInfo.itemID; // object ID or menu ID
if (breakdownInfo.classID != cUnallocatedItem)
rowInfo.classID = TCS_ABS(breakdownInfo.classID); // object class or menu cmd #
}
else
{
rowInfo.itemID = breakdownInfo.itemID;
rowInfo.classID = breakdownInfo.classID;
}
rowInfo.rowType = rowtype_condensedtotal;
rowArray->Append(rowInfo);
}
else if (mShowSubTotal)
{
blankRowInfo.rowType = rowtype_subtotal;
rowArray->Append(blankRowInfo);
}
// add a blank separator row
if (!mCondensed)
{
blankRowInfo.rowType = rowtype_blank;
rowArray->Append(blankRowInfo);
}
} // END OF BREAKDOWN LOOP
}
}
/*********************************************************************************
FillReportFromTwoLevelArray TCS 4/18/02
fill a report row array from a breakdown array. This is where we take the
raw data, and convert it to an array that describes each row in the table.
*********************************************************************************/
void CReportTable::FillRowsFromTwoLevelArray(TReportRowArray *rowArray,
TTwoLevelArray *breakdownArray)
{
TCS_FailNILMsg(rowArray, TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(breakdownArray, TCS_GetErrString(errID_BadArray));
// initialize row structs
STwoLevelInfo breakdownInfo;
SReportGroupInfo groupInfo;
SObjectInfo objectInfo;
SReportRowInfo rowInfo,
blankRowInfo;
// prep the stock rows
InitializeRowInfo(rowInfo, rowtype_data);
InitializeRowInfo(blankRowInfo, rowtype_blank);
// get ready to iterate thru breakdown array
TTwoLevelArrayIterator iterator(*breakdownArray);
// loop through each main breakdown in the array
while (iterator.Next(breakdownInfo)) // ******** OUTER LOOP, BREAKDOWNS ***********
{
TCS_FailNILMsg(breakdownInfo.groupArray, TCS_GetErrString(errID_BadArray));
// should we include this group?
if (ShowZeroItems() || breakdownInfo.hasItems) // TCS 4/23/02
{
// add the outer title row
rowInfo.itemID = breakdownInfo.itemID;
rowInfo.classID = breakdownInfo.classID;
rowInfo.rowType = rowtype_grouptitle;
rowArray->Append(rowInfo);
if (breakdownInfo.groupArray->GetCount() > 0)
{
// we have groups, so add them
TReportGroupArrayIterator rowIterator(*breakdownInfo.groupArray);
while (rowIterator.Next(groupInfo)) // ******** MIDDLE LOOP, GROUPS ***********
{
TCS_FailNILMsg(groupInfo.itemArray, TCS_GetErrString(errID_BadArray));
if (groupInfo.itemArray->GetCount() > 0) // we have data in this group
{
// add the group title row
if (!mCondensed)
{
rowInfo.itemID = groupInfo.itemID;
rowInfo.classID = groupInfo.classID;
rowInfo.rowType = rowtype_subtitle;
rowArray->Append(rowInfo);
}
TObjectInfoArrayIterator itemIterator(*groupInfo.itemArray);
while (itemIterator.Next(objectInfo)) // ************* INNER LOOP, ITEMS ***********
{
rowInfo.itemID = objectInfo.itemID;
rowInfo.classID = objectInfo.classID;
rowInfo.conditions = objectInfo.transactionType; // TCS 7/11/02
if (mCondensed)
rowInfo.rowType = rowtype_condenseddata;
else
rowInfo.rowType = rowtype_data;
rowArray->Append(rowInfo);
} // ************* END OF INNER LOOP (ITEMS) ***********
// if needed, add a subtotal row
if (mCondensed)
{
rowInfo.itemID = groupInfo.itemID;
rowInfo.classID = groupInfo.classID;
rowInfo.rowType = rowtype_condensedtotal;
rowArray->Append(rowInfo);
}
else if (mShowSubTotal)
{
blankRowInfo.rowType = rowtype_subtotal;
rowArray->Append(blankRowInfo);
}
// add a blank separator row
if (!mCondensed)
{
blankRowInfo.rowType = rowtype_blank;
rowArray->Append(blankRowInfo);
}
}
else if (!mCondensed && ShowZeroItems())
{ // if no items, add a 'no items' row
blankRowInfo.rowType = rowtype_noitems;
rowArray->Append(blankRowInfo);
}
} // ******** END OF MIDDLE LOOP (GROUPS) ***********
}
// if needed, add a group subtotal row
if (mShowSubTotal)
{
rowInfo.itemID = breakdownInfo.itemID;
rowInfo.classID = breakdownInfo.classID;
rowInfo.rowType = rowtype_grouptotal;
rowArray->Append(rowInfo);
}
// add a blank separator row
blankRowInfo.rowType = rowtype_blank;
rowArray->Append(blankRowInfo);
} // ******** END OF OUTER LOOP (BREAKDOWNS) ***********
}
}
/*********************************************************************************
FillRowsFromCatItemArray TCS 1/25/03
fill a report row array from a breakdown array. This is where we take the
raw data, and convert it to an array that describes each row in the table.
*********************************************************************************/
void CReportTable::FillRowsFromCatItemArray(TReportRowArray *rowArray,
TReportGroupArray *breakdownArray)
{
TCS_FailNILMsg(rowArray, TCS_GetErrString(errID_BadArray));
TCS_FailNILMsg(breakdownArray, TCS_GetErrString(errID_BadArray));
// initialize row structs
SReportGroupInfo groupInfo;
SObjectInfo objectInfo;
SReportRowInfo rowInfo,
blankRowInfo;
// prep the stock rows
InitializeRowInfo(rowInfo, rowtype_data);
InitializeRowInfo(blankRowInfo, rowtype_blank);
// get ready to iterate thru breakdown array
TReportGroupArrayIterator iterator(*breakdownArray);
// loop through each main breakdown in the array
while (iterator.Next(groupInfo)) // ******** OUTER LOOP, CATEGORIES ***********
{
TCS_FailNILMsg(groupInfo.itemArray, TCS_GetErrString(errID_BadArray));
// should we include this group?
if (ShowZeroItems() || groupInfo.itemArray->GetCount() > 0)
{
// add the outer title row
if (!mCondensed)
{
rowInfo.itemID = groupInfo.itemID;
rowInfo.classID = groupInfo.classID;
rowInfo.rowType = rowtype_subtitle;
rowArray->Append(rowInfo);
}
if (groupInfo.itemArray->GetCount() > 0)
{
// we have items, so add them
TObjectInfoArrayIterator itemIterator(*groupInfo.itemArray);
while (itemIterator.Next(objectInfo)) // ************* INNER LOOP, ITEMS ***********
{
rowInfo.itemID = objectInfo.itemID;
rowInfo.classID = objectInfo.classID;
rowInfo.conditions = objectInfo.transactionType;
if (mCondensed)
rowInfo.rowType = rowtype_condenseddata;
else
rowInfo.rowType = rowtype_data;
rowArray->Append(rowInfo);
}
// if needed, add a subtotal row
if (mCondensed)
{
rowInfo.itemID = groupInfo.itemID;
rowInfo.classID = groupInfo.classID;
rowInfo.rowType = rowtype_condensedtotal;
rowArray->Append(rowInfo);
}
else if (mShowSubTotal)
{
blankRowInfo.rowType = rowtype_subtotal;
rowArray->Append(blankRowInfo);
}
// add a blank separator row
if (!mCondensed)
{
blankRowInfo.rowType = rowtype_blank;
rowArray->Append(blankRowInfo);
}
}
}
}
}
/*********************************************************************************
FetchSpecialTableValue TCS 4/10/02
fill in a special value and return whether it was filled
*********************************************************************************/
Boolean CReportTable::FetchSpecialTableValue(const TagType tag, const SReportRowInfo &rowInfo,
CTextString &outString, CMoney &outValue)
{
if (mReportClassID == id_ToolReport || mReportClassID == id_ToolByItemReport) // TCS 4/11/02
{
if (tag == tag_costitem || tag == tag_date) // bugfix TCS 11/9/02
{
DB_PersistentObject *estBreakdown =
gDBFile->GetOneObject(rowInfo.classID, rowInfo.matchValue); // bugfix TCS 3/7/04
if (estBreakdown)
{
DB_ObjectWatcher watcher (estBreakdown);
estBreakdown->GetMemberValue(tag, type_cstring, &outString);
}
else
outString = cEmptyString;
outValue = 0;
return true;
}
else
return false;
}
else if (mReportClassID == id_TakeoffReport || mReportClassID == id_TakeoffByItemReport ||
mReportClassID == id_LaborHoursReport)
{
if (tag == tag_grossquantity || tag == tag_netquantity || tag == tag_duedate ||
tag == tag_grossprice || tag == tag_netprice || tag == tag_unitcost)
{
TTakeoffArrayIterator iterator (mTakeoffArray);
STakeoffInfo takeoffInfo;
while (iterator.Next(takeoffInfo))
{
if (takeoffInfo.costItemID == rowInfo.itemID &&
(mReportClassID == id_TakeoffReport ||
takeoffInfo.breakdownID == rowInfo.matchValue)) // TCS 5/15/02
{
switch (tag)
{
case tag_duedate:
outValue = 0;
outString = takeoffInfo.dateNeeded.GetCString();
break;
case tag_grossquantity:
outValue = takeoffInfo.grossQuantity;
outString = outValue.GetNumberString();
break;
case tag_netquantity:
outValue = takeoffInfo.netQuantity;
outString = outValue.GetNumberString();
break;
case tag_unitcost:
outValue = takeoffInfo.purchasePrice;
outString = outValue.GetNumberString();
break;
case tag_grossprice:
outValue = takeoffInfo.purchasePrice * takeoffInfo.grossQuantity;
outString = outValue.GetCurrencyString();
break;
case tag_netprice:
outValue = takeoffInfo.purchasePrice * takeoffInfo.netQuantity;
outString = outValue.GetCurrencyString();
break;
default: // should never get here
outValue = 0;
outString = TCS_GetStockString(stockID_BadValue);
break;
}
return true;
}
}
// we shouldn't get this far, but if so let's fill
// in some error values
outValue = 0;
outString = TCS_GetStockString(stockID_BadValue);
return true;
}
}
else if (mReportClassID == id_CatSubcatReport)
{
// we have to special case for the credit side of a cost transfer TCS 7/11/02
if (rowInfo.classID == id_CostTransfer && rowInfo.conditions == condition_Credit)
{
switch (tag)
{
case tag_costarea:
case tag_category:
case tag_subcategory:
case tag_amount:
case tag_jobcostamount: // TCS 11/26/02
{
DB_PersistentObject *transfer = gDBFile->GetOneObject(id_CostTransfer, rowInfo.itemID);
if (transfer)
{
DB_ObjectWatcher watcher (transfer);
switch (tag)
{
case tag_costarea:
return transfer->GetMemberValue(tag_newcostarea, type_cstring, &outString);
break;
case tag_category:
return transfer->GetMemberValue(tag_newcat, type_cstring, &outString);
break;
case tag_subcategory:
return transfer->GetMemberValue(tag_newsubcat, type_cstring, &outString);
break;
case tag_jobcostamount: // TCS 11/26/02
case tag_amount:
{
outValue = transfer->GetNegativeAmount();
outString = outValue.GetCurrencyString();
return true;
}
break;
default:
break;
}
}
}
default: // we fetch other fields as usual
return false;
break;
}
}
}
return false; // for other reports, we fetch info from the object as usual
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillReportTableFromArray TCS 8/6/99
fill in the table with data from the given row array. We must handle all
kinds of possible report rows here, so we need to do a massive amount of
prep work and switching.
*********************************************************************************/
void CReportTable::FillReportTableFromArray(TReportRowArray *rowArray,
const UInt8 breakdownType, const Boolean showProgress)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
TCS_FailNILMsg(rowArray, TCS_GetErrString(errID_BadArray));
// clear the cache TCS 12/30/02
gDBFile->SaveAllAndPurge();
// clear the table
SetNumRows(0);
// reset the low-memory bit TCS 1/24/03
SetMemoryIsShort(false);
// set all totals and subtotals to zero
ResetTotals();
// calculate our 'hide soft cost' multiplier
if (mHideOverhead && mHardCostAmount.IsPositive() && mSoftCostAmount.IsPositive())
{
mHardCostMultiplier = 1 + CMoney::GetRatio(mSoftCostAmount, mHardCostAmount);
}
else
mHardCostMultiplier = 1;
DBid reportClassID = GetReportClassID();
CCursorSpinner spinner;
SInt32 rowCount = rowArray->GetCount();
SInt8 success = progress_continue;
// if we have enough rows, set up a progress bar
CProgressBar *progressDialog = nil; // TCS 1/1/01
if (rowCount > 40 && showProgress)
{
progressDialog = CProgressBar::CreateBar(cEmptyString, DLOG_ProgressWithStop);
if (progressDialog)
{
progressDialog->SetValue(0);
progressDialog->SetMaxValue(rowCount);
progressDialog->SetTextStep(10);
progressDialog->SetWindowTitle(TCS_GetStockString(stockID_FillingReportTable));
progressDialog->SetMessage(TCS_GetStockString(stockID_FillingReportTable));
progressDialog->SetActionType(TCS_GetStockString(stockID_Row));
progressDialog->Refresh();
}
}
// set a watcher on the progress dialog so we don't leave it dangling
CProgressBarWatcher watcher(progressDialog);
// initialize and prepare
DB_PersistentObject *object = nil;
SLETableColInfo colInfo;
SColDataInfo colData;
CMoney amount;
CTextString textString;
TableIndexT row = 0;
// set up to loop thru the array
TReportRowArrayIterator iterator(*rowArray);
SReportRowInfo rowInfo;
// now we'll go through the row info array, and fill in each table
// row based on the specs in its rowInfo struct.
while (iterator.Next(rowInfo) && success == progress_continue)
{
// clear the RAM cache occasionally TCS 1/27/03
if (iterator.GetCurrentIndex() % 50 == 1)
{
gDBFile->ForcePurge();
}
// we used to check for low memory and give a warning, but that seems
// to cause more problems than it solves. We now purge more frequently
// and the former memory leaks in tables are fixed, so out-of-memory
// conditions should be rarer now anyhow TCS removed 1/2/04
if (progressDialog)
{
if (progressDialog->Increment() != progress_continue) // TCS rev 1/27/03
break;
}
// for data rows in a condensed report, we do not add a row.
// but we still need to update totals and subtotals
if (rowInfo.rowType == rowtype_condenseddata)
{
FillCondensedDataRow(rowInfo);
continue; // no more action needed for this row.
}
Boolean skipRows = false;
Boolean skipIfLastRow = (rowInfo.rowType == rowtype_subtitle ||
rowInfo.rowType == rowtype_grouptitle); // TCS 6/3/02
// start by inserting a row (or two rows, if we are at a page boundary)
UInt8 extraRows = 0; // TCS 8/6/01
if (IsPrinting())
{
extraRows = InsertPrintRow(skipIfLastRow); // rev TCS 11/26/00
}
else
{
InsertRow();
}
row = LastRow();
// spin the cursor
++spinner;
// we used to stop at 50 rows in the demo version TCS removed 11/9/03
// fill some basic data into the hidden rows at far right
SetCellValue(row, mNumDisplayCols + cRowTypeOffset, rowInfo.rowType);
SetCellValue(row, mNumDisplayCols + cClassIDOffset, rowInfo.classID);
SetCellValue(row, mNumDisplayCols + cItemIDOffset, rowInfo.itemID);
// the cell text we fill in depends on the row type
switch (rowInfo.rowType)
{
case rowtype_notice: // BEGINNING TABLE NOTICE (before table is updated)
SetCellText(row, 1, TCS_GetMsgString(msgID_ReportWarning));
break;
case rowtype_title: // COLUMN TITLES (very first row)
FillTableTitles(row);
mFirstDataRow = 2;
break;
case rowtype_noitems: // NO ITEMS NOTICE (empty table)
SetCellText(row, 1, TCS_GetMsgString(msgID_ReportNoItems));
break;
case rowtype_subtitle: // SUBTITLES (top of each breakdown)
case rowtype_grouptitle: // TCS 4/22/02
// fetch text from the object. Note that this also
// allows the subtotal breakdown object to do any prep work
textString = GetSubtitleText(breakdownType, rowInfo);
if (rowInfo.rowType == rowtype_grouptitle) // TCS 7/11/02
{
mGroupName = textString;
textString.Capitalize(cEachLetterCaps);
}
// fill in subtitle text
SetCellText(row, 1, textString);
break;
case rowtype_condensedtotal: // SUBTITLE PLUS AMOUNTS // TCS 5/30/01
FillCondensedTotalRow(row, rowInfo, breakdownType); // TCS rev 2/24/04
break;
case rowtype_data: // BASIC DATA ROWS
case rowtype_categoryitem: // CATEGORY REPORT-- ITEM INFO TCS 4/15/02
FillDataRow(row, rowInfo);
break;
case rowtype_categorycosts: // JOB COST BREAKDOWNS // TCS 8/5/01
case rowtype_unallocatedcat:
// fetch category costs from our stored array
FillJobCostRow(row, rowInfo, extraRows);
// update the row count TCS 4/17/02
mLastDataRow = row;
break;
case rowtype_subtotal: // SUBTOTAL ROWS
FillSubtotalRow(row);
break;
case rowtype_grandtotal: // GRAND TOTAL ROW
FillGrandTotalRow(row);
break;
case rowtype_grouptotal: // GROUP TOTAL ROW TCS 4/22/02
FillGroupTotalRow(row);
break;
case rowtype_blank: // BLANK ROW
// no need to do anything
break;
default:
TCS_DebugAlert("Oops, invalid row type in CReportTable::FillReportTableFromArray!");
break;
}
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillTableTitles TCS split & rev 4/4/04
fill in the first title row
*********************************************************************************/
void CReportTable::FillTableTitles(const TableIndexT row)
{
TableIndexT col;
CTextString textString;
if (mCondensed)
{
SColDataInfo colData;
// for condensed reports we only show titles where there are totals.
// we also skip the first col since the breakdown titles are there
for (col = 2; col <= mNumDisplayCols; col++) // TCS 4/4/04
{
mColumnData.FetchItemAt(col, colData);
if (colData.showSubtotal || colData.showGroupTotal || colData.showTotal)
{
textString = GetColTitle(col);
SetCellText(row, col, textString);
}
}
}
else
{
// for regular reports we fill in all the titles
for (col = 1; col <= mNumDisplayCols; col++)
{
textString = GetColTitle(col);
SetCellText(row, col, textString);
}
}
}
/*********************************************************************************
FillDataRow TCS split 2/24/04
fill in a data report row
*********************************************************************************/
void CReportTable::FillDataRow(const TableIndexT row, const SReportRowInfo &rowInfo)
{
// should we skip rows? // TCS 1/29/02 rev 2/24/04
Boolean skipRows = !ShowZeroItems() && SkipsZeroColumns(GetReportClassID());
Boolean boolValue;
SColDataInfo colData;
SLETableColInfo colInfo;
CTextString textString;
DB_PersistentObject *object = nil;
CMoney amount;
if (mHideOverhead && rowInfo.isSoftCost) // TCS 11/9/03
object = nil;
else if (rowInfo.classID && rowInfo.itemID)
object = gDBFile->GetOneObject(rowInfo.classID, rowInfo.itemID);
else
object = nil;
if (object)
{
DB_ObjectWatcher watcher(object);
// let the object do any prep work needed TCS 4/5/02
object->DoReportBreakdownPrep(GetReportClassID(), rowInfo);
// loop through the columns in the row and fill in text for each cell
for (TableIndexT col = 1; col <= mNumDisplayCols; col++)
{
mColumns.FetchItemAt(col, colInfo);
mColumnData.FetchItemAt(col, colData);
amount = 0;
if (FetchSpecialTableValue(colInfo.tag, rowInfo, textString, amount))
{
// if this table provides a special value,
// no need to look further TCS 4/10/02
}
else if (DB_ClassDescriptor::MemberIsNumber(rowInfo.classID, colInfo.tag))
{
// we fetch all number values as a money value. GetRoundedString
// will convert it to the correct format. Note that we currently round
// the original values so totals will match the display, even though they
// may not match the sum of the unrounded values. Should this be user controlled? %%%%%%%%%%%%%%%%
if (object->GetMemberValue(colInfo.tag, type_money, &amount))
{
if (mHideOverhead && object->IsSoftCostMultiplied(colInfo.tag) && mHardCostMultiplier > 1)
{
amount *= mHardCostMultiplier; // TCS 11/9/03
}
textString = amount.GetRoundedString(colData.dataRounding, GetFieldType(col), cRoundOriginalData);
}
else
{
textString = TCS_GetStockString(stockID_ReportTableNoValue);
}
}
else if (DB_ClassDescriptor::GetDataType(rowInfo.classID, colInfo.tag) == type_boolean)
{
// we display all booleans as checkmarks
if (object->GetMemberValue(colInfo.tag, type_boolean, &boolValue))
{
if (boolValue)
textString = TCS_GetStockString(stockID_Checkmark);
else
textString = TCS_GetStockString(stockID_ReportTableNoValue);
}
else
textString = TCS_GetStockString(stockID_ReportTableNoValue);
}
else if (object->GetMemberValue(colInfo.tag, type_cstring, &textString))
{
// we fetch everything else as a text string, which we
// tidy for one-line display
textString.ReplaceReturns(cSpaceString);
}
else
{ // couldn't get the member, so put dashes into the cell
textString = TCS_GetStockString(stockID_ReportTableNoValue);
}
// fill in the text
SetCellText(row, col, textString);
// update the row count TCS 4/17/02
mLastDataRow = row;
// should we update totals and subtotals?
if (colData.showSubtotal || colData.showTotal)
{
colData.subTotal += amount; // rev TCS 10/19/01
colData.groupTotal += amount; // TCS 7/11/02
colData.grandTotal += amount;
mColumnData.AssignItemAt(col, colData);
}
}
mSubTotalCount++; // TCS 2/24/04
mGrandTotalCount++;
mGroupTotalCount++;
}
} |