Accounting Software
Small Business Software Estimating Software
Unit Cost SoftwareConstruction Estimating SoftwareProject Estimating SoftwareCost Estimation SoftwareCost Estimating SoftwareConstruction Management SoftwareBusiness Management Software

Custom Calculators (Source Code)

Link to: header | unit cost directory

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

Comments

CCalcCustom

This class manages custom calculators in the Goldenseal accounting software,
estimating software, project management software and construction estimating software.

Custom calculators are available to do math on other calculators. We use
these in the balance sheet, income statement and other accounting reports.

SUPERCLASS = CCalculatorList

Constructor

/*********************************************************************************
default constructor
*********************************************************************************/
CCalcCustom::CCalcCustom()
{
mFirstCalculation = mSecondCalculation = 0;

mCalculationType = calc_add;
mFirstGroup = mSecondGroup = id_CalcAccount;
mRoundingType = calc_roundpennies;

mConstant = 0;

mCalcDirty = true;
mDisplayDirty = false;
filler = 0; // TCS 9/26/02

mCalcCustFiller = 0; // TCS 3/27/02

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

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

mCurrentValue = src->mCurrentValue;

TCS_BlockMove(&src->mFirstCalculation, &mFirstCalculation, cCopyFileLength);
}/*********************************************************************************

GetFileLength

return the file length used by this object

*********************************************************************************/
NeoSize CCalcCustom::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 CCalcCustom::GetMemberValue(const NeoTag aTag, const NeoTag aType,
void *aValue) const
{
switch (aTag)
{
case tag_firstcalculation:
return ConvertObjectIDMember(mFirstCalculation, mFirstGroup, aValue, aType);
break;

case tag_secondcalculation:
return ConvertObjectIDMember(mSecondCalculation, mSecondGroup, aValue, aType);
break;

case tag_calculationtype:
return ConvertEnumMember(mCalculationType, MENU_CalcCustomTypes, aValue, aType);
break;

case tag_firstgroup:
return ConvertEnumMember(mFirstGroup, MENU_CalcSources, aValue, aType);
break;

case tag_secondgroup:
return ConvertEnumMember(mSecondGroup, MENU_CalcSources, aValue, aType);
break;

case tag_rounding:
return ConvertEnumMember(mRoundingType, MENU_RoundingTypes, aValue, aType);
break;

case tag_currentvalue:
return ConvertMember(&mCurrentValue, type_cstring, aValue, aType);
break;

case tag_constant:
return ConvertMember(&mConstant, type_number, aValue, aType);
break;

case tag_layouttag: // TCS 4/30/01
{
CTextString outString = CCalculatorList::GetCalculatorTagString(id_CalcCustom, GetDBID());
return ConvertMember(&outString, type_cstring, aValue, aType);
}
break;

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

SetMemberValue

set the value of the member with the given tag

*********************************************************************************/
Boolean CCalcCustom::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_firstgroup:
return ConvertMember(aValue, aType, &mFirstGroup, type_enum);
break;

case tag_secondgroup:
return ConvertMember(aValue, aType, &mSecondGroup, type_enum);
break;

case tag_firstcalculation: // don't use ConvertDataToObjectID since uncertain class
return ConvertMember(aValue, aType, &mFirstCalculation, type_objectid);
break;

case tag_secondcalculation: // don't use ConvertDataToObjectID since uncertain class
return ConvertMember(aValue, aType, &mSecondCalculation, type_objectid);
break;

case tag_rounding:
return ConvertMember(aValue, aType, &mRoundingType, type_enum);
break;

case tag_currentvalue:
return SafeConvertString(aValue, aType, &mCurrentValue);
break;

case tag_constant:
return ConvertMember(aValue, aType, &mConstant, type_number);
break;

case tag_layouttag: // calculated, no need to set 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 CCalcCustom::ReadObject(CNeoStream *aStream, const TagType aTag)
{
TCS_FailNILMsg(aStream, TCS_GetErrString(errID_BadStream));

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

THE_SUPERCLASS::ReadObject(aStream, aTag);

ReadTextFromStream(aStream, &mCurrentValue);


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

mFirstCalculation = aStream->ReadID(); // mfs_sa rev 20feb2k3
mSecondCalculation = aStream->ReadID();

mCalculationType = aStream->ReadChar();
mFirstGroup = aStream->ReadChar();
mSecondGroup = aStream->ReadChar();
mRoundingType = aStream->ReadChar();

mConstant.ReadFromStream(aStream);
*((UInt8*)&mConstant + sizeof(mConstant)) = aStream->ReadBits(2); // --Bitfield

mCalcCustFiller = aStream->ReadChar();

mEndSafetyTag = aStream->ReadEndSafetyTag(this);

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

WriteObject

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

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

CNeoDebugExport checker(aStream, this, cCheckTooSmall);

THE_SUPERCLASS::WriteObject(aStream, aTag);

WriteTextToStream(aStream, mCurrentValue, cStandardTextLen); // removed -1 TCS 2/28/00


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

aStream->WriteID(mFirstCalculation); // mfs_sa rev 20feb2k3
aStream->WriteID(mSecondCalculation);

aStream->WriteChar(mCalculationType);
aStream->WriteChar(mFirstGroup);
aStream->WriteChar(mSecondGroup);
aStream->WriteChar(mRoundingType);

mConstant.WriteToStream(aStream);
aStream->WriteChar(*((UInt8*)&mConstant + sizeof(mConstant))); // --Bitfield

aStream->WriteChar(mCalcCustFiller);

aStream->WriteEndSafetyTag(mEndSafetyTag, this);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

PostNewRecord TCS 11/22/03

post info for a new record
*********************************************************************************/
void CCalcCustom::PostNewRecord(const UInt8 creationType)
{
PostUseCount(mFirstGroup, mFirstCalculation);
PostUseCount(mSecondGroup, mSecondCalculation);

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

PostDeletion TCS 11/22/03

post info from a deleted record
*********************************************************************************/
void CCalcCustom::PostDeletion(const Boolean postAudit)
{
PostUseCount(mFirstGroup, mFirstCalculation, cRemoveItem);
PostUseCount(mSecondGroup, mSecondCalculation, cRemoveItem);

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

PostRecordChanging TCS 11/22/03

a record will be changing. Post the change.
*********************************************************************************/
void CCalcCustom::PostRecordChanging(const Boolean accountChanging, const Boolean jobChanging)
{
PostUseCount(mFirstGroup, mFirstCalculation, cRemoveItem);
PostUseCount(mSecondGroup, mSecondCalculation, cRemoveItem);

// let the superclass do any posting
THE_SUPERCLASS::PostRecordChanging(accountChanging, jobChanging);
}
/*********************************************************************************

PostRecordChanged TCS 11/22/03

a record has changed. Post it.
*********************************************************************************/
void CCalcCustom::PostRecordChanged(const CMoney &oldAmount, const Boolean accountChanged,
const Boolean jobChanged)
{
PostUseCount(mFirstGroup, mFirstCalculation);
PostUseCount(mSecondGroup, mSecondCalculation);

// let the superclass do any posting
THE_SUPERCLASS::PostRecordChanged(oldAmount, accountChanged, jobChanged);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

GetCalculatorMoney TCS 9/16/99 rev TCS 12/18/00

return the calculated money value.

*********************************************************************************/
CMoney CCalcCustom::GetCalculatorMoney()
{
SInt32 runLimit = 0; // TCS 4/9/02
GetCalculation(&mCurrentValue, mFirstGroup, mFirstCalculation, mSecondGroup,
mSecondCalculation, mCalculationType, mRoundingType,
mConstant, runLimit, cDefaultMoneyFormat);
return CMoney(mCurrentValue);
}
/*********************************************************************************

GetFieldType TCS 9/11/99

get the field type.

*********************************************************************************/
UInt8 CCalcCustom::GetFieldType() const
{
UInt8 firstFieldType = 0, secondFieldType = 0;
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

DB_PersistentObject *firstCalculator =
gDBFile->GetOneObject(mFirstGroup, mFirstCalculation);

// if no calculator, just set an empty string and return
if (firstCalculator)
{
DB_ObjectWatcher firstWatcher(firstCalculator);
firstFieldType = firstCalculator->GetFieldType();

if (mCalculationType == calc_add || mCalculationType == calc_subtract ||
mCalculationType == calc_multiply || mCalculationType == calc_divide ||
mCalculationType == calc_greaterof || mCalculationType == calc_lesserof)
{
DB_PersistentObject *secondCalculator =
gDBFile->GetOneObject(mSecondGroup, mSecondCalculation);

if (secondCalculator)
{
DB_ObjectWatcher secondWatcher(secondCalculator);
secondFieldType = secondCalculator->GetFieldType();
}
}
}

return GetFieldType(firstFieldType, secondFieldType);
}/*********************************************************************************

GetFieldType (static) TCS 9/11/99

get the field type.

*********************************************************************************/
UInt8 CCalcCustom::GetFieldType(const UInt8 firstFieldType,
const UInt8 secondFieldType)
{
if (firstFieldType == fieldtype_emoney || secondFieldType == fieldtype_emoney)
return fieldtype_emoney;
else if (firstFieldType == fieldtype_money || secondFieldType == fieldtype_money)
return fieldtype_money;
else if ((firstFieldType == fieldtype_integer || firstFieldType == fieldtype_posinteger) &&
(secondFieldType == fieldtype_integer || secondFieldType == fieldtype_posinteger))
return fieldtype_integer;
else
return fieldtype_number;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

UpdateCalculatorValue TCS 1/27/99

calculate a value for the display string.

*********************************************************************************/
void CCalcCustom::UpdateCalculatorValue(SInt32 &runLimit, const FormatType format,
const Boolean alwaysUpdate)
{
runLimit++;
if (runLimit > cRecursionLimit) // TCS 4/9/02
{
TCS_DebugAlert("Oops, calculation too deep in CCalcCustom::UpdateCalculatorValue!");
return;
}

if (mCalcDirty || mDisplayDirty || alwaysUpdate) // rev TCS 4/5/01
{
// now we update the member
DB_ObjectTempRemover remover (this); // TCS 8/26/03

if (remover.WasRemoved())
{
GetCalculation(&mCurrentValue, mFirstGroup, mFirstCalculation, mSecondGroup,
mSecondCalculation, mCalculationType, mRoundingType,
mConstant, runLimit, format);
}

// reset dirty status
mDisplayDirty = false;
mCalcDirty = false;
}
}
/*********************************************************************************

GetDisplayCString TCS 6/14/00

return the calculated text

*********************************************************************************/
CTextString CCalcCustom::GetDisplayCString(const FormatType format, const Boolean alwaysUpdate)
{
SInt32 runLimit = 0;
UpdateCalculatorValue(runLimit, format, alwaysUpdate);
return mCurrentValue;
}
/*********************************************************************************

GetStarterCString TCS 12/18/00

return a starter value

*********************************************************************************/
CTextString CCalcCustom::GetStarterCString(const FormatType format) const
{
CMoney zero = 0;
return zero.GetFormatString(format); // rev TCS 5/7/04
}
/*********************************************************************************

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 CCalcCustom::GetCalculation(CTextString *outString, const UInt8 firstCalcClass,
const DBid firstCalc, const UInt8 secondCalcClass,
const DBid secondCalc, const UInt8 calculationType,
const UInt8 roundingMethod, const CMoney &constant,
SInt32 &runLimit, const FormatType format)
{

// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
TCS_FailNILMsg(outString, TCS_GetErrString(errID_BadString));

#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_GetCustomCalculation);
ioMessage.SetFirstByte(firstCalcClass);
ioMessage.SetLongValue(firstCalc);
ioMessage.SetSecondLongValue(calculationType);
ioMessage.SetThirdLongValue(runLimit);
ioMessage.SetFourthLongValue(format); // TCS 4/30/04
ioMessage.SetSecondByte(secondCalcClass);
ioMessage.SetThirdByte(calculationType);
ioMessage.SetFourthByte(roundingMethod);
ioMessage.SetMoneyValue(constant);

ioMessage.SetNeedsReply();

if (gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning))
{
// fetch the string
if (ioMessage.GetFirstByte())
{
CTCS_NetworkStream stream(&ioMessage);

if (stream.ReadFromMessage()) // TCS 8/11/03
{
ReadTextFromStream(&stream, outString);
}
return true;
}
else
return false;
}
else
return false;
}
#endif

CMoney value, secondValue;
UInt8 firstFieldType = 0, secondFieldType = 0;
CDate date;
CTextString listString;
// fetch the first calculation value
DB_PersistentObject *firstCalculator =
gDBFile->GetOneObject(firstCalcClass, firstCalc);

// if no calculator, just set an empty string and return
if (!firstCalculator)
{
outString->MakeNull();
return false;
}

DB_ObjectWatcher watcher(firstCalculator);

firstCalculator->UpdateCalculatorValue(runLimit, CMoney::GetDefaultMoneyFormat());
value = firstCalculator->GetCalculatorMoney();
firstFieldType =firstCalculator->GetFieldType();

// fetch the second calculation value
switch (calculationType)
{
case calc_add:
case calc_subtract:
case calc_multiply:
case calc_divide:
case calc_greaterof:
case calc_lesserof:
{
DB_PersistentObject *secondCalculator =
gDBFile->GetOneObject(secondCalcClass, secondCalc);

if (secondCalculator)
{
DB_ObjectWatcher watcher(secondCalculator);

secondCalculator->UpdateCalculatorValue(runLimit, CMoney::GetDefaultMoneyFormat());
secondValue = secondCalculator->GetCalculatorMoney();
secondFieldType = secondCalculator->GetFieldType();
}
}
break;

case calc_subtractconstant:
case calc_addconstant:
case calc_multiplyconstant:
case calc_divideconstant:
case calc_greaterofconstant:
case calc_lesserofconstant:
secondValue = constant;
break;

default:
TCS_DebugAlert("Oops, bad case in CCalcCustom::Calculate!");
break;
}

switch (calculationType)
{
case calc_add:
case calc_addconstant:
value += secondValue;
break;

case calc_subtract:
case calc_subtractconstant:
value -= secondValue;
break;

case calc_multiply:
case calc_multiplyconstant:
value *= secondValue;
break;

case calc_divide:
case calc_divideconstant:
if (secondValue.IsZero())
value = secondValue;
else
value = value / secondValue;
break;

case calc_greaterof:
case calc_greaterofconstant:
value = TCS_MAX(value, secondValue);
break;

case calc_lesserof:
case calc_lesserofconstant:
value = TCS_MIN(value, secondValue);
break;
}
// apply rounding
*outString = value.GetRoundedString(roundingMethod, GetFieldType(firstFieldType, secondFieldType));

return true;
}
/*********************************************************************************

DependsOnCalc TCS 9/7/99

return whether or not this calculator depends on the listed item

*********************************************************************************/
Boolean CCalcCustom::DependsOnCalc(const UInt8 inClassID, const DBid inItemID, Boolean deeply) const
{
if (inClassID == id_CalcCustom && inItemID == GetDBID())
{ // the item id is the same as our id, and naturally
// we depend on ourselves!
return true;
}
else
{ // look at the first calculator
if (mFirstGroup == inClassID)
{
if (mFirstCalculation == inItemID)
{ // the item is used directly
return true;
}
else if (deeply && (inClassID == id_CalcCustom || inClassID == id_CalcSummation))
{ // we're looking deeply, so pass it along
DB_PersistentObject *calculator =
gDBFile->GetOneObject(mFirstGroup, mFirstCalculation);

if (calculator)
{
DB_ObjectWatcher watcher(calculator);
if (calculator->DependsOnCalc(inClassID, inItemID, deeply))
return true;
}
}
}

// look at the second calculator
if (mSecondGroup == inClassID)
{
if (mSecondCalculation == inItemID)
{ // the item is used directly
return true;
}
else if (deeply && (inClassID == id_CalcCustom || inClassID == id_CalcSummation))
{ // we're looking deeply, so pass it along
DB_PersistentObject *calculator =
gDBFile->GetOneObject(mSecondGroup, mSecondCalculation);

if (calculator)
{
DB_ObjectWatcher watcher(calculator);
if (calculator->DependsOnCalc(inClassID, inItemID, deeply))
return true;
}
}
}
}
// if we got this far, it means we don't depend on the item
return false;
}
/*********************************************************************************

SetDisplayDirty TCS 12/19/00

respond to a change in report conditions, and return whether a recalc is needed.
We need to pass it along to any linked calculators

*********************************************************************************/
Boolean CCalcCustom::SetDisplayDirty(const UInt8 source)
{
Boolean needsUpdate = false;

DB_PersistentObject *calculator =
gDBFile->GetOneObject(mFirstGroup, mFirstCalculation);

if (calculator)
{
DB_ObjectWatcher watcher(calculator);
needsUpdate = calculator->SetDisplayDirty(source);
}

calculator = gDBFile->GetOneObject(mSecondGroup, mSecondCalculation);

if (calculator)
{
DB_ObjectWatcher watcher(calculator);

if (needsUpdate)
calculator->SetDisplayDirty(source);
else
needsUpdate = calculator->SetDisplayDirty(source);
}

if (source == calc_startup) // bugfix TCS 1/3/00
mDisplayDirty = true;
else if (source == calc_completed)
mDisplayDirty = false;
else if (needsUpdate)
mDisplayDirty = true;

return needsUpdate;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

FillDataReport TCS 9/6/02

fill in a diagnostic table that shows data field values.

*********************************************************************************/
void CCalcCustom::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);

FillFieldObjectIDRow(table, stream, tag_firstcalculation, mFirstCalculation, mFirstGroup);
FillFieldObjectIDRow(table, stream, tag_secondcalculation, mSecondCalculation, mSecondGroup);

FillFieldEnumRow(table, stream, tag_calculationtype, mCalculationType, MENU_CalcCustomTypes);
FillFieldEnumRow(table, stream, tag_firstgroup, mFirstGroup, MENU_CalcSources);
FillFieldEnumRow(table, stream, tag_secondgroup, mSecondGroup, MENU_CalcSources);
FillFieldEnumRow(table, stream, tag_rounding, mRoundingType, MENU_RoundingTypes);

FillFieldTagRow(table, stream, tag_constant, cMoneySize, mConstant.GetCurrencyString());

FillFieldBitRow(table, stream, "mCalcDirty", mCalcDirty, true);
FillFieldBitRow(table, stream, "mDisplayDirty", mDisplayDirty);
FillFieldStockRow(table, stream, stockID_Padding, -6, SInt32(filler));
FillFieldStockRow(table, stream, stockID_Padding, cCharSize, SInt32(mCalcCustFiller));

FillEndSafetyTag(table, stream, mEndSafetyTag);
}