Link to: header | unit cost
directory
Copyright Turtle Creek Software 1996-2006. All Rights Reserved.
Comments
CCalculatorList
This class manages calculators in the Goldenseal accounting software,
estimating software, project
management software
and construction
accounting software.
a generic list of calculators for accounting reports. This is the parent for specific calculator
lists
SUPERCLASS = DB_DescribedPersistent
Constructor
/*********************************************************************************
default constructor
*********************************************************************************/
CCalculatorList::CCalculatorList()
{
mCalculationType = calc_list;
mTimeRange = date_standard;
mDateChoice = date_entered;
mField = mFieldMatch = 0;
mFieldValue = 0;
mObjectClass = 0;
mRoundingType = calc_roundpennies;
mCalcDirty = true;
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 CCalculatorList::CopyFrom(DB_PersistentObject *source, const UInt8 copyFlags)
{
THE_SUPERCLASS::CopyFrom(source, copyFlags);
CCalculatorList *src = TCS_SAFE_CAST(source, CCalculatorList);
TCS_FailNILMsg(src, TCS_GetErrString(errID_BadRecord));
//mCurrentValue = src->mCurrentValue;
TCS_BlockMove(&src->mCalculationType, &mCalculationType, cCopyFileLength);
}/*********************************************************************************
GetFileLength
return the file length used by this object
*********************************************************************************/
NeoSize CCalculatorList::GetFileLength(const CNeoFormat *aFormat) const
{
return THE_SUPERCLASS::GetFileLength(aFormat) +
mCurrentValue.FileLength(cStandardTextLen) + // rev TCS 3/14/00
cFileLength;
}/*********************************************************************************
GetMemberValue
return the value of the member with the given tag
*********************************************************************************/
Boolean CCalculatorList::GetMemberValue(const NeoTag aTag, const NeoTag aType,
void *aValue) const
{
switch (aTag)
{
case tag_calculationtype:
return ConvertEnumMember(mCalculationType, MENU_CalculationTypes, aValue, aType);
break;
case tag_timerange:
return ConvertEnumMember(mTimeRange, MENU_CalcTimeRanges, aValue, aType);
break;
case tag_date:
return ConvertEnumMember(mDateChoice, MENU_CalcDateChoices, aValue, aType);
break;
case tag_rounding:
return ConvertEnumMember(mRoundingType, MENU_RoundingTypes, aValue, aType);
break;
case tag_field: // it's a popup, but uses member tag, not an enum
return ConvertMember(&mField, type_long, aValue, aType);
break;
case tag_fieldmatch:
return ConvertMember(&mFieldMatch, type_long, aValue, aType);
break;
case tag_fieldvalue:
{ // we fetch the object id class from the descriptor tag
DB_ClassDescriptor *desc =
DB_ClassDescriptor::GetExtendedClassDescriptor(mObjectClass);
TCS_FailNILMsg(desc, TCS_GetErrString(errID_BadDescriptor));
SMemberInfo memberInfo;
desc->GetMemberInfo(mFieldMatch, &memberInfo);
if (memberInfo.type == type_objectid) // it's an object rev TCS 11/5/99
return ConvertObjectIDMember(mFieldValue, memberInfo.params, aValue, aType);
else // must be an enum
return ConvertMember(&mFieldValue, type_long, aValue, aType);
}
break;
case tag_fieldvalueid:
{ // we fetch the object id class from the descriptor tag
DB_ClassDescriptor *desc =
DB_ClassDescriptor::GetExtendedClassDescriptor(mObjectClass);
TCS_FailNILMsg(desc, TCS_GetErrString(errID_BadDescriptor));
SMemberInfo memberInfo;
desc->GetMemberInfo(mFieldMatch, &memberInfo);
SInt32 classID = memberInfo.params;
return ConvertMember(&classID, type_long, aValue, aType);
}
break;
// we used to fill this in with a calculated value, but that was a nuisance
// to get when exporting. So the viewer now fills in current value
// rather than doing it here TCS rev 11/10/02
case tag_currentvalue: // rev TCS 9/1/99, rev TCS 2/6/01
return ConvertMember(&mCurrentValue, type_cstring, aValue, aType);
break;
case tag_isdirty:
return ConvertBitFieldMember(mCalcDirty, aValue, aType);
break;
case tag_layouttag: // TCS 4/30/01
{
CTextString outString = CCalculatorList::GetCalculatorTagString(GetDBClassID(), GetDBID());
return ConvertMember(&outString, type_cstring, aValue, aType);
}
break;
//mObjectClass is set in subclasses
default:
return THE_SUPERCLASS::GetMemberValue(aTag, aType, aValue);
break;
}
}/*********************************************************************************
SetMemberValue
set the value of the member with the given tag
*********************************************************************************/
Boolean CCalculatorList::SetMemberValue(const NeoTag aTag, const NeoTag aType,
const void *aValue)
{
switch (aTag)
{
case tag_calculationtype:
return ConvertMember(aValue, aType, &mCalculationType, type_enum);
break;
case tag_timerange:
return ConvertMember(aValue, aType, &mTimeRange, type_enum);
break;
case tag_date:
return ConvertMember(aValue, aType, &mDateChoice, type_enum);
break;
case tag_rounding:
return ConvertMember(aValue, aType, &mRoundingType, type_enum);
break;
case tag_field: // it's a popup, but uses member tag, not an enum
return ConvertMember(aValue, aType, &mField, type_long);
break;
case tag_fieldmatch:
return ConvertMember(aValue, aType, &mFieldMatch, type_long);
break;
case tag_fieldvalue:
return ConvertMember(aValue, aType, &mFieldValue, type_objectid);
break;
case tag_transaction:
return ConvertMember(aValue, aType, &mObjectClass, type_enum);
break;
case tag_currentvalue: // rev TCS 9/1/99
if (IsSimpleNumber())
return SafeConvertString(aValue, aType, &mCurrentValue);
else
{
if (IsInDatabase()) // TCS 5/31/00
{
TCS_TokenErrorAlert(errID_BadLengthChange, DB_ClassDescriptor::GetClassName(GetDBClassID()));
return false;
}
else
{
mCurrentValue.MakeNull();
return true;
}
}
break;
case tag_isdirty:
mCalcDirty = ConvertDataToBitField(aValue, aType);
return true;
break;
case tag_fieldtype: // calculated, no need to set TCS 9/13/99
case tag_layouttag: // TCS 4/30/01
return true;
break;
default:
return THE_SUPERCLASS::SetMemberValue(aTag, aType, aValue);
break;
}
}/*********************************************************************************
ReadObject
read the persistent object's data in from a stream
*********************************************************************************/
void CCalculatorList::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;
ReadTextFromStream(aStream, &mCurrentValue);
/// aStream->ReadChunk(&mCalculationType, cFileLength);
mCalculationType = aStream->ReadChar(); // mfs_sa rev 20feb2k3
mRoundingType = aStream->ReadChar();
mTimeRange = aStream->ReadChar();
mDateChoice = aStream->ReadChar();
mObjectClass = aStream->ReadChar();
*((UInt8*)&mObjectClass + sizeof(mObjectClass)) = aStream->ReadBits(2); // --Bitfield
mField = aStream->ReadTag();
mFieldMatch = aStream->ReadTag();
mFieldValue = aStream->ReadID();
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 CCalculatorList::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);
THE_SUPERCLASS::WriteObject(aStream, aTag);
WriteTextToStream(aStream, mCurrentValue, cStandardTextLen); // removed -1 TCS 2/28/00
/// aStream->WriteChunk(&mCalculationType, cFileLength);
aStream->WriteChar(mCalculationType); // mfs_sa rev 20feb2k3
aStream->WriteChar(mRoundingType);
aStream->WriteChar(mTimeRange);
aStream->WriteChar(mDateChoice);
aStream->WriteChar(mObjectClass);
aStream->WriteChar(*((UInt8*)&mObjectClass + sizeof(mObjectClass))); // --Bitfield
aStream->WriteTag(mField);
aStream->WriteTag(mFieldMatch);
aStream->WriteID(mFieldValue);
aStream->WriteEndSafetyTag(mEndSafetyTag, this);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
ExportMember TCS 9/5/00
export the given member to a cstring. We override to export the tag
values as a 4-letter tag rather than a huge number
*********************************************************************************/
Boolean CCalculatorList::ExportMember(const TagType tag, CTextString *outString)
{
TCS_FailNILMsg(outString, TCS_GetErrString(errID_BadString));
if (tag == tag_field)
{
*outString = TCS_GetTagString(mField);
return true;
}
else if (tag == tag_fieldmatch)
{
*outString = TCS_GetTagString(mFieldMatch);
return true;
}
else
return THE_SUPERCLASS::ExportMember(tag, outString);
}
/*********************************************************************************
ImportMember TCS 9/5/00
we attempt to parse the tag values from 4-letter tags.
*********************************************************************************/
SInt32 CCalculatorList::ImportMember(const TagType tag, const CTextString &inString)
{
if (tag == tag_field)
{
mField = inString.GetTagValue();
return import_OK;
}
else if (tag == tag_fieldmatch)
{
mFieldMatch = inString.GetTagValue();
return import_OK;
}
else
return THE_SUPERCLASS::ImportMember(tag, inString);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
SetDisplayDirty TCS 12/19/00
respond to a change in report conditions, and return whether a recalc is needed
*********************************************************************************/
Boolean CCalculatorList::SetDisplayDirty(const UInt8 source)
{
switch (source)
{
case calc_startup: // always reset on report startup
mDisplayDirty = true;
return true;
break;
case calc_completed: // just recalculated, so we can turn off the flag
mDisplayDirty = false;
return true;
break;
case calc_daterange: // we may have changed
if (mCalculationType == calc_currbalance)
return false;
else
{
mDisplayDirty = true;
return true;
}
break;
return false;
case calc_datefieldchange: // no need to be dirty for other changes
case calc_accountchange:
case calc_matchchange:
case calc_recordrange:
case calc_breakdownchange:
default:
return false;
break;
}
}
/*********************************************************************************
HasCalculator
return whether a calculator exists for the given tag
*********************************************************************************/
Boolean CCalculatorList::HasCalculator(const TagType tag)
{
DBid calculatorClass = GetCalculatorClassID(tag);
DBid calculatorID = GetCalculatorItemID(tag);
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
return gDBFile->ObjectExists(calculatorClass, calculatorID);
}
/*********************************************************************************
IsValidCalculatorTag TCS 3/1/01
return if the given tag is a valid calculator tag
*********************************************************************************/
Boolean CCalculatorList::IsValidCalculatorTag(const TagType tag)
{
DBid itemID = GetCalculatorItemID(tag);
DBid itemClass = GetCalculatorClassID(tag);
return (itemClass && itemID && itemID < cMaxCalculatorValue);
}
/*********************************************************************************
GetCalculatorTag
return a field tag for a calculated field
*********************************************************************************/
TagType CCalculatorList::GetCalculatorTag(const DBid classValue, const DBid fieldValue)
{
CTextString tagString = GetCalculatorTagString(classValue, fieldValue);
return tagString.GetTagValue();
}
/*********************************************************************************
GetCalculatorTagString TCS split 4/30/01
return a field tag for a calculated field
*********************************************************************************/
CTextString CCalculatorList::GetCalculatorTagString(const DBid classValue, const DBid fieldValue)
{
DBid outTag = 0;
CTextString tagString;
// fieldValue should never be >999
TCS_FailNILMsg(fieldValue <= cMaxCalculatorValue, TCS_GetErrString(errID_BadCalculator));
tagString = GetCalculatorTagPrefix(classValue); // first character
if (tagString.Length())
{
CTextString lastDigits;
lastDigits.SetFromDigits(fieldValue, 3);
tagString.Append(lastDigits); // chars 2-4
}
return tagString;
}/*********************************************************************************
GetCalculatorItemID
return the calculator item ID from the given tag
*********************************************************************************/
DBid CCalculatorList::GetCalculatorItemID(const TagType tag)
{
CTextString numberString, tagString;
// convert the number to a 4-digit text tag
tagString.SetFromTag(tag);
// get the last 3 digits
numberString = tagString.GetSubString(2,4);
// if it's a valid number, return it.
if (!numberString.IsValidNumber(cIntegerOnly, cDontAllowBlanks, cDontAllowPunctuation)) // TCS 4/4/01
return 0;
else
return numberString.GetIntegerValue(); // rev TCS 11/2/99, rev 4/4/01
}
/*********************************************************************************
GetCalculatorClassID (static)
return the class from a calculator tag
NOTE- to get calculator class id for a dimension calculation type, use the
version in CCalcLocation
*********************************************************************************/
DBid CCalculatorList::GetCalculatorClassID(const TagType tag)
{
CTextString tagString = TCS_GetTagString(tag);
char *startChar = tagString.GetCharPtr();
switch (*startChar)
{
case 'A':
return id_CalcAccount;
break;
case 'B' :
return id_CalcList;
break;
case 'c' : // custom fields TCS 4/3/01
return id_ObjectClassInfo;
break;
case 'D' :
return id_CalcDimension;
break;
case 'E' :
return id_CalcBreakdown;
break;
case 'K' :
return id_CalcLocation; // TCS 5/10/01
break;
case 'L' :
return id_LocationDimension; // TCS 5/9/01
break;
case 'M' :
return id_CalcCustom;
break;
case 'N' : // TCS 1/10/01
return id_Dimension;
break;
case 'R' :
return id_CalcTransaction; // TCS 3/19/02
break;
case 'T':
return id_CalcIncomeTax;
break;
case 'U' :
return id_CalcUtilityAccount;
break;
case 'W':
return id_CalcPayrollTax;
break;
case 'X' :
return id_CalcSummation;
break;
default:
return 0;
break;
}
}
/*********************************************************************************
GetCalculatorTagPrefix
return the first character of a calculator tag
*********************************************************************************/
CTextString CCalculatorList::GetCalculatorTagPrefix(const DBid classValue)
{
switch (classValue)
{
case id_CalcAccount :
return CTextString("A");
break;
case id_CalcList:
return CTextString("B");
break;
case id_ObjectClassInfo: // custom fields TCS 4/3/01
return CTextString("c");
break;
case id_CalcDimension :
return CTextString("D");
break;
case id_CalcBreakdown:
return CTextString("E");
break;
case id_CalcLocation :
return CTextString("K");
break;
case id_LocationDimension: // TCS 5/9/01
return CTextString("L");
break;
case id_CalcCustom :
return CTextString("M");
break;
case id_Dimension: // TCS 1/10/01
return CTextString("N");
break;
case id_CalcTransaction : // TCS 3/19/02
return CTextString("R");
break;
case id_CalcIncomeTax:
return CTextString("T");
break;
case id_CalcUtilityAccount :
return CTextString("U");
break;
case id_CalcPayrollTax:
return CTextString("W");
break;
case id_CalcSummation :
return CTextString("X");
break;
default:
return cEmptyString;
break;
}
}
/*********************************************************************************
GetCalculatorTagStart
return the first value of a calculator tag series
*********************************************************************************/
TagType CCalculatorList::GetCalculatorTagStart(const DBid classValue)
{
switch (classValue)
{
case id_CalcAccount :
return 'A000';
break;
case id_CalcList :
return 'B000';
break;
case id_ObjectClassInfo : // custom fields TCS 4/3/01
return 'c000';
break;
case id_CalcDimension :
return 'D000';
break;
case id_CalcBreakdown:
return 'E000';
break;
case id_CalcLocation :
return 'K000';
break;
case id_LocationDimension :
return 'L000';
break;
case id_CalcCustom :
return 'M000';
break;
case id_Dimension: // TCS 1/10/01
return 'N000';
break;
case id_CalcTransaction: // TCS 3/19/02
return 'R000';
break;
case id_CalcIncomeTax:
return 'T000';
break;
case id_CalcUtilityAccount :
return 'U000';
break;
case id_CalcPayrollTax:
return 'W000';
break;
case id_CalcSummation :
return 'X000';
break;
default:
return 0;
break;
}
}
/*********************************************************************************
GetCalculatorName TCS 7/10/01
return the name of the calculator with the given tag
*********************************************************************************/
CTextString CCalculatorList::GetCalculatorName(const TagType tag)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
DBClass calcClass = GetCalculatorClassID(tag);
DBid calcID = GetCalculatorItemID(tag);
return gDBFile->GetObjectName(calcClass, calcID);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
GetDisplayCString
return the calculated text
*********************************************************************************/
CTextString CCalculatorList::GetDisplayCString(const FormatType format, const Boolean alwaysUpdate)
{
if (IsSimpleNumber())
{ // we'll recalc and store the value
SInt32 runLimit = 0;
UpdateCalculatorValue(runLimit, format, alwaysUpdate);
return mCurrentValue;
}
else
{ // no storage, so just build it on the fly
CTextString valueString;
GetCalculation(&valueString, mField, mObjectClass, mCalculationType,
records_allitems, mRoundingType, mFieldMatch,
mFieldValue, mDateChoice, format);
return valueString;
}
}
/*********************************************************************************
GetStarterCString TCS 12/18/00
return a starter value
*********************************************************************************/
CTextString CCalculatorList::GetStarterCString(const FormatType format) const
{
CTextString outValue;
if (IsSimpleNumber())
{
UInt8 fieldType = GetFieldType();
CMoney zero = 0;
switch (fieldType)
{
case fieldtype_integer:
case fieldtype_posinteger:
outValue = zero.GetIntegerString();
break;
case fieldtype_number:
outValue = zero.GetNumberString();
break;
case fieldtype_money:
case fieldtype_emoney:
default:
outValue = zero.GetFormatString(format); // TCS rev 5/7/04
break;
case fieldtype_percent:
outValue = zero.GetPercentString();
break;
}
}
return outValue;
}
/*********************************************************************************
GetSampleCString
return the calculated text
*********************************************************************************/
CTextString CCalculatorList::GetSampleCString() const
{
CTextString valueString;
GetCalculation(&valueString, mField, mObjectClass, mCalculationType,
records_allitems, mRoundingType, mFieldMatch,
mFieldValue, mDateChoice);
return valueString;
}
/*********************************************************************************
GetCalculatorMoney
return the calculated money value.
*********************************************************************************/
CMoney CCalculatorList::GetCalculatorMoney()
{
if (mCalculationType == calc_sum || mCalculationType == calc_count) // rev TCS 11/12/99
{
if (IsCalcDirty())
{
CTextString mCurrentValue;
GetCalculation(&mCurrentValue, mField, mObjectClass, mCalculationType,
records_allitems, mRoundingType, mFieldMatch,
mFieldValue, mDateChoice);
return CMoney(mCurrentValue);
}
else
return CMoney(mCurrentValue);
}
else
return CMoney(0,0);
}
/*********************************************************************************
GetCalculatorInteger
return the integer value of a calculator. Note that if we're dirty, we don't
update the stored value, since this is a const function and the compiler
doesn't like changes
*********************************************************************************/
SInt32 CCalculatorList::GetCalculatorInteger()
{
if (mCalculationType == calc_sum || mCalculationType == calc_count)
{
// fetch the value string
if (IsCalcDirty())
{
GetCalculation(&mCurrentValue, mField, mObjectClass, mCalculationType,
records_allitems, mRoundingType, mFieldMatch,
mFieldValue, mDateChoice);
}
// convert it to a number
return mCurrentValue.GetNumberValue(); // rev TCS 11/2/99
}
else
return 0;
}
/*********************************************************************************
UsesField TCS 4/8/99
return whether this object uses the given field. We override, since we
want to know whether the parent transaction class contains the field
*********************************************************************************/
Boolean CCalculatorList::UsesField(const TagType tag)
{
DB_ClassDescriptor *desc = DB_ClassDescriptor::GetExtendedClassDescriptor(mObjectClass);
if (desc)
{
return desc->HasMember(tag);
}
else
return false;
}
/*********************************************************************************
HasCircularReference TCS moved 5/21/03
does the second calculator depend on the first one?
*********************************************************************************/
Boolean CCalculatorList::HasCircularReference(const DBClass calcClass, const DBid calcID,
const DBClass thisClass, const DBid thisID)
{
#if TCS_MULTIUSER
if (gIsClient)
{
// if this is a client we send a message to the server
// so we don't have to fetch a zillion objects
CTCS_NetworkMessage ioMessage(msg_CheckCircularCalc);
ioMessage.SetClassID(calcClass);
ioMessage.SetRecordID(calcID);
ioMessage.SetFirstByte(thisClass);
ioMessage.SetLongValue(thisID);
ioMessage.SetNeedsReply();
if (gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning))
{
return ioMessage.GetSecondByte();
}
else
return false;
}
#endif
// fetch the calculator and see if it depends on us
DB_PersistentObject *calc = gDBFile->GetOneObject(calcClass, calcID);
if (calc)
{
DB_ObjectWatcher watcher(calc);
return calc->DependsOnCalc(thisClass, thisID);
}
return false;
}
/*********************************************************************************
ShowsObjectValue (static) TCS 11/19/02
return whether the given data type can show enum or object id values
*********************************************************************************/
Boolean CCalculatorList::ShowsObjectValue(const TagType dataType)
{
switch (dataType)
{
case type_objectid:
case type_objectidx:
case type_cstringcv:
case type_enum:
case type_objclass:
return true;
break;
default:
return false;
break;
}
}
/*********************************************************************************
GetFieldType TCS 8/21/99 rev 9/11/99
get the field type. If it is not stored, we fetch it from the tag. This allows
us to support previous calculators that were already created with no stored tag
*********************************************************************************/
UInt8 CCalculatorList::GetFieldType() const
{
return DB_ClassDescriptor::GetFieldTypeForTag(mObjectClass, mField);
}
/*********************************************************************************
IsNumber TCS 8/21/99
return whether this calculator produces a number
*********************************************************************************/
Boolean CCalculatorList::IsNumber() const
{
UInt8 fieldType = GetFieldType();
switch (fieldType)
{
case fieldtype_integer:
case fieldtype_posinteger:
case fieldtype_number:
case fieldtype_money:
case fieldtype_emoney:
case fieldtype_percent:
return true;
break;
default:
return false;
break;
}
}
/*********************************************************************************
CalcIsSimpleNumber (static) TCS 12/13/00 renamed 4/30/02
return whether a calculation method produces a simple number
*********************************************************************************/
Boolean CCalculatorList::CalcIsSimpleNumber(const UInt8 calcType)
{
switch (calcType)
{
case calc_sum:
case calc_count:
case calc_add:
case calc_subtract:
case calc_multiply:
case calc_divide:
case calc_greaterof:
case calc_lesserof:
case calc_addconstant:
case calc_subtractconstant:
case calc_multiplyconstant:
case calc_divideconstant:
case calc_greaterofconstant:
case calc_lesserofconstant:
case calc_currbalance:
case calc_startdatebalance:
case calc_enddatebalance:
case calc_totalinrange:
case calc_changeinrange:
case calc_activeinrange:
case calc_squarerootof:
case calc_absolutevalue:
return true;
default:
return false;
}
}
/*********************************************************************************
IsSimpleNumber TCS 8/22/99
return whether this calculator produces a single number
*********************************************************************************/
Boolean CCalculatorList::IsSimpleNumber() const
{
return (mCalculationType == calc_sum || mCalculationType == calc_count);
}
/*********************************************************************************
IsMathable TCS 8/21/99
return whether this calculator produces a number, date or time
*********************************************************************************/
Boolean CCalculatorList::IsMathable() const
{
UInt8 fieldType = GetFieldType();
switch (fieldType)
{
case fieldtype_integer:
case fieldtype_posinteger:
case fieldtype_number:
case fieldtype_money:
case fieldtype_emoney:
case fieldtype_percent:
case fieldtype_date:
case fieldtype_time:
return true;
break;
default:
return false;
break;
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
Func_AppendFieldText
fetch text from a field in a database object, and append it to our delivered cstring.
*********************************************************************************/
Boolean CCalculatorList::Func_AppendFieldText(DB_PersistentObject *obj, TagType *fieldTag,
CTextString *text, UInt8 *roundingMethod,
SInt32 *fieldType)
{
TCS_FailNILMsg(obj, TCS_GetErrString(errID_BadObject)); // TCS 9/19/02
TCS_FailNILMsg(fieldTag, TCS_GetErrString(errID_BadPointer));
TCS_FailNILMsg(text, TCS_GetErrString(errID_BadString));
TCS_FailNILMsg(roundingMethod, TCS_GetErrString(errID_BadPointer));
TCS_FailNILMsg(fieldType, TCS_GetErrString(errID_BadPointer));
TCS_TRY
{ // fetch the member text
CTextString thisText;
// fetch the value and round if necessary
switch (*fieldType)
{
case type_money:
case type_emoney:
case type_number:
case type_percent:
case type_long:
//case type_ulong:
//case type_float:
//case type_double:
//case type_longdouble:
case type_real:
{
CMoney moneyValue;
if (obj->GetMemberValue(*fieldTag, type_money, &moneyValue))
thisText = CMoney::GetRoundedString(moneyValue, *roundingMethod, *fieldType);
else
TCS_SysBeep(); // we beep and continue rev TCS 8/22/00
}
break;
default: // no need to round other types
if (!obj->GetMemberValue(*fieldTag, type_cstring, &thisText))
TCS_SysBeep(); // we beep and continue rev TCS 8/22/00
break;
}
text->Append(thisText);
text->Append(cReturnChar); // rev TCS 9/11/02
return false; // we want to keep going
}
TCS_CATCH {}
return true; // problems, so let's terminate
}
/*********************************************************************************
Func_AddFieldValue
function to fetch the total value of a member field from a database object.
*********************************************************************************/
Boolean CCalculatorList::Func_AddFieldValue(DB_PersistentObject *obj, TagType *fieldTag,
CMoney *value)
{
TCS_FailNILMsg(obj, TCS_GetErrString(errID_BadObject)); // TCS 9/19/02
TCS_FailNILMsg(fieldTag, TCS_GetErrString(errID_BadPointer));
TCS_FailNILMsg(value, TCS_GetErrString(errID_BadPointer));
TCS_TRY
{ // fetch the member value
CMoney thisMoney(0,0);
if (obj->GetMemberValue(*fieldTag, type_money, &thisMoney))
*value += thisMoney;
else
TCS_SysBeep(); // we'll beep but not give an error msg TCS 8/22/00
return false;
}
TCS_CATCH {}
return true; // problems, so let's terminate
}
/*********************************************************************************
Func_AddFieldCount TCS 7/27/99
function to fetch the total count of a member field from a database object.
*********************************************************************************/
Boolean CCalculatorList::Func_AddFieldCount(DB_PersistentObject *obj, TagType *fieldTag,
SInt32 *value)
{
TCS_FailNILMsg(obj, TCS_GetErrString(errID_BadObject));
TCS_FailNILMsg(fieldTag, TCS_GetErrString(errID_BadPointer));
TCS_FailNILMsg(value, TCS_GetErrString(errID_BadPointer));
TCS_TRY
{ // fetch the member value
CMoney thisMoney(0,0);
TCS_ASSERTMsg(obj->GetMemberValue(*fieldTag, type_money, &thisMoney),
TCS_GetValueErrString(*fieldTag));
if (thisMoney.IsPositive())
(*value) ++; // increment the value (not the address)
return false;
}
TCS_CATCH {}
return true; // problems, so let's terminate
}
/*********************************************************************************
UpdateCalculatorValue
calculate a value for the display string. This method should only be
used for simple lists
*********************************************************************************/
void CCalculatorList::UpdateCalculatorValue(SInt32 &/*runLimit*/, const FormatType format,
const Boolean alwaysUpdate)
{
if (!IsSimpleNumber())
{
TCS_DebugAlert("Oops, improper call to CCalculatorList::Calculate!");
}
else if (mCalcDirty || mDisplayDirty || alwaysUpdate) // rev TCS 4/5/01
{
// length may change, so remove from dbase
DB_ObjectTempRemover remover (this); // TCS 8/26/03
if (remover.WasRemoved())
{
// update the member
GetCalculation(&mCurrentValue, mField, mObjectClass, mCalculationType,
records_allitems, mRoundingType, mFieldMatch,
mFieldValue, format, mDateChoice);
}
// mark as clean
mDisplayDirty = false;
mCalcDirty = false;
}
// if not dirty, no need to do anything
}
/*********************************************************************************
GetCalculation (static) TCS rev 6/8/99
the static method that fills in a calculated value for calculator objects
and the calculator viewer. Note that for lists, this is just a SAMPLE with
just cSampleListItemMax items displayed.
*********************************************************************************/
Boolean CCalculatorList::GetCalculation(CTextString *outString, const TagType fieldTag,
const UInt8 objectClass, const UInt8 calculationType,
const UInt8 recordRange, const UInt8 roundingMethod,
const TagType matchTag, const DBid matchValue,
const FormatType /*format*/, const UInt8 dateChoice,
const Boolean limitRange)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
TCS_FailNILMsg(outString, TCS_GetErrString(errID_BadString));
NeoUsed(dateChoice);
#if TCS_MULTIUSER
if (gIsClient)
{
// a client fetches a value from the server TCS 6/7/03
// so we don't need to access a zillion objects
CTCS_NetworkMessage ioMessage(msg_GetCalculation);
ioMessage.SetRecordID(fieldTag);
ioMessage.SetClassID(objectClass);
ioMessage.SetFirstByte(calculationType);
ioMessage.SetSecondByte(recordRange);
ioMessage.SetThirdByte(roundingMethod);
ioMessage.SetFourthByte(dateChoice);
ioMessage.SetLongValue(matchTag); // TCS 12/17/03
ioMessage.SetSecondLongValue(matchValue);
ioMessage.SetThirdLongValue(limitRange);
if (limitRange)
ioMessage.SetLongValue(1);
else
ioMessage.SetLongValue(0);
ioMessage.SetNeedsReply();
if (gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning))
{
// fetch the string
CTCS_NetworkStream stream(&ioMessage);
if (stream.ReadFromMessage()) // TCS 8/11/03
{
ReadTextFromStream(&stream, outString);
}
return true;
}
else
return false;
}
#endif
CMoney value(0,0);
SInt32 count = 0;
// transfer values so the compiler doesn't complain about const-ness
TagType field = fieldTag;
UInt8 rounding = roundingMethod;
SInt32 maxCount = limitRange ? cSampleListItemMax : 0;
SInt32 dataType = DB_ClassDescriptor::GetDataType(objectClass, fieldTag);
// do we have a field to show?
if (!field || !objectClass)
{
*outString = value.GetCurrencyString();
}
// branch depending on the calculation type
// %%%INCOMPLETE %%%% need to work in mTimeRange, matchTag and matchValue.
// right now we use all available functions in DoForEachFoundMax etc.
// Rather than rewriting that for more void parameters, we should probably
// rewrite this to just get an object ID array from gDBFile, and then
// and do the looping here. It will be cleaner and easier to debug.
// avoid all those ugly void pointers.
else if (calculationType == calc_list)
{ // ******** it's a list *********
switch (recordRange) // TCS 6/7/99
{
case records_flaggeditems:
gDBFile->DoForEachFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
objectClass, maxCount, &field,
outString, &rounding, &dataType);
break;
case records_founditems:
gDBFile->DoForEachFoundMax((BooleanActionFunc)Func_AppendFieldText,
objectClass, maxCount, &field,
outString, &rounding, &dataType);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
objectClass, maxCount, &field,
outString, &rounding, &dataType);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
objectClass, maxCount, &field,
outString, &rounding, &dataType);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AppendFieldText,
objectClass, &field, outString,
&rounding, &dataType);
break;
case records_allitems:
default:
gDBFile->DoForEachMax((BooleanActionFunc)Func_AppendFieldText,
objectClass, maxCount, &field,
outString, &rounding, &dataType);
break;
case records_firstitem:
gDBFile->DoForFirstRecord((BooleanActionFunc)Func_AppendFieldText,
objectClass, &field, outString,
&rounding, &dataType);
break;
}
}
else if (calculationType == calc_sum)
{ // ******** it's a sum *********
switch (recordRange) // TCS 6/7/99
{
case records_flaggeditems:
gDBFile->DoForEachFlagged((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
case records_founditems:
gDBFile->DoForEachFound((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlagged((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlagged((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
case records_allitems:
default: // changed from DoForEachUntil TCS 1/26/04
gDBFile->DoForEach((BooleanActionFunc)Func_AddFieldValue,
objectClass, &field, &value);
break;
}
*outString = CMoney::GetRoundedString(value, roundingMethod, dataType);
}
else
{ // ******** it's a count *********
switch (recordRange) // TCS 7/27/99
{
case records_flaggeditems:
gDBFile->DoForEachFlagged((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
case records_founditems:
gDBFile->DoForEachFound((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlagged((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlagged((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
case records_allitems:
default: // changed from DoForEachUntil TCS 1/26/04
gDBFile->DoForEach((BooleanActionFunc)Func_AddFieldCount,
objectClass, &field, &count);
break;
}
*outString = count;
}
return true;
}
/*********************************************************************************
GetAdjustedCalculation TCS 8/22/99
fill in a calc string with values adjusted by a value
*********************************************************************************/
Boolean CCalculatorList::GetAdjustedCalculation(CTextString *outString, const UInt8 recordRange,
const UInt8 /*calcMethod*/, const CMoney &/*multiplier*/,
const Boolean limitRange)
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
TCS_FailNILMsg(outString, TCS_GetErrString(errID_BadString));
CMoney value(0,0);
SInt32 theCount = 0;
SInt32 maxCount = limitRange ? cSampleListItemMax : 0;
SInt32 dataType = DB_ClassDescriptor::GetDataType(mObjectClass, mField);
// do we have a field to show?
if (!mField || !mObjectClass)
{
*outString = value.GetCurrencyString();
}
else if (mCalculationType == calc_list)
{ // ******** it's a list *********
switch (recordRange)
{
case records_flaggeditems:
gDBFile->DoForEachFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, maxCount, &mField,
outString, &mRoundingType, &dataType);
break;
case records_founditems:
gDBFile->DoForEachFoundMax((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, maxCount, &mField,
outString, &mRoundingType, &dataType);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, maxCount, &mField,
outString, &mRoundingType, &dataType);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlaggedMax((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, maxCount, &mField,
outString, &mRoundingType, &dataType);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, &mField,
outString, &mRoundingType, &dataType);
break;
case records_allitems:
default:
gDBFile->DoForEachMax((BooleanActionFunc)Func_AppendFieldText,
mObjectClass, maxCount, &mField,
outString, &mRoundingType, &dataType);
break;
}
}
else if (mCalculationType == calc_sum)
{ // ******** it's a sum *********
switch (recordRange) // TCS 6/7/99
{
case records_flaggeditems:
gDBFile->DoForEachFlagged((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
case records_founditems:
gDBFile->DoForEachFound((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlagged((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlagged((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
case records_allitems:
default: // changed from DoForEachUntil TCS 1/26/04
gDBFile->DoForEach((BooleanActionFunc)Func_AddFieldValue,
mObjectClass, &mField, &value);
break;
}
*outString = CMoney::GetRoundedString(value, mRoundingType, dataType);
}
else
{ // ******** it's a count *********
switch (recordRange) // TCS 7/27/99
{
case records_flaggeditems:
gDBFile->DoForEachFlagged((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
case records_founditems:
gDBFile->DoForEachFound((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
case records_flaggedandfound:
gDBFile->DoForEachFoundandFlagged((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
case records_unprinted:
gDBFile->DoForEachPrintFlagged((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
case records_activeitem:
gDBFile->DoForActiveRecord((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
case records_allitems:
default: // changed from DoForEachUntil TCS 1/26/04
gDBFile->DoForEach((BooleanActionFunc)Func_AddFieldCount,
mObjectClass, &mField, &theCount);
break;
}
*outString = theCount;
}
return true;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillDataReport TCS 9/7/02
fill in a diagnostic table that shows data field values.
*********************************************************************************/
void CCalculatorList::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);
FillFieldStringRow(table, stream, tag_currentvalue, mCurrentValue);
FillFieldEnumRow(table, stream, tag_calculationtype, mCalculationType, MENU_CalculationTypes);
FillFieldEnumRow(table, stream, tag_rounding, mRoundingType, MENU_RoundingTypes);
FillFieldEnumRow(table, stream, tag_timerange, mTimeRange, MENU_CalcTimeRanges);
FillFieldEnumRow(table, stream, tag_date, mDateChoice, MENU_CalcDateChoices);
if (GetDBClassID() == id_CalcAccount)
FillFieldEnumRow(table, stream, tag_objectclass, mObjectClass, MENU_AccountClasses);
else if (GetDBClassID() == id_CalcBreakdown)
FillFieldEnumRow(table, stream, tag_objectclass, mObjectClass, MENU_IOBreakdownTypes);
else if (GetDBClassID() == id_CalcList)
FillFieldEnumRow(table, stream, tag_objectclass, mObjectClass, MENU_IOListLayouts);
else
FillFieldEnumRow(table, stream, tag_objectclass, mObjectClass, MENU_IOTransactionTypes);
FillFieldBitRow(table, stream, "mCalcDirty", mCalcDirty, true);
FillFieldBitRow(table, stream, "mDisplayDirty", mDisplayDirty);
FillFieldStockRow(table, stream, stockID_Padding, -6, SInt32(filler));
CTextString fieldTag;
fieldTag.SetFromTag(mField);
FillFieldTagRow(table, stream, tag_field, cLongSize, fieldTag);
fieldTag.SetFromTag(mFieldMatch);
FillFieldTagRow(table, stream, tag_fieldmatch, cLongSize, fieldTag);
FillFieldTagRow(table, stream, tag_fieldvalue, cLongSize, mFieldValue);
FillEndSafetyTag(table, stream, mEndSafetyTag);
}
|