Link to: header | unit cost
directory
Copyright Turtle Creek Software 1996-2006. All Rights Reserved.
Comments
CAssembly
This class manages estimating assemblies in the Goldenseal accounting software,
estimating software, project management software
and construction estimating software.
an assembly is a kind of cost item which contains a list of components
(subassemblies). Subassemblies can be cost items, tools, reminders,
unlisted items or other assemblies. CAssembly inherits a breakdown array
from CBreakdownArrayOwner, which handles much of the data processing.
We use assemblies extensively in the Goldenseal estimating software-- they
provide most of the unit costs for most construction estimates. They are also helpful
in sales.
SUPERCLASS = CUnitCost
Constructor
/*********************************************************************************
default constructor
*********************************************************************************/
CAssembly::CAssembly()
{
mLaborModifier = mMaterialModifier = 0;
mComponentCatSystem = 0;
mTimesUpdated = 0;
mLaborAdjustment = mMaterialAdjustment = 0; // rev TCS 6/4/01
mStartsAfter = mParallelWith = 0; // TCS 4/20/04
mBreakdownType = breakdown_assembly; // TCS 5/10/00
mBreakdownClassID = id_SubAssembly; // TCS 12/1/98
mLaborCost = mEquipmentCost = 0; // TCS 8/1/01
mMaterialCost = mSubcontractorCost = 0;
mOtherCost = mSoftCosts = 0;
mLaborHours = 0;
mCrewSize = 1;
mWorkCrew = 0;
mEndSafetyTag = tag_endsafetytag; // TCS 9/6/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 CAssembly::CopyFrom(DB_PersistentObject *source, const UInt8 copyFlags)
{
THE_SUPERCLASS::CopyFrom(source, copyFlags);
CAssembly *src = TCS_SAFE_CAST(source, CAssembly);
TCS_FailNILMsg(src, TCS_GetErrString(errID_BadRecord));
NeoVersion version = GetVersion();
if (version > 3)
TCS_BlockMove(&src->mLaborModifier, &mLaborModifier, cCopyFileLength4);
else
TCS_BlockMove(&src->mLaborModifier, &mLaborModifier, cCopyFileLength);
// also copy all of the subassemblies TCS 5/10/00
if (!(copyFlags & flag_shallowcopy)) // TCS rev 7/19/00
CopyBreakdowns(src, GetDBID()); // TCS rev 11/1/01
}
/*********************************************************************************
GetFileLength
return the file length to reserve for this object
*********************************************************************************/
NeoSize CAssembly::GetFileLength(const CNeoFormat *aFormat) const
{
long basicLength = THE_SUPERCLASS::GetFileLength(aFormat) +
ARRAY_FILE_SIZE(mFeedbackArray) + // TCS 5/20/02
CBreakdownArrayOwner::GetFileLength(aFormat); // rev TCS 3/14/00
NeoVersion version = GetVersion();
if (version > 3)
return basicLength + cFileLength4;
else
return basicLength + cFileLength;
}
/*********************************************************************************
GetMemberValue
return the value of the member with the given tag
*********************************************************************************/
Boolean CAssembly::GetMemberValue(const NeoTag aTag, const NeoTag aType,
void *aValue) const
{
if (CBreakdownArrayOwner::GetMemberValue(aTag, aType, aValue))
return true;
else switch (aTag)
{
case tag_labormodifier:
if (mCalcLaborModifier) // rev TCS 12/7/01
return ConvertObjectIDMember(mLaborModifier, id_CalcDimension, aValue, aType);
else
return ConvertObjectIDMember(mLaborModifier, id_Dimension, aValue, aType);
break;
case tag_materialmodifier:
if (mCalcMaterialModifier) // rev TCS 12/7/01
return ConvertObjectIDMember(mMaterialModifier, id_CalcDimension, aValue, aType);
else
return ConvertObjectIDMember(mMaterialModifier, id_Dimension, aValue, aType);
break;
case tag_componentcatsystem:
return ConvertObjectIDMember(mComponentCatSystem, id_CategorySystem, aValue, aType);
break;
case tag_workcrew: // TCS 8/14/02
return ConvertObjectIDMember(mWorkCrew, id_WorkCrew, aValue, aType);
break;
case tag_parallelwith: // TCS 4/20/04
return ConvertObjectIDMember(mParallelWith, id_Assembly, aValue, aType);
break;
case tag_startsafter: // TCS 4/20/04
return ConvertObjectIDMember(mStartsAfter, id_Assembly, aValue, aType);
break;
case tag_purchaseprice:
return ConvertMember(&mPurchasePrice, type_emoney, aValue, aType);
break;
case tag_actuallabor: // TCS 8/1/01
return ConvertMember(&mLaborCost, type_emoney, aValue, aType);
break;
case tag_actualequipment: // TCS 8/1/01
return ConvertMember(&mEquipmentCost, type_emoney, aValue, aType);
break;
case tag_actualmaterials: // TCS 8/1/01
return ConvertMember(&mMaterialCost, type_emoney, aValue, aType);
break;
case tag_actualsubs: // TCS 8/1/01
return ConvertMember(&mSubcontractorCost, type_emoney, aValue, aType);
break;
case tag_actualother: // TCS 8/1/01
return ConvertMember(&mOtherCost, type_emoney, aValue, aType);
break;
case tag_actualsoft: // TCS 8/1/01
return ConvertMember(&mSoftCosts, type_emoney, aValue, aType);
break;
case tag_laborhours: // TCS 4/26/02
return ConvertMember(&mLaborHours, type_number, aValue, aType);
break;
case tag_crewsize: // TCS 5/14/02
return ConvertMember(&mCrewSize, type_number, aValue, aType);
break;
case tag_laboradjustment:
return ConvertMember(&mLaborAdjustment, type_percent, aValue, aType);
break;
case tag_materialadjustment:
return ConvertMember(&mMaterialAdjustment, type_percent, aValue, aType);
break;
case tag_costarea:
{
UInt8 costArea = costtype_assembly;
return ConvertMember(&costArea, type_enum, aValue, aType);
}
break;
case tag_timesupdated:
return ConvertMember(&mTimesUpdated, type_short, aValue, aType);
break;
default:
return THE_SUPERCLASS::GetMemberValue(aTag, aType, aValue);
break;
}
}
/*********************************************************************************
SetMemberValue
set the value of the member with the given tag
*********************************************************************************/
Boolean CAssembly::SetMemberValue(const NeoTag aTag, const NeoTag aType,
const void *aValue)
{
if (CBreakdownArrayOwner::SetMemberValue(aTag, aType, aValue))
return true;
else switch (aTag)
{
case tag_labormodifier:
if (mCalcLaborModifier) // rev TCS 12/7/01
return ConvertDataToObjectID(aValue, aType, &mLaborModifier, id_CalcDimension);
else
return ConvertDataToObjectID(aValue, aType, &mLaborModifier, id_Dimension);
break;
case tag_materialmodifier:
if (mCalcMaterialModifier) // rev TCS 12/7/01
return ConvertDataToObjectID(aValue, aType, &mMaterialModifier, id_CalcDimension);
else
return ConvertDataToObjectID(aValue, aType, &mMaterialModifier, id_Dimension);
break;
case tag_componentcatsystem:
return ConvertDataToObjectID(aValue, aType, &mComponentCatSystem, id_CategorySystem);
break;
case tag_workcrew: // TCS 8/14/02
return ConvertDataToObjectID(aValue, aType, &mWorkCrew, id_WorkCrew);
break;
case tag_parallelwith: // TCS 4/20/04
return ConvertDataToObjectID(aValue, aType, &mParallelWith, id_Assembly);
break;
case tag_startsafter: // TCS 4/20/04
return ConvertDataToObjectID(aValue, aType, &mStartsAfter, id_Assembly);
break;
case tag_purchaseprice:
return ConvertMember(aValue, aType, &mPurchasePrice, type_emoney);
break;
case tag_actuallabor: // TCS 8/1/01
return ConvertMember(aValue, aType, &mLaborCost, type_emoney);
break;
case tag_actualequipment: // TCS 8/1/01
return ConvertMember(aValue, aType, &mEquipmentCost, type_emoney);
break;
case tag_actualmaterials: // TCS 8/1/01
return ConvertMember(aValue, aType, &mMaterialCost, type_emoney);
break;
case tag_actualsubs: // TCS 8/1/01
return ConvertMember(aValue, aType, &mSubcontractorCost, type_emoney);
break;
case tag_actualother: // TCS 8/1/01
return ConvertMember(aValue, aType, &mOtherCost, type_emoney);
break;
case tag_actualsoft: // TCS 8/1/01
return ConvertMember(aValue, aType, &mSoftCosts, type_emoney);
break;
case tag_laborhours: // TCS 4/26/02
return ConvertMember(aValue, aType, &mLaborHours, type_number);
break;
case tag_crewsize: // TCS 5/14/02
return ConvertMember(aValue, aType, &mCrewSize, type_number);
break;
case tag_laboradjustment:
return ConvertMember(aValue, aType, &mLaborAdjustment, type_percent);
break;
case tag_materialadjustment:
return ConvertMember(aValue, aType, &mMaterialAdjustment, type_percent);
break;
case tag_timesupdated:
return ConvertMember(aValue, aType, &mTimesUpdated, type_short);
break;
default:
return THE_SUPERCLASS::SetMemberValue(aTag, aType, aValue);
break;
}
}
/*********************************************************************************
ReadObject
read the persistent object's data in from a stream
*********************************************************************************/
void CAssembly::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);
if (!IsIOValid()) // TCS 2/5/02
return;
NeoVersion version = GetVersion();
CBreakdownArrayOwner::ReadObject(aStream, aTag);
ReadInfoArrayFromStream(aStream, mFeedbackArray, cHasSafetyTag); // TCS 5/20/02
mLaborModifier = aStream->ReadID();
mMaterialModifier = aStream->ReadID();
mComponentCatSystem = aStream->ReadID();
mWorkCrew = aStream->ReadID();
mEquipmentCost.ReadFromStream(aStream);
mLaborCost.ReadFromStream(aStream);
mMaterialCost.ReadFromStream(aStream);
mSubcontractorCost.ReadFromStream(aStream);
mOtherCost.ReadFromStream(aStream);
mSoftCosts.ReadFromStream(aStream);
mLaborHours.ReadFromStream(aStream);
mCrewSize.ReadFromStream(aStream);
if (version > 3) // TCS 4/20/04
{
mStartsAfter = aStream->ReadID();
mParallelWith = aStream->ReadID();
}
mTimesUpdated = aStream->ReadShort();
mLaborAdjustment.ReadFromStream(aStream);
mMaterialAdjustment.ReadFromStream(aStream);
mEndSafetyTag = aStream->ReadEndSafetyTag(this);
if (!IsValidEndTag(mEndSafetyTag))
ReportDamagedObject(GetDBClassID(), GetDBID());
}
/*********************************************************************************
WriteObject
write the persistent object's data to a stream
*********************************************************************************/
void CAssembly::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);
NeoVersion version = GetVersion();
CBreakdownArrayOwner::WriteObject(aStream, aTag);
WriteInfoArrayToStream(aStream, mFeedbackArray, cHasSafetyTag); // TCS 5/20/02
aStream->WriteID(mLaborModifier);
aStream->WriteID(mMaterialModifier);
aStream->WriteID(mComponentCatSystem);
aStream->WriteID(mWorkCrew);
mEquipmentCost.WriteToStream(aStream);
mLaborCost.WriteToStream(aStream);
mMaterialCost.WriteToStream(aStream);
mSubcontractorCost.WriteToStream(aStream);
mOtherCost.WriteToStream(aStream);
mSoftCosts.WriteToStream(aStream);
mLaborHours.WriteToStream(aStream);
mCrewSize.WriteToStream(aStream);
if (version > 3)
{
aStream->WriteID(mStartsAfter);
aStream->WriteID(mParallelWith);
}
aStream->WriteShort(mTimesUpdated);
mLaborAdjustment.WriteToStream(aStream);
mMaterialAdjustment.WriteToStream(aStream);
aStream->WriteEndSafetyTag(mEndSafetyTag, this);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
PostNewRecord TCS 12/27/00
post creation of an assembly. We want all our subassemblies to add links
to the cost items they use.
*********************************************************************************/
void CAssembly::PostNewRecord(const UInt8 creationType)
{
// link this item's subassemblies to the cost items or assemblies
// this is also where we update use counts TCS rev 11/21/03
AddDependentLinks();
// the superclass handles updating of dependents, etc
THE_SUPERCLASS::PostNewRecord(creationType);
}
/*********************************************************************************
PostDeletion TCS 12/27/00
post deletion of an assembly. We want all our subassemblies to delete links
from the cost items they use.
*********************************************************************************/
void CAssembly::PostDeletion(const Boolean postAudit)
{
// link this item's subassemblies to the cost items or assemblies
// this is also where we update use counts TCS rev 11/21/03
AddDependentLinks(cRemoveItem);
THE_SUPERCLASS::PostDeletion(postAudit);
}
/*********************************************************************************
PostRecordChanging TCS 12/27/00
a record will be changing. Post the change. Since we don't know what our
subassemblies are up to, we just have them delete all their links.
*********************************************************************************/
void CAssembly::PostRecordChanging(const Boolean accountChanging, const Boolean jobChanging)
{
// link this item's subassemblies to the cost items or assemblies
// this is also where we update use counts TCS rev 11/21/03
AddDependentLinks(cRemoveItem);
// we pass it along, though currently nothing more happens
THE_SUPERCLASS::PostRecordChanging(accountChanging, jobChanging);
}
/*********************************************************************************
PostRecordChanged TCS 12/27/00
post changes in an assembly.
*********************************************************************************/
void CAssembly::PostRecordChanged(const CMoney &oldAmount, const Boolean accountChanged,
const Boolean jobChanged)
{
// force a recalc so other stored items are updated TCS moved 4/14/03
RecalcPrice();
// link this item's subassemblies to the cost items or assemblies
// this is also where we update use counts TCS rev 11/21/03
AddDependentLinks();
// the superclass handles updating of dependents, etc
THE_SUPERCLASS::PostRecordChanged(oldAmount, accountChanged, jobChanged);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
HasFixedQuantity TCS 12/24/01
return whether or not this assembly includes a 'flat cost' item
*********************************************************************************/
Boolean CAssembly::HasFixedQuantity() const
{
// we need to check each subassembly
if (mBreakdownArray.GetCount() > 0)
{
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid id;
CSubAssembly *subAssembly;
while (iterator.Next(id))
{
subAssembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, id),
CSubAssembly);
if (subAssembly)
{
DB_ObjectWatcher watcher (subAssembly);
if (subAssembly->HasFixedQuantity())
return true;
}
}
return false;
}
else
return false;
}
/*********************************************************************************
DependsOnItem
return whether or not this assembly depends on the assembly with the given ID.
*********************************************************************************/
Boolean CAssembly::DependsOnItem(const DBid itemID, const Boolean deeply) const
{
if (itemID == GetDBID())
{ // the item id is the same as our id, so a dependency exists
return true;
}
#if TCS_MULTIUSER
else if (gIsClient && gApplication && deeply)
{
// if this is a client we send a message to the server
// so we don't have to fetch a zillion objects TCS 5/8/03
CTCS_NetworkMessage ioMessage(msg_AssemblyDependsOn);
ioMessage.SetRecordID(GetDBID());
ioMessage.SetLongValue(itemID);
ioMessage.SetFirstByte(deeply);
ioMessage.SetNeedsReply();
if (gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning))
{
return ioMessage.GetSecondByte();
}
else
return false;
}
#endif
else // we need to look at each subassembly in our breakdown
{
TObjectIDArrayIterator iterator(mBreakdownArray); // rev TCS 11/28/00
DBid subID, assemblyID;
CUnitCost *costItem = nil;
CSubAssembly *sub = nil;
CAssembly *assembly = nil;
while (iterator.Next(subID)) // fetch each subassembly
{
sub = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID),
CSubAssembly);
TCS_FailNILMsg(sub, TCS_GetErrString(errID_BadAssembly));
DB_ObjectWatcher subWatcher(sub);
// we only need to look at assembly components rev TCS 12/27/00
if (sub->GetCostItemType() == id_Assembly)
{
assemblyID = sub->GetCostItemID();
assembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, assemblyID),
CAssembly);
if (assembly)
{
DB_ObjectWatcher itemWatcher(assembly);
if (costItem->GetDBID() == itemID)
return true;
else if (deeply)
{ // we need to look at the assemblies components
if (assembly->DependsOnItem(itemID, deeply))
return true;
}
}
}
}
}
// if we got here, it means we don't depend on the item
return false;
}
/*********************************************************************************
UpdateDimensions TCS 9/3/02
update the reference in suggested quantities. We override to also store a
reference in modifier dimensions
*********************************************************************************/
void CAssembly::UpdateDimensions(const Boolean removeItem)
{
// the superclass handles suggested dimension posting
THE_SUPERCLASS::UpdateDimensions(removeItem);
DB_PersistentObject *dimension = nil;
DBClass dimensionClass;
if (mLaborModifier)
{
dimensionClass = mCalcLaborModifier ? id_CalcDimension : id_Dimension;
dimension = gDBFile->GetOneObject(dimensionClass, mLaborModifier);
if (dimension)
{
DB_ObjectWatcher watcher (dimension);
dimension->AddToDependentArray(GetDBClassID(), GetDBID(), removeItem);
}
else
ReportMissingObject(dimensionClass, mSuggestDimension);
}
if (mMaterialModifier)
{
dimensionClass = mCalcMaterialModifier ? id_CalcDimension : id_Dimension;
dimension = gDBFile->GetOneObject(dimensionClass, mMaterialModifier);
if (dimension)
{
DB_ObjectWatcher watcher (dimension);
dimension->AddToDependentArray(GetDBClassID(), GetDBID(), removeItem);
}
else
ReportMissingObject(dimensionClass, mSuggestDimension);
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
RemoveSubAssemblyByID
remove the subassembly with the given id. This forces a recalculation
of the price. Note that this doesn't remove the subassembly from the
db- that's the caller's responsibility
TCS removed this 12/27/00. It looks like a baddy anyhow, since it changes
the array length without taking this object out of the database
*********************************************************************************/
/*void CAssembly::RemoveSubAssemblyByID(const DBid subID)
{
// let's remove the given id from our breakdown array
mBreakdownArray.Remove(subID);
// now that it's removed, we need to recalculate our cost
RecalcPrice();
}*/
/*********************************************************************************
AddDependentLinks TCS 12/27/00
link this item's subassemblies to the cost items or assemblies they reference
*********************************************************************************/
void CAssembly::AddDependentLinks(const Boolean removeItem)
{
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid subID;
CSubAssembly *sub = nil;
while (iterator.Next(subID))
{
sub = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID),
CSubAssembly);
if (sub) // TCS 3/18/04
{
DB_ObjectWatcher subWatcher(sub);
sub->AdjustSubassemblyArray(removeItem);
}
}
if (mCalcLaborModifier)
PostUseCount(id_CalcDimension, mLaborModifier, removeItem); // TCS 11/21/03
else
PostUseCount(id_Dimension, mLaborModifier, removeItem);
if (mCalcMaterialModifier)
PostUseCount(id_CalcDimension, mMaterialModifier, removeItem);
else
PostUseCount(id_Dimension, mMaterialModifier, removeItem);
PostUseCount(id_CategorySystem, mComponentCatSystem, removeItem);
PostUseCount(id_WorkCrew, mWorkCrew, removeItem);
}
/*********************************************************************************
AddBreakdown moved TCS 5/9/01
add a breakdown to the breakdown array. This routine doesn't check
whether the id is valid for the current breakdown type; the caller
is expected to ensure that
*********************************************************************************/
void CAssembly::AddBreakdown(const DBid breakdownID)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, AddBreakdown called while in database!");
mBreakdownArray.Append(breakdownID);
}/*********************************************************************************
RemoveAllBreakdownEntries moved TCS 5/9/01
remove all the breakdowns.
WARNING- because this method changes an object's file length, it should only be
used on an object that has been removed from the database.
*********************************************************************************/
void CAssembly::RemoveAllBreakdownEntries(const Boolean removeFromDatabase)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, RemoveAllBreakdownEntries called while in database!");
ClearBreakdownArray(&mBreakdownArray, GetBreakdownClassID(), removeFromDatabase);
}
/*********************************************************************************
InitializeUpdateInfo (static) TCS 8/2/01
recalculate and return this assembly's net price
*********************************************************************************/
void CAssembly::InitializeUpdateInfo(SAssemblyUpdateInfo &info)
{
info.equipmentCost = 0;
info.laborCost = 0;
info.materialCost = 0;
info.subcontractorCost = 0;
info.otherCost = 0;
info.softCost = 0;
info.totalCost = 0;
info.laborHours = 0; // TCS 4/26/02
}
/*********************************************************************************
RemoveFromDatabase
remove this object from the db. We override since we also need to dispose of
any subassemblies
*********************************************************************************/
Boolean CAssembly::RemoveFromDatabase(const UInt32 clientID)
{
// do we have any breakdowns?
if (mBreakdownArray.GetCount() > 0)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
DB_ObjectTempRemover remover (this); // TCS 8/26/03
if (remover.WasRemoved())
RemoveAllBreakdownEntries(); // if so, let's delete them
}
// now let the superclass do the actual deletion of this object
return THE_SUPERCLASS::RemoveFromDatabase(clientID);
}
/*********************************************************************************
HasAssemblyCode (static) TCS 7/9/00
return whether there is an assembly with the given code
*********************************************************************************/
DBid CAssembly::GetAssemblyWithCode(const CMoney &inCode)
{
if (!inCode.IsPositive())
return 0;
// fetch the array of assembly names
TCostNameArray costArray;
TCS_ASSERTMsg(DB_ListManager::GetCostNameArray(id_Assembly, &costArray),
TCS_GetErrString(errID_BadAssembly));
TCostNameArrayIterator iterator(costArray);
SCostNameInfo costInfo;
while (iterator.Next(costInfo))
{
if (costInfo.itemCode == inCode)
return costInfo.id;
}
// if we got this far, the code is not listed
return 0;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
HandleReplaceAll TCS 8/23/00
we've been 'replaced all'. We may need to recalculate
*********************************************************************************/
void CAssembly::HandleReplaceAll(const TMemberArray &memberArray)
{
TMemberArrayIterator iterator(memberArray);
SMemberInfo memberInfo;
Boolean needsRecalc = false;
while (iterator.Next(memberInfo))
{
// we only need to recalculate if the markup field has changed
if (memberInfo.tag == tag_markup)
needsRecalc = true;
}
if (needsRecalc)
RecalcNetPrices();
}
/*********************************************************************************
HandleBreakdownAdded TCS 12/24/98
a breakdown has been added (probably via an import). We'd better recalculate costs
*********************************************************************************/
void CAssembly::HandleBreakdownAdded(const UInt8 /*creationType*/)
{
RecalcPrice();
}
/*********************************************************************************
HandleItemsManufactured TCS 11/14/01
some items have been manufactured. Adjust inventory of any components
*********************************************************************************/
void CAssembly::HandleItemsManufactured(const CMoney &changeAmount, const DBid accountID,
const Boolean isComponent, const Boolean removeItem)
{
if (isComponent) // TCS 3/20/02
return;
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid id;
CSubAssembly *component;
while (iterator.Next(id))
{
component = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, id), CSubAssembly);
if (component)
{
DB_ObjectWatcher watcher(component);
component->HandleItemsManufactured(changeAmount, accountID, true, removeItem);
}
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillCostBreakdown TCS 12/22/99, 8/1/01
fill in a job cost struct based on assembly subcomponents
*********************************************************************************/
void CAssembly::FillCostBreakdown(SJobCostReportInfo &catItem, const CMoney &quantity) const
{
catItem.equipmentAmount = mEquipmentCost * quantity;
catItem.laborAmount = mLaborCost * quantity;
catItem.materialAmount = mMaterialCost * quantity;
catItem.subcontractorAmount = mSubcontractorCost * quantity;
catItem.otherAmount = mOtherCost * quantity;
catItem.softAmount = mSoftCosts * quantity;
}
/*********************************************************************************
FetchComponentCosts TCS 8/2/01 rev 8/6/01
fetch the assembly costs and add them to a price update struct
*********************************************************************************/
CMoney CAssembly::FetchComponentCosts(SAssemblyUpdateInfo &info, const CMoney &inQuantity)
{
info.equipmentCost += mEquipmentCost * inQuantity; // bugfix TCS 11/26/01
info.laborCost += mLaborCost * inQuantity;
info.materialCost += mMaterialCost * inQuantity;
info.subcontractorCost += mSubcontractorCost * inQuantity;
info.otherCost += mOtherCost * inQuantity;
info.softCost += mSoftCosts * inQuantity;
info.laborHours += mLaborHours * inQuantity; // TCS 4/26/02
return GetAmount();
}
/*********************************************************************************
RecalcPrice
recalculate and return this assembly's net price, which we calculate
from the subassemblies. We also store component prices, so this function
cannot be const.
*********************************************************************************/
CMoney CAssembly::RecalcPrice()
{
// prepare to loop thru all of the subassemblies
TObjectIDArrayIterator iterator(mBreakdownArray); // rev TCS 11/28/00
DBid subID;
CSubAssembly *sub = nil;
CMoney itemPrice, totalPrice;
UInt8 costType;
Boolean adjustAssemblies = GetPrefsBoolean(id_ExpensePrefs, tag_adjustassemblies); // TCS 6/4/01
SAssemblyUpdateInfo info;
InitializeUpdateInfo(info);
// loop through subassemblies
while (iterator.Next(subID))
{
sub = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID),
CSubAssembly);
TCS_FailNILMsg(sub, TCS_GetErrString(errID_BadAssembly));
DB_ObjectWatcher subWatcher(sub);
// fetch the subassembly cost breakdown rev TCS 8/2/01
itemPrice = sub->FetchComponentCosts(info, 1);
// apply labor and material adjusters
if (adjustAssemblies)
{
costType = sub->GetCostArea();
if (costType == costtype_material)
{
info.materialCost *= (mMaterialAdjustment.GetPercentMultiplier()); // bugfix TCS 6/4/01
}
else if (costType == costtype_labor)
{
info.laborCost *= (mLaborAdjustment.GetPercentMultiplier());
info.laborHours *= (mLaborAdjustment.GetPercentMultiplier()); // TCS 4/26/02
}
}
// figure the cumulative total price
totalPrice += itemPrice;
}
// update our total purchase price. rev TCS 6/4/01
mPurchasePrice = totalPrice;
// and store the cost breakdown values TCS 8/2/01
mEquipmentCost = info.equipmentCost;
mLaborCost = info.laborCost;
mMaterialCost = info.materialCost;
mSubcontractorCost = info.subcontractorCost;
mOtherCost = info.otherCost;
mSoftCosts = info.softCost;
mLaborHours = info.laborHours; // TCS 4/26/02
// make sure that the various markup prices are updated as well.
// the recalc also sets the dirty status and resets the mod. date rev TCS 4/9/02
RecalcNetPrices();
// we used to update dependent items here, but that should now be handled
// with a separate call TCS rev 12/27/00
// return the newly recalculated value
return mPurchasePrice;
}
/*********************************************************************************
CalcLaborHours TCS 3/31/03
calculate the number of labor hours included in this item for the given quantity.
This form handles "fixed quantity" subcomponents
*********************************************************************************/
CMoney CAssembly::CalcLaborHours(const CMoney &quantity) const
{
TObjectIDArrayIterator iterator(mBreakdownArray); // rev TCS 11/28/00
DBid subID;
CSubAssembly *sub = nil;
CMoney itemHours,
totalHours = 0;
while (iterator.Next(subID))
{
sub = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID), CSubAssembly);
if (sub)
{
DB_ObjectWatcher watcher (sub);
itemHours = sub->CalcLaborHours(quantity);
totalHours += itemHours;
}
}
return totalHours;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
GetJobCostAmount TCS 8/1/01
get the assembly cost for the given cost type
*********************************************************************************/
CMoney CAssembly::GetJobCostAmount(const UInt8 costArea) const
{
switch (costArea)
{
case costtype_labor:
return mLaborCost;
break;
case costtype_equipment:
return mEquipmentCost;
break;
case costtype_material:
return mMaterialCost;
break;
case costtype_subcontractor:
return mSubcontractorCost;
break;
case costtype_other:
return mOtherCost;
break;
case costtype_soft:
return mSoftCosts;
break;
default:
return 0;
break;
}
}
/*********************************************************************************
GetEstimateCost TCS 12/26/01 rev 4/10/04
fill in unit cost and total cost. Formerly called GetAdjustedUnitCosts.
*********************************************************************************/
Boolean CAssembly::GetEstimateCost(DB_PersistentObject *source, const UInt8 costArea,
const CMoney &quantity, CMoney &unitCost, CMoney &totalCost,
CMoney &hoursPerUnit, CMoney &totalHours)
{
if (quantity.IsZero())
{
return THE_SUPERCLASS::GetEstimateCost(source, costArea, quantity, unitCost, totalCost,
hoursPerUnit, totalHours);
}
else
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
Boolean hasFixedQuantity = HasFixedQuantity();
// we need to go through the subassemblies and get a total for
// the given quantity, factoring in 'fixed quantity' items and
// any labor or material adjustments. Since many assemblies have
// adjustments we just run this on everything.
CMoney runningTotal = 0,
runningHours = 0,
itemAmount, laborPrice, materialPrice, otherPrice;
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid id;
CSubAssembly *subAssembly;
TCS_Real laborModifier = GetLaborModifier(source);
TCS_Real materialModifier = GetMaterialModifier(source);
while (iterator.Next(id))
{
subAssembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, id),
CSubAssembly);
if (subAssembly)
{
DB_ObjectWatcher watcher (subAssembly);
laborPrice = subAssembly->GetLaborPrice(); // TCS rev 4/10/04
materialPrice = subAssembly->GetMaterialPrice();
otherPrice = subAssembly->GetOtherPrice();
if (subAssembly->UsesLaborModifier() && gDBFile->UseEstimateModifiers())
{
laborPrice.Multiply(laborModifier);
}
if (subAssembly->UsesMaterialModifier() && gDBFile->UseEstimateModifiers())
{
materialPrice.Multiply(materialModifier);
}
if (gDBFile->UseEstimateExperience()) // TCS 4/11/04
{
if (mLaborAdjustment.IsPositive())
laborPrice.Multiply(mLaborAdjustment);
if (mMaterialAdjustment.IsPositive())
materialPrice.Multiply(mMaterialAdjustment);
}
if (costArea == costtype_assmlabor) // TCS 11/26/03
itemAmount = laborPrice;
else if (costArea == costtype_assmmaterial)
itemAmount = materialPrice;
else
itemAmount = laborPrice + materialPrice + otherPrice;
if (subAssembly->HasFixedQuantity())
runningTotal += itemAmount;
else
runningTotal += (itemAmount * quantity);
// update the hours also TCS 3/31/03
if (costArea != costtype_assmmaterial)
{
itemAmount = subAssembly->CalcLaborHours(quantity);
runningHours += itemAmount;
}
}
}
totalCost = runningTotal;
totalHours = runningHours; // TCS 3/31/03
if (quantity.IsPositive()) // TCS 11/26/03
{
unitCost = totalCost / quantity;
hoursPerUnit = runningHours / quantity;
}
else
{
unitCost = 0;
hoursPerUnit = 0;
}
return hasFixedQuantity;
}
}
/*********************************************************************************
GetLaborModifier TCS 4/10/04
get the labor modifier from a specific estimate.
*********************************************************************************/
TCS_Real CAssembly::GetLaborModifier(DB_PersistentObject *source)
{
if (mLaborModifier)
{
CEstimate *estimate = TCS_SAFE_CAST(source, CEstimate);
if (estimate)
{
CMoney dimensionAmount;
TagType dimensionTag;
if (mCalcLaborModifier)
dimensionTag = CCalculatorList::GetCalculatorTag(id_CalcDimension, mLaborModifier);
else
dimensionTag = CCalculatorList::GetCalculatorTag(id_Dimension, mLaborModifier);
dimensionAmount = estimate->GetDimensionValue(dimensionTag);
return dimensionAmount.GetReal();
}
}
// if we get this far there is no modifier
return 1;
}
/*********************************************************************************
GetMaterialModifier TCS 4/10/04
get the material modifier from a specific estimate.
*********************************************************************************/
TCS_Real CAssembly::GetMaterialModifier(DB_PersistentObject *source)
{
if (mMaterialModifier)
{
CEstimate *estimate = TCS_SAFE_CAST(source, CEstimate);
if (estimate)
{
CMoney dimensionAmount;
TagType dimensionTag;
if (mCalcMaterialModifier)
dimensionTag = CCalculatorList::GetCalculatorTag(id_CalcDimension, mMaterialModifier);
else
dimensionTag = CCalculatorList::GetCalculatorTag(id_Dimension, mMaterialModifier);
dimensionAmount = estimate->GetDimensionValue(dimensionTag);
return dimensionAmount.GetReal();
}
}
// if we get this far there is no modifier
return 1;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
MatchesFindCriteria TCS 12/17/01 rev 12/21/01
return whether this object matches the given find criteria. selectorArray
must be an array of pointers to selector objects.
We override completely, since we need to check for table criteria
*********************************************************************************/
Boolean CAssembly::MatchesFindCriteria(CTCS_Array &selectorArray,
const Boolean matchAny, const SInt32 selectorCount) const
{
DB_MemberSelector *selector = nil;
CTCS_ArrayIterator iterator (selectorArray);
Boolean matchesThis;
Boolean hasCheckedTable = false;
// loop through each selector, and see where it came from
while (iterator.Next(&selector))
{
TCS_FailNILMsg(selector, TCS_GetErrString(errID_BadSelector));
if (selector->IsFromTable())
{
if (!hasCheckedTable)
{
matchesThis = BreakdownMatchesSelectors(selectorArray, matchAny, selectorCount);
hasCheckedTable = true;
}
}
else
matchesThis = MatchesSelector(selector);
if (matchesThis)
{ // for 'any match', we exit at the first match. However we
// skip group class tags (so it's possible to find two specific
// accounts in an 'any match' search) TCS rev 1/11/03
if (matchAny && (selectorCount < 3 || !DB_ClassDescriptor::ControlsMultipleClasses(selector->getSelectTag())))
return true;
}
else
{ // for regular find, we exit at the first non-match
if (!matchAny)
return false;
}
}
// if we get this far, the match status depends on the search type
if (matchAny)
return false; // nothing matched
else
return true; // nothing mis-matched
}
/*********************************************************************************
WriteToPrintFormTable moved TCS 1/29/02
write breakdowns to a report table (in print forms)
*********************************************************************************/
Boolean CAssembly::WriteToPrintFormTable(CReportTable *table)
{
// sanity check
TCS_FailNILMsg(table, TCS_GetErrString(errID_BadTable));
return WriteToReportTable(table, id_SubAssembly, mBreakdownArray, this);
}
/*********************************************************************************
ExportBreakdowns TCS 8/21/03
export breakdowns for this item
*********************************************************************************/
void CAssembly::ExportBreakdowns(CTextOutputStream &stream)
{
CDataTranslator::ExportRecordArray(id_SubAssembly, mBreakdownArray, stream);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************
FillProjectReportArray TCS 4/4/02
fill in data for a project report- tools or reminders.
*********************************************************************************/
Boolean CAssembly::FillProjectReportArray(TReportRowArray *reportRowArray, const DBid reportID,
const DBid breakdownID, SInt32 runLimit) const
{
runLimit++;
if (runLimit > cRecursionLimit)
{
TCS_DebugAlert("Oops, too many linked items in CAssembly::FillProjectReportArray!");
return false;
}
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid id;
while (iterator.Next(id))
{
CSubAssembly *component =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, id),
CSubAssembly);
if (component)
{
DB_ObjectWatcher watcher (component);
component->FillProjectReportArray(reportRowArray, reportID,
breakdownID, runLimit);
}
}
return true;
}
/*********************************************************************************
FillTakeoffArray TCS 4/4/02
fill in data for a material takeoff report.
*********************************************************************************/
Boolean CAssembly::FillTakeoffArray(TTakeoffArray &takeoffArray, const CMoney &parentQuantity,
const DBid breakdownID, SInt32 &runLimit, const CDate dateNeeded,
const Boolean isLabor) const
{
runLimit++;
if (runLimit > cRecursionLimit)
{
TCS_DebugAlert("Oops, too many linked items in CAssembly::FillTakeoffArray!");
return false;
}
TObjectIDArrayIterator iterator(mBreakdownArray);
DBid id;
while (iterator.Next(id))
{
CSubAssembly *component =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, id),
CSubAssembly);
if (component)
{
DB_ObjectWatcher watcher (component);
if (!component->FillTakeoffArray(takeoffArray, parentQuantity,
breakdownID, runLimit, dateNeeded, isLabor))
{
return false;
}
}
}
return true;
}
/*********************************************************************************
FillDataReport TCS 9/6/02
fill in a diagnostic table that shows data field values.
*********************************************************************************/
void CAssembly::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);
NeoVersion version = GetVersion();
FillFieldArrayRow(table, stream, "mBreakdownArray", mBreakdownArray);
FillFieldTableRow(table, stream, "mBreakdownType", cCharSize, SInt32(mBreakdownType));
FillFieldTableRow(table, stream, "mBreakdownClassID", cCharSize, SInt32(mBreakdownClassID));
FillFieldArrayRow(table, stream, "mFeedbackArray", mFeedbackArray);
FillFieldObjectIDRow(table, stream, tag_labormodifier, mLaborModifier, id_Dimension);
FillFieldObjectIDRow(table, stream, tag_materialmodifier, mMaterialModifier, id_Dimension);
FillFieldObjectIDRow(table, stream, tag_componentcatsystem, mComponentCatSystem, id_CategorySystem);
FillFieldObjectIDRow(table, stream, tag_workcrew, mWorkCrew, id_WorkCrew);
FillFieldTableRow(table, stream, "mEquipmentCost", cMoneySize, mEquipmentCost.GetCurrencyString());
FillFieldTableRow(table, stream, "mLaborCost", cMoneySize, mLaborCost.GetCurrencyString());
FillFieldTableRow(table, stream, "mMaterialCost", cMoneySize, mMaterialCost.GetCurrencyString());
FillFieldTableRow(table, stream, "mSubcontractorCost", cMoneySize, mSubcontractorCost.GetCurrencyString());
FillFieldTableRow(table, stream, "mOtherCost", cMoneySize, mOtherCost.GetCurrencyString());
FillFieldTableRow(table, stream, "mSoftCosts", cMoneySize, mSoftCosts.GetCurrencyString());
FillFieldTagRow(table, stream, tag_laborhours, cMoneySize, mLaborHours.GetNumberString());
FillFieldTagRow(table, stream, tag_crewsize, cMoneySize, mCrewSize.GetNumberString());
if (version > 3)
{
FillFieldObjectIDRow(table, stream, tag_startsafter, mStartsAfter, id_Assembly);
FillFieldObjectIDRow(table, stream, tag_parallelwith, mParallelWith, id_Assembly);
}
FillFieldTagRow(table, stream, tag_timesupdated, cShortSize, SInt32(mTimesUpdated));
FillFieldTagRow(table, stream, tag_laboradjustment, cMoneySize, mLaborAdjustment.GetNumberString());
FillFieldTagRow(table, stream, tag_materialadjustment, cMoneySize, mMaterialAdjustment.GetNumberString());
FillEndSafetyTag(table, stream, mEndSafetyTag);
}
|