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

Subassemblies (Source Code)

Link to: header | unit cost directory

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

Comments

CSubAssembly

This class manages subassemblies in the Goldenseal accounting software,
estimating software, unit cost software, job costing software and construction estimating software.

a subassembly stores information about a line in an assembly breakdown.

RELATED CLASSES: CSubAssemblyTable handles table display. CAssembly,
CAssemblyViewer and CBreakdownArrayOwner for the parent transaction
which owns the subassemblies.

SUPERCLASS = CBreakdownEntry

Constructor

/*********************************************************************************
default constructor
*********************************************************************************/
CSubAssembly::CSubAssembly()
{
mCostItem = 0;
mQuantity = 0;
mWaste = 0;
mItemCost = 0;

mCategory = mSubcategory = 0;

mCostArea = 0;
mMainAccountClass = id_Assembly; // TCS 3/21/00

mUseLaborModifier = false;
mUseMaterialModifier = false;
mFixedQuantity = false;

subAssmFiller = 0;

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

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

NeoVersion version = src->GetVersion(); // TCS 12/8/01

TCS_BlockMove(&src->mCostItem, &mCostItem, cCopyFileLength);

mItemName = src->mItemName;
mUnitSize = src->mUnitSize;
}
/*********************************************************************************

GetFileLength TCS 12/8/01

return the file length used by this object

*********************************************************************************/
NeoSize CSubAssembly::GetFileLength(const CNeoFormat *aFormat) const
{
return THE_SUPERCLASS::GetFileLength(aFormat) +
mItemName.FileLength(cMenuTextLen) +
mUnitSize.FileLength(cMenuTextLen) +
cFileLength;
}/*********************************************************************************

GetMemberValue

return the value of the member with the given tag

*********************************************************************************/
Boolean CSubAssembly::GetMemberValue(const NeoTag aTag, const NeoTag aType, void *aValue) const
{
switch (aTag)
{
case tag_costitem:
if (IsAssembly())
{
return ConvertObjectIDMember(mCostItem, id_Assembly, aValue, aType);
}
else if (IsCostItem())
{
return ConvertObjectIDMember(mCostItem, id_CostItem, aValue, aType);
}
else if (IsReminder()) // TCS 3/26/99
{
return ConvertObjectIDMember(mCostItem, id_ProjectReminder, aValue, aType);
}
else if (IsTool()) // TCS 3/26/99
{
return ConvertObjectIDMember(mCostItem, id_Tool, aValue, aType);
}
else if (IsUnlistedItem()) // TCS 12/11/01
{
return ConvertMember(&mItemName, type_cstring, aValue, aType);
}
else return false;

break;

case tag_ownerobjectclass:
return ConvertEnumMember(id_Assembly, MENU_IOTransactionTypes, aValue, aType);
break;

case tag_costarea: // rev TCS 4/26/99
return ConvertEnumMember(mCostArea, MENU_AssemblyTableCostTypes, aValue, aType);
break;

case tag_quantity:
return ConvertMember(&mQuantity, type_number, aValue, aType);
break;

case tag_amount: // TCS 12/11/01
return ConvertMember(&mAmount, type_emoney, aValue, aType);
break;

case tag_waste:
return ConvertMember(&mWaste, type_percent, aValue, aType);
break;

case tag_itemtext: // the text of the item name TCS 12/18/01
return ConvertMember(&mItemName, type_cstring, aValue, aType);
break;

case tag_sizetext: // the text of the unit size TCS 12/18/01
return ConvertMember(&mUnitSize, type_cstring, aValue, aType);
break;

case tag_labormodifier: // TCS 12/8/01
return ConvertBitFieldMember(mUseLaborModifier, aValue, aType);
break;

case tag_materialmodifier: // TCS 12/8/01
return ConvertBitFieldMember(mUseMaterialModifier, aValue, aType);
break;

case tag_fixedquantity: // TCS 12/23/01
return ConvertBitFieldMember(mFixedQuantity, aValue, aType);
break;

case tag_itemcode: // we fetch this from the parent
return GetObjectMemberValue(id_Assembly, mOwnerID, tag_itemcode, aType, aValue);
break;

case tag_category:
if (IsAssembly())
{
return GetObjectMemberValue(id_Assembly, mCostItem, aTag, aType, aValue);
}
else if (IsCostItem())
{
return GetObjectMemberValue(id_CostItem, mCostItem, aTag, aType, aValue);
}
else
{
return ConvertMember(&mCategory, type_objectid, aValue, aType);
}
break;

case tag_subcategory:
if (IsAssembly())
{
return GetObjectMemberValue(id_Assembly, mCostItem, aTag, aType, aValue);
}
else if (IsCostItem())
{
return GetObjectMemberValue(id_CostItem, mCostItem, aTag, aType, aValue);
}
else
{
return ConvertMember(&mSubcategory, type_objectid, aValue, aType);
}
break;

case tag_unitsize:
if (IsAssembly())
{
return GetObjectMemberValue(id_Assembly, mCostItem, aTag, aType, aValue);
}
else if (IsCostItem())
{
return GetObjectMemberValue(id_CostItem, mCostItem, aTag, aType, aValue);
}
else
{
return ConvertMember(&mUnitSize, type_cstring, aValue, aType);
}
break;

case tag_unitcost:
{
CMoney unitCost = GetUnitCost();
return ConvertMember(&unitCost, type_money, aValue, aType);
}
break;

case tag_grossquantity: // TCS 4/5/02
{
// this value is for takeoff reports. We multiply by the number of units
// in the cost item using this assembly.
CMoney outValue = mQuantity * gGlobalMoney;
outValue.RoundDigits(1);
return ConvertMember(&outValue, type_number, aValue, aType);
}
break;

case tag_netquantity: // TCS 4/5/02
{
// this value is for takeoff reports. We multiply by the number of units
// in the cost item using this assembly.
CMoney outValue = mQuantity * mWaste.GetPercentMultiplier() * gGlobalMoney;
outValue.RoundDigits(1);
return ConvertMember(&outValue, type_number, aValue, aType);
}
break;

case tag_grossprice: // TCS 4/5/02
{
// this value is for takeoff reports. We multiply by the number of units
// in the cost item using this assembly.
CMoney outValue = mQuantity * mWaste.GetPercentMultiplier() * gGlobalMoney * GetUnitCost(false);
outValue.RoundPennies();
return ConvertMember(&outValue, type_money, aValue, aType);
}
break;

case tag_tool: // TCS 4/4/02
if (mCostArea == costtype_tool)
{
return ConvertObjectIDMember(mCostItem, id_Tool, aValue, aType);
}
else
return false;
break;

case tag_reminder: // TCS 4/4/02
if (mCostArea == costtype_reminder)
{
return ConvertObjectIDMember(mCostItem, id_ProjectReminder, aValue, aType);
}
else
return false;
break;

case tag_remindertext: // TCS 4/4/02
{
CTextString outString;

if (mCostArea == costtype_reminder)
{
CProjectReminder *reminder =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_ProjectReminder, mCostItem),
CProjectReminder);
if (reminder)
{
DB_ObjectWatcher watcher (reminder);
outString = reminder->GetMessageText();
}
}

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 CSubAssembly::SetMemberValue(const NeoTag aTag, const NeoTag aType,
const void *aValue)
{
switch (aTag)
{
case tag_category: // TCS 12/12/01
return ConvertDataToObjectID(aValue, aType, &mCategory, id_Category);
break;

case tag_subcategory: // TCS 12/12/01
return ConvertDataToObjectID(aValue, aType, &mSubcategory, id_Category);
break;

case tag_costitem: // we dont use ConvertMemberToObjectID since we may not
// know what type it is yet
if (IsUnlistedItem() || aType == type_cstring) // rev TCS 3/6/02
{
return ConvertMember(aValue, aType, &mItemName, type_cstring);
}
else
return ConvertMember(aValue, aType, &mCostItem, type_objectid);
break;

case tag_itemtext: // the text of the item name TCS 12/18/01
return ConvertMember(aValue, aType, &mItemName, type_cstring);
break;

case tag_sizetext: // the text of the unit size TCS 12/18/01
return ConvertMember(aValue, aType, &mUnitSize, type_cstring);
break;

case tag_ownerobjectclass: // we don't need to set a value for these
case tag_mainaccountclass: // TCS 3/21/00
return true;
break;

case tag_amount: // TCS 12/11/01
return ConvertMember(aValue, aType, &mAmount, type_emoney);
break;

case tag_quantity:
return ConvertMember(aValue, aType, &mQuantity, type_number);
break;

case tag_waste:
return ConvertMember(aValue, aType, &mWaste, type_percent);
break;

case tag_unitcost: // TCS 12/12/01
if (IsUnlistedItem())
return ConvertMember(aValue, aType, &mItemCost, type_emoney);
else
return true;
break;

case tag_unitsize: // TCS 12/12/01
return ConvertMember(aValue, aType, &mUnitSize, type_cstring);
break;

case tag_costarea:
return ConvertMember(aValue, aType, &mCostArea, type_enum);
break;

case tag_labormodifier: // TCS 12/8/01
mUseLaborModifier = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_materialmodifier: // TCS 12/8/01
mUseMaterialModifier = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_fixedquantity: // TCS 12/23/01
mFixedQuantity = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_netquantity: // calculated, no need to set
case tag_grossquantity:
case tag_reminder:
case tag_tool:
return true;
break;

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

ReadObject

read the persistent object's data in from a stream
*********************************************************************************/
void CSubAssembly::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;

ReadTextFromStream(aStream, &mItemName);
ReadTextFromStream(aStream, &mUnitSize);

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

mCostItem = aStream->ReadID(); // mfs_sa rev 20feb2k3
mCategory = aStream->ReadID();
mSubcategory = aStream->ReadID();

mQuantity.ReadFromStream(aStream);
mWaste.ReadFromStream(aStream);

mCostArea = aStream->ReadChar();
*((UInt8*)&mCostArea + sizeof(mCostArea)) = aStream->ReadBits(3); // --Bitfield

mItemCost.ReadFromStream(aStream);

mEndSafetyTag= aStream->ReadEndSafetyTag(this);

// validate the object end marker TCS 9/8/02
if (!IsValidEndTag(mEndSafetyTag))
ReportDamagedObject(GetDBClassID(), GetDBID());
}/*********************************************************************************

WriteObject

write the persistent object's data to a stream
*********************************************************************************/
void CSubAssembly::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, mItemName, cMenuTextLen);
WriteTextToStream(aStream, mUnitSize, cMenuTextLen);

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

aStream->WriteID(mCostItem); // mfs_sa rev 20feb2k3
aStream->WriteID(mCategory);
aStream->WriteID(mSubcategory);

mQuantity.WriteToStream(aStream);
mWaste.WriteToStream(aStream);

aStream->WriteChar(mCostArea);
aStream->WriteChar(*((UInt8*)&mCostArea + sizeof(mCostArea))); // --Bitfield

mItemCost.WriteToStream(aStream);

aStream->WriteEndSafetyTag(mEndSafetyTag, this);

}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

FinishImportCreate TCS 12/27/00

fill in values after creation from an import
*********************************************************************************/
void CSubAssembly::FinishImportCreate()
{
// let the superclass do its stuff
THE_SUPERCLASS::FinishImportCreate();
// update any linked items
AdjustSubassemblyArray();
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

PostNewRecord TCS 12/27/98

we need to adjust the assembly array for the cost item included here

*********************************************************************************/
void CSubAssembly::PostNewRecord(const UInt8 creationType)
{
AdjustSubassemblyArray();
// let the superclass do any posting
THE_SUPERCLASS::PostNewRecord(creationType);
}
/*********************************************************************************

PostDeletion TCS 12/27/98

we remove our parent from the cost item's array

*********************************************************************************/
void CSubAssembly::PostDeletion(const Boolean postAudit)
{
AdjustSubassemblyArray(cRemoveItem);

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

PostRecordChanging TCS 12/28/98

if the cost item has changed, we want to remove ourselves from the old
item's updating array

*********************************************************************************/
void CSubAssembly::PostRecordChanging(const Boolean accountChanging, const Boolean /*jobChanging*/)
{
if (accountChanging)
AdjustSubassemblyArray(cRemoveItem);
}
/*********************************************************************************

PostRecordChanged

a record has changed. Post it.
*********************************************************************************/
void CSubAssembly::PostRecordChanged(const CMoney &/*oldAmount*/, const Boolean accountChanged,
const Boolean /*jobChanged*/)
{
if (accountChanged)
AdjustSubassemblyArray();
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

CanImportEnum TCS 9/14/99

is the given imported value OK? We override to accept mainaccountclass

*********************************************************************************/
Boolean CSubAssembly::CanImportEnum(const TagType tag, const SInt32 value) const
{
if (tag == tag_mainaccountclass)
{
// we don't have menu enum info, so we just accept any value
return true;
}
else
return THE_SUPERCLASS::CanImportEnum(tag, value);
}
/*********************************************************************************

IsMixedCV TCS 12/18/01

return whether the given tag field is a clairvoyant value
*********************************************************************************/
Boolean CSubAssembly::IsMixedCV(const TagType tag) const
{
switch (tag)
{
case tag_costitem:
return !IsUnlistedItem();
break;

default:
return false;
break;
}
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

HandlePriceChange TCS 9/23/99

this item's cost item has changed its price. Update the assembly price
*********************************************************************************/
Boolean CSubAssembly::HandlePriceChange(const CMoney &newAmount, SInt32 &updateCount)
{
// first update the pricing for this item
CMoney netCost = GetNetCost(newAmount);
SetAmount(netCost);

MakeDirty();

// now update totals in the parent assembly
DBid parentID = GetParentAssemblyID();
Boolean success = true;

CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, parentID),
CAssembly);
if (assembly)
{
DB_ObjectWatcher assemblyWatcher(assembly);

UInt8 saveSuccess = assembly->PrepareViewerDisplay();

assembly->RecalcPrice();
success = assembly->UpdateDependentItems(updateCount); // TCS 12/27/00

assembly->UpdateViewerDisplay(saveSuccess); // rev TCS 12/27/00

}
else
ReportMissingObject(id_Assembly, parentID);

return success;
}
/*********************************************************************************

HandleItemsManufactured TCS 11/14/01

some of this assembly has been manufactured. Pass it along to the components
*********************************************************************************/
void CSubAssembly::HandleItemsManufactured(const CMoney &changeAmount, const DBid accountID,
const Boolean isComponent, const Boolean removeItem)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

UInt8 costClass = 0;

if (mCostArea == costtype_assembly || mCostArea == costtype_assmlabor ||
mCostArea == costtype_assmmaterial)
costClass = id_Assembly;
else if (mCostArea == costtype_material)
costClass = id_CostItem;

if (costClass)
{
// the component will change in a manufacturing, so pass it along.
// assembly components will pass the change to their components.
// cost items will reduce inventory count.
CMoney componentAmount = changeAmount * mQuantity;

CUnitCost *component =
TCS_SAFE_CAST(gDBFile->GetOneObject(costClass, mCostItem),
CUnitCost);

if (component)
{
DB_ObjectWatcher watcher(component);
component->HandleItemsManufactured(componentAmount, accountID, isComponent, removeItem);
}
}
}
/*********************************************************************************

GetNetCost TCS 9/23/99

get the net cost of this item, based on the given unit cost
*********************************************************************************/
CMoney CSubAssembly::GetNetCost(const CMoney &unitCost) const
{
CMoney netQuantity = GetNetQuantity();

// figure cost * quantity
return unitCost * netQuantity;
}
/*********************************************************************************

GetNetQuantity TCS 4/9/02

get the net quantity of this item, including waste
*********************************************************************************/
CMoney CSubAssembly::GetNetQuantity() const
{
return mQuantity * mWaste.GetPercentMultiplier();
}
/*********************************************************************************

GetLaborPrice TCS 11/26/03

get the labor cost of this item
*********************************************************************************/
CMoney CSubAssembly::GetLaborPrice() const
{
if (IsAssembly()) // TCS 4/10/04
{
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem), CAssembly);

if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->GetLaborPrice();
}
else
return 0;
}
else if (mCostArea == costtype_labor)
return GetAmount();
else
return 0;
}
/*********************************************************************************

GetMaterialPrice TCS 11/26/03

get the material cost of this item
*********************************************************************************/
CMoney CSubAssembly::GetMaterialPrice() const
{
if (IsAssembly()) // TCS 4/10/04
{
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem), CAssembly);

if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->GetMaterialPrice();
}
else
return 0;
}
else if (mCostArea == costtype_material)
return GetAmount();
else
return 0;
}
/*********************************************************************************

GetOtherPrice TCS 4/10/04

get the material cost of this item
*********************************************************************************/
CMoney CSubAssembly::GetOtherPrice() const
{
if (IsAssembly())
{
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem), CAssembly);

if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->GetOtherPrice();
}
else
return 0;
}
else if (mCostArea != costtype_material && mCostArea != costtype_labor)
return GetAmount();
else
return 0;
}
/*********************************************************************************

FetchComponentCosts TCS 8/2/01

add the the cost of this subassembly to an assembly cost struct
*********************************************************************************/
CMoney CSubAssembly::FetchComponentCosts(SAssemblyUpdateInfo &info, const CMoney &/*inQuantity*/)
{
CMoney totalCost = GetAmount();

switch (mCostArea)
{
case costtype_equipment:
case costtype_unlistedequip:
info.equipmentCost += totalCost;
break;

case costtype_labor:
case costtype_unlistedlabor:
info.laborCost += totalCost;
info.laborHours += mQuantity; // TCS 4/26/02
break;

case costtype_material:
case costtype_unlistedmaterial:
info.materialCost += totalCost;
break;

case costtype_subcontractor:
case costtype_unlistedsub:
info.subcontractorCost += totalCost;
break;

case costtype_other:
case costtype_unlistedother:
info.otherCost += totalCost;
break;

case costtype_assembly: // we need to fetch from the assembly
case costtype_assmlabor:
case costtype_assmmaterial:
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem),
CAssembly);
if (assembly)
{
DB_ObjectWatcher watcher(assembly);
assembly->FetchComponentCosts(info, mQuantity); // rev TCS 8/6/01
}
else
TCS_SysBeep();
}
break;

default:
break;
}

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

GetCostItemClass rev TCS 12/12/01

get the class of item listed in this assembly
*********************************************************************************/
DBClass CSubAssembly::GetCostItemClass() const
{
switch (mCostArea)
{
case costtype_assembly:
case costtype_assmlabor:
case costtype_assmmaterial:
return id_Assembly;
break;

case costtype_equipment:
case costtype_labor:
case costtype_material:
case costtype_subcontractor:
case costtype_other:
return id_CostItem;
break;

case costtype_reminder:
return id_ProjectReminder;
break;

case costtype_tool:
return id_Tool;
break;

default:
return 0;
break;
}

}
/*********************************************************************************

GetPurchasePrice TCS 4/10/02

return the purchase price for this assembly
*********************************************************************************/
CMoney CSubAssembly::GetPurchasePrice() const
{
if (IsUnlistedItem())
return mItemCost;
else if (IsCostItem())
{
CUnitCost *costItem =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_CostItem, mCostItem),
CUnitCost);

if (costItem)
{
DB_ObjectWatcher watcher(costItem);

return costItem->GetPurchasePrice();
}
else
return 0;
}
else
return 0;
}
/*********************************************************************************

GetAmount

return the calculated cost amount for this subassembly. Note that the
returned amount may not be the same as mAmount, since component prices may
have changed. This is a const method so we don't update prices here- calling
methods may want to do it, however.
*********************************************************************************/
CMoney CSubAssembly::GetAmount() const
{
// get the cost item first
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
CMoney amount;

// for assemblies and cost items, we fetch net cost from the component
// unit cost
CUnitCost *costItem = nil;
if (IsAssembly())
{
costItem = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem), CUnitCost);
}
else if (IsCostItem()) // bugfix TCS 12/20/01
{
costItem = TCS_SAFE_CAST(gDBFile->GetOneObject(id_CostItem, mCostItem), CUnitCost);
}
else
{ // for other items we use the stored values to calculate net cost
amount = GetNetCost(mItemCost);
return amount;
}

// we have to calculate the amount from
// the quantity, unit price, & waste
if (costItem)
{ // got the cost item
CMoney unitcost;
DB_ObjectWatcher watcher(costItem);

amount = GetNetCost(costItem->GetComponentPrice());
}
else
amount = 0;

return amount;
}
/*********************************************************************************

GetUnitCost

return the cost for one unit of this item
*********************************************************************************/
CMoney CSubAssembly::GetUnitCost(const Boolean isComponent) const
{
if (IsAssembly() || IsCostItem())
{
UInt8 costClass = IsAssembly() ? id_Assembly : id_CostItem;

CUnitCost *unitCost = TCS_SAFE_CAST(gDBFile->GetOneObject(costClass, mCostItem),
CUnitCost);

if (unitCost)
{
DB_ObjectWatcher watcher (unitCost);

if (isComponent)
return unitCost->GetComponentPrice();
else
return unitCost->GetPurchasePrice();
}
else
return 0;
}
else
{
return mItemCost;
}
}
/*********************************************************************************

CalcLaborHours TCS 3/31/03

return the labor hour quantity for the given number of units
*********************************************************************************/
CMoney CSubAssembly::CalcLaborHours(const CMoney &quantity) const
{
if (mCostArea == costtype_labor)
{
if (HasFixedQuantity())
return mQuantity;
else
return quantity * mQuantity;
}
else if (mCostArea == costtype_assembly || mCostArea == costtype_assmlabor)
{
CAssembly *assembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem),
CAssembly);

if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->CalcLaborHours(quantity * mQuantity);
}
}
return 0;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

AdjustSubassemblyArray TCS 12/28/98

adjust the array for the cost item used in this subassembly. That way the
cost item will know to update us if its price changes.

*********************************************************************************/
void CSubAssembly::AdjustSubassemblyArray(const Boolean removeIt)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// if we don't have a cost item, no updating is needed
if (!mCostItem)
return;

DBClass costClass = GetCostItemClass();

// fetch the cost item
if (costClass == id_CostItem || costClass == id_Assembly)
{
CUnitCost *unitCost = TCS_SAFE_CAST(gDBFile->GetOneObject(costClass,
mCostItem), CUnitCost);

if (unitCost)
{
DB_ObjectWatcher watcher(unitCost);

unitCost->AddToDependentArray(id_SubAssembly, GetDBID(), removeIt); // rev TCS 9/23/99
} // we formerly stored the assembly ID,
else // not the subassembly ID.
ReportMissingObject(costClass, mCostItem); // TCS 12/27/00
}

// we currently do not store dependent arrays in tools or reminders
}
/*********************************************************************************

FillProjectReportArray TCS 4/4/02 rev 4/11/02

fill in data for a project report- schedules, tools or reminders.

*********************************************************************************/
Boolean CSubAssembly::FillProjectReportArray(TReportRowArray *reportRowArray, const DBid reportID,
const DBid breakdownID, SInt32 runLimit) const
{
TCS_FailNILMsg(reportRowArray, TCS_GetErrString(errID_BadArray));
UInt8 costType = GetCostArea();

switch (costType)
{
case costtype_material:
return true;
break;

case costtype_labor: // TCS 8/8/03
if (reportID != id_LaborHoursReport)
return true;
break;

case costtype_tool:
case costtype_unlistedtool:
if (reportID != id_ToolReport && reportID != id_ToolByItemReport)
return true;
break;

case costtype_delay: // TCS 4/29/02
if (reportID != id_ScheduleReport && reportID != id_LaborHoursReport)
return true;
break;

case costtype_reminder:
if (reportID != id_ReminderReport)
return true;
break;

case costtype_assembly: // TCS 4/5/02
case costtype_assmlabor:
case costtype_assmmaterial:
{
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem),
CAssembly);
if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->FillProjectReportArray(reportRowArray, reportID, breakdownID, runLimit);
}
}
break;

default:
return true;
break;
}

// if we get this far, fill in a report row
SReportRowInfo rowInfo;
CReportTable::InitializeRowInfo(rowInfo, rowtype_data);

rowInfo.itemID = GetDBID();
rowInfo.matchValue = breakdownID;
rowInfo.classID = id_SubAssembly;

reportRowArray->Append(rowInfo);

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

FillTakeoffArray TCS 4/9/02

fill in data for a material takeoff report. We only return false if there
was a problem, i.e. over recursion limit.

*********************************************************************************/
Boolean CSubAssembly::FillTakeoffArray(TTakeoffArray &takeoffArray, const CMoney &parentQuantity,
const DBid breakdownID, SInt32 &runLimit, const CDate dateNeeded,
const Boolean isLabor) const
{
UInt8 costType = GetCostArea();

CMoney grossQuantity = mQuantity * parentQuantity;
CMoney netQuantity = GetNetQuantity() * parentQuantity;

if (costType == costtype_assembly || mCostArea == costtype_assmmaterial)
{
CAssembly *assembly =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, mCostItem),
CAssembly);
if (assembly)
{
DB_ObjectWatcher watcher (assembly);

CMoney newQuantity = parentQuantity * mQuantity;
return assembly->FillTakeoffArray(takeoffArray, newQuantity, breakdownID,
runLimit, dateNeeded, isLabor);
}
}
else if (costType == costtype_material)
{
if (!isLabor)
{
AddMaterialTakeoff(takeoffArray, mCostItem, breakdownID, grossQuantity,
netQuantity, dateNeeded, GetPurchasePrice());
}
}
else if (costType == costtype_labor) // TCS 1/22/04
{
if (isLabor)
{
AddMaterialTakeoff(takeoffArray, mCostItem, breakdownID, grossQuantity,
netQuantity, dateNeeded, GetPurchasePrice());
}
}
else if (costType == costtype_unlistedmaterial)
{
if (!isLabor)
{
AddUnlistedTakeoff(takeoffArray, mItemName, breakdownID, grossQuantity,
netQuantity, dateNeeded, GetPurchasePrice());
}
}
else if (costType == costtype_unlistedlabor) // TCS 1/22/04
{
if (isLabor)
{
AddUnlistedTakeoff(takeoffArray, mItemName, breakdownID, grossQuantity,
netQuantity, dateNeeded, GetPurchasePrice());
}
}

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

FillDataReport TCS 9/6/02

fill in a diagnostic table that shows data field values.

*********************************************************************************/
void CSubAssembly::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_name, mItemName);
FillFieldStringRow(table, stream, tag_unitsize, mUnitSize);

FillFieldObjectIDRow(table, stream, tag_costitem, mCostItem, GetCostItemClass());
FillFieldObjectIDRow(table, stream, tag_category, mCategory, id_Category);
FillFieldObjectIDRow(table, stream, tag_subcategory, mSubcategory, id_Category);

FillFieldTagRow(table, stream, tag_quantity, cMoneySize, mQuantity.GetNumberString());
FillFieldTagRow(table, stream, tag_waste, cMoneySize, mWaste.GetPercentString());

FillFieldEnumRow(table, stream, tag_costarea, mCostArea, MENU_AssemblyTableCostTypes);

FillFieldBitRow(table, stream, tag_labormodifier, mUseLaborModifier, true);
FillFieldBitRow(table, stream, tag_materialmodifier, mUseMaterialModifier);
FillFieldBitRow(table, stream, tag_fixedquantity, mFixedQuantity);
FillFieldStockRow(table, stream, stockID_Padding, -5, SInt32(subAssmFiller));

FillFieldTagRow(table, stream, tag_amount, cMoneySize, mItemCost.GetCurrencyString());

FillEndSafetyTag(table, stream, mEndSafetyTag);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

AddMaterialTakeoff (static) TCS 4/9/02

add a material to the given takeoff report array. We declare this as static
so estimate breakdowns can use it too.

*********************************************************************************/
void CSubAssembly::AddMaterialTakeoff(TTakeoffArray &takeoffArray, const DBid costItemID,
const DBid breakdownID, const CMoney &grossQuantity, const CMoney &netQuantity,
const CDate dateNeeded, const CMoney &purchasePrice)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// is the order date within the report date range? TCS 5/14/02
CCostItem *costItem = TCS_SAFE_CAST(gDBFile->GetOneObject(id_CostItem, costItemID),
CCostItem);
if (costItem)
{
DB_ObjectWatcher watcher(costItem);
CDate orderDate = dateNeeded;
SInt16 leadTime = costItem->GetLeadTime();

orderDate.AddDays(-leadTime);

if (!orderDate.IsBetween(gDBFile->GetStartDate(), gDBFile->GetEndDate()))
return;
}
else
return;

TTakeoffArrayIterator iterator(takeoffArray);
STakeoffInfo info;

// first check if this item is already in the array
while (iterator.Next(info))
{
if (info.costItemID == costItemID &&
(!breakdownID || info.breakdownID == breakdownID))
{
info.grossQuantity += grossQuantity;
info.netQuantity += netQuantity;

if (dateNeeded.IsBefore(info.dateNeeded))
info.dateNeeded = dateNeeded;

takeoffArray.AssignItemAt(iterator.GetCurrentIndex(), info);
return;
}
}

// this material item isn't there yet, so append it
info.costItemID = costItemID;
info.grossQuantity = grossQuantity;
info.netQuantity = netQuantity;
info.breakdownID = breakdownID;
info.dateNeeded = dateNeeded;
info.purchasePrice = purchasePrice;

CTextString itemName = gDBFile->GetObjectName(id_CostItem, costItemID);
TCS_BufferFromText(info.itemName, itemName);

takeoffArray.Append(info);
}
/*********************************************************************************

AddUnlistedTakeoff (static) TCS 4/9/02

add an unlisted material to the given takeoff report array. We declare this as static
so estimate breakdowns can use it too.

*********************************************************************************/
void CSubAssembly::AddUnlistedTakeoff(TTakeoffArray &takeoffArray, const CTextString itemName,
const DBid breakdownID, const CMoney &grossQuantity, const CMoney &netQuantity,
const CDate dateNeeded, const CMoney &purchasePrice)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

// is the order date within the report date range? TCS 5/14/02
if (!dateNeeded.IsBetween(gDBFile->GetStartDate(), gDBFile->GetEndDate()))
return;

STakeoffInfo info;

info.costItemID = 0;
info.grossQuantity = grossQuantity;
info.netQuantity = netQuantity;
info.breakdownID = breakdownID;
info.dateNeeded = dateNeeded;
info.purchasePrice = purchasePrice;
TCS_BufferFromText(info.itemName, itemName);

takeoffArray.Append(info);
}