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

Unit Costs (Source Code)

Link to: header | unit cost directory

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

Comments

CUnitCost

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

Each record is a unit cost for estimates & sales. This class is the parent class for Cost Items
and Assemblies. Use it for any unit cost code that applies to both.

formerly named CAbstractCostItem

SUPERCLASS = DB_DescribedPersistent

Constructor

/*********************************************************************************
default constructor
*********************************************************************************/
CUnitCost::CUnitCost()
{
mPurchasePrice = 0;
mProjectPrice = mResalePrice = mReducedPrice = 0;
mComponentPrice = mInventoryPrice = 0;
mWeight = 0;

mCreationDate.SetToNow();
mModDate = mCreationDate;

mCategory = mSubcategory = 0;
mCatSystem = 0; // rev TCS 12/7/01
mMarkup = 0;
mUnitSize = mDiscount = mSuggestDimension = 0;
mSubcontractType = 0;

mReorderAmount = 0;

mStatus = status_Active;
mContractCostDisplay = contract_approx;
mContractQuantDisplay = contract_approx;

mTaxSale = mTaxPurchase = mHasInventory = true;
mRestockingCharge = mCalcDimension = false;
mCanManufacture = false;
mContractAsList = false; // TCS 5/9/02
mAlwaysShow = false;

mPrintFlagged = true;
mFlagged = false;
mCalcLaborModifier = mCalcMaterialModifier = false; // TCS 12/7/01
mIncludeInStarterFile = true; // TCS 12/5/02
mAllowVariablePrices = false; // TCS 1/3/03

mUCFiller = 0;
mUCPadding = 0;

mPicture = 0;

mInventoryQty = 0;
mItemCode = 0; // TCS 5/18/01

mCurrentUser = 0;
mDisplayingViewer = nil; // TCS bugfix 9/8/00
mIsPosting = false; // TCS bugfix 10/1/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 CUnitCost::CopyFrom(DB_PersistentObject *source, const UInt8 copyFlags)
{
THE_SUPERCLASS::CopyFrom(source, copyFlags);

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

NeoVersion version = src->GetVersion();

// copy over the members.
if (version > 1)
TCS_BlockMove(&src->mPurchasePrice, &mPurchasePrice, cCopyFileLength2);
else
TCS_BlockMove(&src->mPurchasePrice, &mPurchasePrice, cCopyFileLength);

// copy the strings by hand TCS 12/13/01
mFullName = src->mFullName;
mContractText = src->mContractText;

if (version > 4) // TCS 5/20/04
mUPCCode = src->mUPCCode;

// non-calc booleans must be copied by hand TCS 12/11/01
mCalcLaborModifier = src->mCalcLaborModifier;
mCalcMaterialModifier = src->mCalcMaterialModifier;
mFlagged = src->mFlagged;

// copy any custom fields TCS 3/5/04
if (version > 1)
{
CCustomFieldOwner::CopyFrom(*(CCustomFieldOwner*)src, GetDBClassID());
}

// we don't copy any of the member arrays- those will be filled in by posting

// item code must be unique so we assign it here
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
mItemCode = gDBFile->GetNextObjectID(id_UnitCost);

// pictures must be duplicated. Hence we make a copy of
// the picture referenced by mPicture, if any
if (mPicture)
{
// need to copy the picture. Let's first get
// the picture
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
DB_Picture *sourcePicture =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_Picture, mPicture), DB_Picture);
TCS_FailNILMsg(sourcePicture, TCS_GetErrString(errID_BadGraphic));

// watch it so it doesn't leak
DB_ObjectWatcher watcher1(sourcePicture);

// got the picture, let's make a copy of it TCS tidied 11/13/01
DB_Picture *newPicture = TCS_SAFE_CAST(gDBFile->CreateObjectFromAnother(sourcePicture),
DB_Picture);
TCS_FailNILMsg(newPicture, TCS_GetErrString(errID_BadGraphic));
DB_ObjectWatcher watcher2(newPicture);

newPicture->FinishNonViewerCreate(); // TCS 5/5/03

// add it to the database TCS 5/1/03
gDBFile->AddObject(newPicture);

// now that we have the copy, make sure that
// our member points to it
mPicture = newPicture->GetDBID();
}
}
/*********************************************************************************

GetFileLength

return the file length to reserve for this object

*********************************************************************************/
NeoSize CUnitCost::GetFileLength(const CNeoFormat *aFormat) const
{

long baseLength = THE_SUPERCLASS::GetFileLength(aFormat) +
ARRAY_FILE_SIZE(mDependentArray) +
ARRAY_FILE_SIZE(mEstimateArray) +
ARRAY_FILE_SIZE(mSaleArray) +
ARRAY_FILE_SIZE(mTimeSpentArray) +
ARRAY_FILE_SIZE(mTransferArray) +
ARRAY_FILE_SIZE(mInventoryUsedArray) +
mFullName.FileLength(cStandardTextLen) + // rev TCS 12/24/01
mContractText.LongFileLength(cMaximumTextLen);

NeoVersion version = GetVersion();

if (version > 4) // TCS 5/20/04
return baseLength + CCustomFieldOwner::GetFileLength(aFormat) +
mUPCCode.FileLength(cMenuTextLen) +
cFileLength2;
else if (version > 1) // TCS 3/5/04
return baseLength + CCustomFieldOwner::GetFileLength(aFormat) +
cFileLength2;
else
return baseLength + cFileLength;
}
/*********************************************************************************

GetMemberValue

return the value of the member with the given tag

*********************************************************************************/
Boolean CUnitCost::GetMemberValue(const NeoTag aTag, const NeoTag aType,
void *aValue) const
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
CMoney amount = 0;

switch (aTag)
{
case tag_unitsize:
return ConvertObjectIDMember(mUnitSize, id_UnitSize, aValue, aType);
break;

case tag_discount:
return ConvertObjectIDMember(mDiscount, id_ItemDiscount, aValue, aType);
break;

case tag_catsystem:
return ConvertObjectIDMember(mCatSystem, id_CategorySystem, aValue, aType);
break;

case tag_markup:
return ConvertObjectIDMember(mMarkup, id_MarkupSystem, aValue, aType);
break;

case tag_category:
return ConvertObjectIDMember(mCategory, id_Category, aValue, aType);
break;

case tag_subcategory: // no need for subcat convert since no change order or allow
return ConvertObjectIDMember(mSubcategory, id_Category, aValue, aType);
break;

case tag_dimension: // Note that we do not need to include location
case tag_suggestedqty: // dimensions here, since they're only intermediates.
if (mCalcDimension) // TCS 5/9/01
return ConvertObjectIDMember(mSuggestDimension, id_CalcDimension, aValue, aType);
else
return ConvertObjectIDMember(mSuggestDimension, id_Dimension, aValue, aType);
break;

case tag_subcontracttype:
return ConvertObjectIDMember(mSubcontractType, id_SubcontractType, aValue, aType);
break;

case tag_dimensionclass: // TCS 7/5/01
{
UInt8 suggestedClass;
if (mCalcDimension)
suggestedClass = id_CalcDimension;
else
suggestedClass = id_Dimension;
return ConvertMember(&suggestedClass, type_objclass, aValue, aType);
}
break;

case tag_status:
return ConvertEnumMember(mStatus, MENU_CostItemStatus, aValue, aType);
break;

case tag_contractcostdisplay:
return ConvertEnumMember(mContractCostDisplay, MENU_ContractNumberDisplay, aValue, aType);
break;

case tag_contractquantdisplay:
return ConvertEnumMember(mContractQuantDisplay, MENU_ContractNumberDisplay, aValue, aType);
break;

case tag_purchaseprice:
return ConvertMember(&mPurchasePrice, type_emoney, aValue, aType);
break;

case tag_projectprice:
return ConvertMember(&mProjectPrice, type_emoney, aValue, aType);
break;

case tag_resaleprice:
return ConvertMember(&mResalePrice, type_emoney, aValue, aType);
break;

case tag_reducedprice:
return ConvertMember(&mReducedPrice, type_emoney, aValue, aType);
break;

case tag_componentprice:
return ConvertMember(&mComponentPrice, type_emoney, aValue, aType);
break;

case tag_inventoryprice: // TCS 11/16/01
return ConvertMember(&mInventoryPrice, type_emoney, aValue, aType);
break;

case tag_weight: // TCS 10/23/02
return ConvertMember(&mWeight, type_emoney, aValue, aType);
break;

case tag_creationdate:
return ConvertMember(&mCreationDate, type_date, aValue, aType);
break;

case tag_moddate:
return ConvertMember(&mModDate, type_date, aValue, aType);
break;

case tag_reorderamount:
return ConvertMember(&mReorderAmount, type_long, aValue, aType);
break;

case tag_inventoryqty:
return ConvertMember(&mInventoryQty, type_number, aValue, aType);
break;

case tag_inventoryshortage: // TCS 1/5/01
{
if (mHasInventory)
{
CMoney reorderNumber = CMoney(mReorderAmount);
if (mInventoryQty.IsLessThan(reorderNumber))
amount = reorderNumber - mInventoryQty;
}
return ConvertMember(&amount, type_number, aValue, aType);
}
break;

case tag_hasshortage: // TCS 12/9/02
{
Boolean hasShortage = false;
if (mHasInventory && (GetCostArea() == costtype_material ||
GetCostArea() == costtype_assembly ||
GetCostArea() == costtype_assmmaterial))
{
CMoney reorderNumber = CMoney(mReorderAmount);
if (mInventoryQty.IsLessThan(reorderNumber))
hasShortage = true;
}
return ConvertMember(&hasShortage, type_boolean, aValue, aType);
}
break;

case tag_itemcode:
return ConvertMember(&mItemCode, type_number, aValue, aType);
break;

case tag_secondname:
return ConvertMember(&mFullName, type_cstring, aValue, aType);
break;

case tag_upccode: // TCS 5/19/04
return ConvertMember(&mUPCCode, type_cstring, aValue, aType);
break;

case tag_contracttext:
return ConvertMember(&mContractText, type_longcstring, aValue, aType);
break;

case tag_picture:
return ConvertMember(&mPicture, type_pictid, aValue, aType);
break;

case tag_taxsale:
return ConvertBitFieldMember(mTaxSale, aValue, aType);
break;

case tag_taxpurchase:
return ConvertBitFieldMember(mTaxPurchase, aValue, aType);
break;

case tag_hasinventory:
return ConvertBitFieldMember(mHasInventory, aValue, aType);
break;

case tag_restockingcharge: // added TCS 4/27/99
return ConvertBitFieldMember(mRestockingCharge, aValue, aType);
break;

case tag_calcdimension: // added TCS 6/13/99 Note that we can use a boolean for this
// because suggested will be either a dimension or calc dimension.
// location dimensions are never referenced directly.
return ConvertBitFieldMember(mCalcDimension, aValue, aType);
break;

case tag_calclabormod:
return ConvertBitFieldMember(mCalcLaborModifier, aValue, aType);
break;

case tag_calcmaterialmod:
return ConvertBitFieldMember(mCalcMaterialModifier, aValue, aType);
break;

case tag_canmanufacture:
return ConvertBitFieldMember(mCanManufacture, aValue, aType);
break;

case tag_contractaslist: // TCS 5/9/02 NOT USED
return ConvertBitFieldMember(mContractAsList, aValue, aType);
break;

case tag_flagged: // TCS 1/31/00
return ConvertBitFieldMember(mFlagged, aValue, aType);
break;

case tag_printflagged: // TCS 1/31/00
return ConvertBitFieldMember(mPrintFlagged, aValue, aType);
break;

case tag_alwaysshow: // TCS 8/13/02
return ConvertBitFieldMember(mAlwaysShow, aValue, aType);
break;

case tag_includeinstarter: // TCS 12/5/02
return ConvertBitFieldMember(mIncludeInStarterFile, aValue, aType);
break;

case tag_allowvariable: // TCS 1/3/03
return ConvertBitFieldMember(mAllowVariablePrices, aValue, aType);
break;

case tag_useinassemblies: // we fetch these from the markup system
case tag_useinprojects: // TCS 8/22/00
case tag_useinsales:
case tag_useinreducedsales:
{
Boolean useIt = true;

CMarkupSystem *markup =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_MarkupSystem, mMarkup),
CMarkupSystem);
if (markup)
{
DB_ObjectWatcher watcher(markup);

if (aTag == tag_useinassemblies)
useIt = markup->UseInAssemblies();
else if (aTag == tag_useinprojects)
useIt = markup->UseInProjects();
else if (aTag == tag_useinsales)
useIt = markup->UseInSales();
else if (aTag == tag_useinreducedsales)
useIt = markup->UseInReducedSales();
}

return ConvertMember(&useIt, type_boolean, aValue, aType);
}
break;

case tag_daterangematerials: // purchase use TCS 1/5/01
amount = GetDateRangePurchases(gDBFile->GetStartDate(), gDBFile->GetEndDate());
return ConvertMember(&amount, type_money, aValue, aType);
break;

case tag_daterangehours: // time log use TCS 1/5/01
amount = GetDateRangeHours(gDBFile->GetStartDate(), gDBFile->GetEndDate());
return ConvertMember(&amount, type_money, aValue, aType);
break;

case tag_daterangesales: // project purchases TCS 1/5/01
amount = GetDateRangeSales(gDBFile->GetStartDate(), gDBFile->GetEndDate());
return ConvertMember(&amount, type_money, aValue, aType);
break;

case tag_daterangetandm: // project use TCS 1/5/01
amount = GetDateRangeProjectUse(gDBFile->GetStartDate(), gDBFile->GetEndDate());
return ConvertMember(&amount, type_money, aValue, aType);
break;

case tag_daterangeother: // inventory used TCS 11/13/01
amount = GetDateRangeInventoryUsed(gDBFile->GetStartDate(), gDBFile->GetEndDate());
return ConvertMember(&amount, type_money, aValue, aType);
break;

default: // it may be a custom field? TCS rev 3/5/04
if (CCustomFieldOwner::GetCustomFieldValue(aTag, aType, aValue, GetDBClassID()))
return true;
else
return THE_SUPERCLASS::GetMemberValue(aTag, aType, aValue);
break;
}
}
/*********************************************************************************

SetMemberValue

set the value of the member with the given tag

*********************************************************************************/
Boolean CUnitCost::SetMemberValue(const NeoTag aTag, const NeoTag aType,
const void *aValue)
{
CMoney zeroValue = 0;
CTextString inString;
switch (aTag)
{
case tag_dimension:
case tag_suggestedqty:
if (mCalcDimension) // rev TCS 5/9/01
return ConvertDataToObjectID(aValue, aType, &mSuggestDimension, id_CalcDimension);
else
return ConvertDataToObjectID(aValue, aType, &mSuggestDimension, id_Dimension);
break;

case tag_unitsize:
return ConvertDataToObjectID(aValue, aType, &mUnitSize, id_UnitSize);
break;

case tag_discount:
return ConvertDataToObjectID(aValue, aType, &mDiscount, id_ItemDiscount);
break;

case tag_catsystem:
return ConvertDataToObjectID(aValue, aType, &mCatSystem, id_CategorySystem);
break;

case tag_markup:
return ConvertDataToObjectID(aValue, aType, &mMarkup, id_MarkupSystem);
break;

case tag_category:
return ConvertDataToObjectID(aValue, aType, &mCategory, id_Category);
break;

case tag_subcategory:
return ConvertDataToObjectID(aValue, aType, &mSubcategory, id_Category);
break;

case tag_subcontracttype:
return ConvertDataToObjectID(aValue, aType, &mSubcontractType, id_SubcontractType);
break;

case tag_contractcostdisplay:
return ConvertMember(aValue, aType, &mContractCostDisplay, type_enum);
break;

case tag_contractquantdisplay:
return ConvertMember(aValue, aType, &mContractQuantDisplay, type_enum);
break;

case tag_status:
return ConvertMember(aValue, aType, &mStatus, type_enum);
break;

case tag_creationdate:
return ConvertMember(aValue, aType, &mCreationDate, type_date);
break;

case tag_moddate:
return ConvertMember(aValue, aType, &mModDate, type_date);
break;

case tag_reorderamount:
return ConvertMember(aValue, aType, &mReorderAmount, type_long);
break;

case tag_purchaseprice:
return ConvertMember(aValue, aType, &mPurchasePrice, type_emoney);
break;

case tag_weight: // TCS 10/23/02
return ConvertMember(aValue, aType, &mWeight, type_emoney);
break;

case tag_projectprice: // if the field is 'n/a', we skip it rev TCS 9/14/00
ConvertMember(aValue, aType, &inString, type_cstring);
if (inString.IsValidNumber(false, false))
mProjectPrice = CMoney(inString);
else
mProjectPrice = mPurchasePrice; // TCS rev 7/27/01
return true;
break;
case tag_resaleprice: // if the field is 'n/a', we skip it rev TCS 9/14/00
ConvertMember(aValue, aType, &inString, type_cstring);
if (inString.IsValidNumber(false, false))
mResalePrice = CMoney(inString);
else
mResalePrice = mPurchasePrice; // TCS rev 7/27/01
return true;
break;

case tag_reducedprice: // if the field is 'n/a', we skip it rev TCS 9/14/00
ConvertMember(aValue, aType, &inString, type_cstring);
if (inString.IsValidNumber(false, false))
mReducedPrice = CMoney(inString);
else
mReducedPrice = mPurchasePrice; // TCS rev 7/27/01
return true;
break;

case tag_componentprice: // if the field is 'n/a', we skip it rev TCS 9/14/00
ConvertMember(aValue, aType, &inString, type_cstring);
if (inString.IsValidNumber(false, false))
mComponentPrice = CMoney(inString);
else
mComponentPrice = mPurchasePrice; // TCS rev 7/27/01
return true;
break;

case tag_inventoryprice: // TCS 11/16/01
ConvertMember(aValue, aType, &inString, type_cstring);
if (inString.IsValidNumber(false, false))
mInventoryPrice = CMoney(inString);
else
mInventoryPrice = mPurchasePrice;
return true;
break;

case tag_inventoryqty:
return ConvertMember(aValue, aType, &mInventoryQty, type_number);
break;

case tag_itemcode:
return ConvertMember(aValue, aType, &mItemCode, type_number);
break;

case tag_secondname:
return SafeConvertString(aValue, aType, &mFullName);
break;
case tag_upccode: // TCS 5/19/04
return SafeConvertString(aValue, aType, &mUPCCode);
break;

case tag_contracttext:
return SafeConvertString(aValue, aType, &mContractText, type_longcstring);
break;

case tag_picture:
return ConvertMember(aValue, aType, &mPicture, type_pictid);
break;

case tag_taxsale:
mTaxSale = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_taxpurchase:
mTaxPurchase = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_hasinventory:
mHasInventory = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_restockingcharge: // added TCS 4/27/99
mRestockingCharge = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_calcdimension: // added TCS 6/13/99
mCalcDimension = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_flagged: // TCS 1/31/00
mFlagged = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_printflagged: // TCS 1/31/00
mPrintFlagged = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_calclabormod: // TCS 12/7/01
mCalcLaborModifier = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_calcmaterialmod: // TCS 12/7/01
mCalcMaterialModifier = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_canmanufacture: // TCS 3/21/02
mCanManufacture = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_contractaslist: // TCS 5/9/02 NOT USED
mContractAsList = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_alwaysshow: // TCS 8/13/02
mAlwaysShow = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_includeinstarter: // TCS 12/5/02
mIncludeInStarterFile = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_allowvariable: // TCS 1/3/03
mAllowVariablePrices = ConvertDataToBitField(aValue, aType);
return true;
break;

case tag_useinassemblies: // no need to set these
case tag_useinprojects:
case tag_useinsales:
case tag_useinreducedsales:
case tag_inventoryshortage:
case tag_daterangematerials:
case tag_daterangehours:
case tag_daterangetandm:
case tag_dimensionclass:
case tag_hasshortage:
return true;
break;

default: // TCS 3/5/04
if (CCustomFieldOwner::SetCustomFieldValue(aTag, aType, aValue, GetDBClassID()))
return true;
else
return THE_SUPERCLASS::SetMemberValue(aTag, aType, aValue);
break;
}
}/*********************************************************************************

ReadObject

read the persistent object's data in from a stream
*********************************************************************************/
void CUnitCost::ReadObject(CNeoStream *aStream, const TagType aTag)
{
TCS_FailNILMsg(aStream, TCS_GetErrString(errID_BadStream));

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

THE_SUPERCLASS::ReadObject(aStream, aTag);

if (!IsIOValid()) // TCS 2/5/02
return;

NeoVersion version = GetVersion();

if (version > 1) // TCS 3/5/04
{
if (!CCustomFieldOwner::ReadObject(*aStream, this))
{
SetHadIOProblems();
return;
}
}

ReadIDArrayFromStream(aStream, mDependentArray, cHasSafetyTag);
ReadIDArrayFromStream(aStream, mEstimateArray, cHasSafetyTag);
ReadIDArrayFromStream(aStream, mSaleArray, cHasSafetyTag);
ReadInfoArrayFromStream(aStream, mTimeSpentArray, cHasSafetyTag);
ReadInfoArrayFromStream(aStream, mTransferArray, cHasSafetyTag);

ReadIDArrayFromStream(aStream, mInventoryUsedArray, cHasSafetyTag);

ReadTextFromStream(aStream, &mFullName);
ReadLongTextFromStream(aStream, &mContractText);

if (version > 4) // TCS 5/20/04
ReadTextFromStream(aStream, &mUPCCode);

mPurchasePrice.ReadFromStream(aStream); // mfs_sa 20feb2k3
mProjectPrice.ReadFromStream(aStream);
mResalePrice.ReadFromStream(aStream);
mReducedPrice.ReadFromStream(aStream);
mComponentPrice.ReadFromStream(aStream);
mInventoryPrice.ReadFromStream(aStream);
mWeight.ReadFromStream(aStream);

mCategory = aStream->ReadID();
mSubcategory = aStream->ReadID();
mCatSystem = aStream->ReadID();
mMarkup = aStream->ReadID();
mUnitSize = aStream->ReadID();
mDiscount = aStream->ReadID();
mSuggestDimension = aStream->ReadID();
mSubcontractType = aStream->ReadID();

mReorderAmount = aStream->ReadLong();

mStatus = aStream->ReadChar();
mContractCostDisplay = aStream->ReadChar();
mContractQuantDisplay = aStream->ReadChar();

*((UInt8*)&mContractQuantDisplay + sizeof(mContractQuantDisplay)) = aStream->ReadBits(8); // --Bitfield
*((UInt8*)&mContractQuantDisplay + sizeof(mContractQuantDisplay) + 1) = aStream->ReadBits(6); // --Bitfield

mUCPadding = aStream->ReadChar();

mPicture = aStream->ReadID();

mInventoryQty.ReadFromStream(aStream);
mItemCode.ReadFromStream(aStream);

mCreationDate.ReadFromStream(aStream);
mModDate.ReadFromStream(aStream);
}
/*********************************************************************************

WriteObject

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

CNeoDebugExport checker(aStream, this);
THE_SUPERCLASS::WriteObject(aStream, aTag);

NeoVersion version = GetVersion();

if (version > 1) // TCS 3/5/04
{
if (!CCustomFieldOwner::WriteObject(*aStream, this))
{
SetHadIOProblems();
return;
}
}

WriteIDArrayToStream(aStream, mDependentArray, cHasSafetyTag);
WriteIDArrayToStream(aStream, mEstimateArray, cHasSafetyTag);
WriteIDArrayToStream(aStream, mSaleArray, cHasSafetyTag);
WriteInfoArrayToStream(aStream, mTimeSpentArray, cHasSafetyTag);
WriteInfoArrayToStream(aStream, mTransferArray, cHasSafetyTag);
WriteIDArrayToStream(aStream, mInventoryUsedArray, cHasSafetyTag);

WriteTextToStream(aStream, mFullName, cStandardTextLen); // TCS rev 12/24/01
WriteLongTextToStream(aStream, mContractText, cMaximumTextLen);

if (version > 4) // TCS 5/20/04
WriteTextToStream(aStream, mUPCCode, cMenuTextLen);

mPurchasePrice.WriteToStream(aStream); // mfs_sa rev 20feb2k3
mProjectPrice.WriteToStream(aStream);
mResalePrice.WriteToStream(aStream);
mReducedPrice.WriteToStream(aStream);
mComponentPrice.WriteToStream(aStream);
mInventoryPrice.WriteToStream(aStream);
mWeight.WriteToStream(aStream);

aStream->WriteID(mCategory);
aStream->WriteID(mSubcategory);
aStream->WriteID(mCatSystem);
aStream->WriteID(mMarkup);
aStream->WriteID(mUnitSize);
aStream->WriteID(mDiscount);
aStream->WriteID(mSuggestDimension);
aStream->WriteID(mSubcontractType);

aStream->WriteLong(mReorderAmount);

aStream->WriteChar(mStatus);
aStream->WriteChar(mContractCostDisplay);
aStream->WriteChar(mContractQuantDisplay);

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

aStream->WriteChar(mUCPadding);

aStream->WriteID(mPicture);

mInventoryQty.WriteToStream(aStream);
mItemCode.WriteToStream(aStream);

mCreationDate.WriteToStream(aStream);
mModDate.WriteToStream(aStream);

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

FinishImportCreate

fill in values after creation from an import TCS 8/11/99
*********************************************************************************/
void CUnitCost::FinishImportCreate()
{
// let the superclass do its stuff
THE_SUPERCLASS::FinishImportCreate();

// update the reference in any suggested quantities TCS 9/3/02
UpdateDimensions();

// fill in a sequential code number if none was already imported TCS 5/18/01
if (mItemCode.IsZero())
mItemCode = gDBFile->GetNextObjectID(id_UnitCost);
// calculate net prices
RecalcNetPrices();
}
/*********************************************************************************

FinishViewerCreate TCS 11/15/00

fill in values after creation in a viewer. We fetch an item code from
an otherwise unused series of potential values.
*********************************************************************************/
void CUnitCost::FinishViewerCreate()
{
// let the superclass do its stuff, if anything TCS 5/18/01
THE_SUPERCLASS::FinishViewerCreate();
mItemCode = gDBFile->GetNextObjectID(id_UnitCost);
}
/*********************************************************************************

FinishNonViewerCreate

fill in values after creation outside a regular viewer. Not sure this will
ever be used...
*********************************************************************************/
void CUnitCost::FinishNonViewerCreate()
{
// let the superclass do its stuff, if anything
THE_SUPERCLASS::FinishNonViewerCreate();

// update the reference in any suggested quantities TCS 9/3/02
UpdateDimensions();
// calculate net prices
RecalcNetPrices();
}
/*********************************************************************************

FinishTemplateCreate TCS 8/17/01

fill in values after creation via template or recurring.
We'd better recalc, since prices may have changed
*********************************************************************************/
void CUnitCost::FinishTemplateCreate()
{
// let the superclass do its stuff, if anything
THE_SUPERCLASS::FinishTemplateCreate();

// update the reference in any suggested quantities TCS 9/3/02
UpdateDimensions();
// calculate net prices
RecalcNetPrices();
}
/*********************************************************************************

FinishClientCreate TCS 5/9/03

fill in values after creation in a client
*********************************************************************************/
void CUnitCost::FinishClientCreate()
{
// let the superclass do its stuff, if anything
THE_SUPERCLASS::FinishTemplateCreate();

// update the reference in any suggested quantities
UpdateDimensions();
// calculate net prices
RecalcNetPrices();
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

PostNewRecord TCS 12/27/00

post creation of a transaction. Note that we do NOT get here during an
import.
*********************************************************************************/
void CUnitCost::PostNewRecord(const UInt8 /*creationType*/)
{
// create an audit trail entry
CAuditEntry::CreateAuditEntry(entry_newtransaction, this);

// update any linked prices. We keep track of depth so we don't
// go forever if we've somehow created a circular ref, despite all
// code designed to prevent it.
SInt32 updateCount = 0;
UpdateDependentItems(updateCount);

// update the reference in any suggested quantities
// this is also where we update use counts TCS rev 11/21/03
UpdateDimensions();

// let the file deal with the oldest date TCS 6/27/99
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
gDBFile->UpdateEarlyLateDates(mModDate);
}
/*********************************************************************************

PostDeletion TCS 12/27/00

post deletion of a transaction.
*********************************************************************************/
void CUnitCost::PostDeletion(const Boolean /*postAudit*/)
{
// create an audit trail entry
CAuditEntry::CreateAuditEntry(entry_transactiondeleted, this);

// update the reference in any suggested quantities
// this is also where we update use counts TCS rev 11/21/03
UpdateDimensions(cRemoveItem);

// no price updating needed here.
}
/*********************************************************************************

PostRecordChanging TCS 9/3/02

a record will be changing. Post the change.
*********************************************************************************/
void CUnitCost::PostRecordChanging(const Boolean /*accountChanging*/, const Boolean /*jobChanging*/)
{
// update the reference in any suggested quantities
// this is also where we update use counts TCS rev 11/21/03
UpdateDimensions(cRemoveItem);

// no price updating needed here.
}
/*********************************************************************************

PostRecordChanged TCS 12/27/00

post changes in the transaction.
*********************************************************************************/
void CUnitCost::PostRecordChanged(const CMoney &/*oldAmount*/, const Boolean /*accountChanged*/,
const Boolean /*jobChanged*/)
{
// create an audit trail entry
CAuditEntry::CreateAuditEntry(entry_transactionchanged, this);

// update dependent items. We reset the calc flag first so the
// update is done once and only once
SInt32 updateCount = 0;
UpdateDependentItems(updateCount);

// update the reference in any suggested quantities
// this is also where we update use counts TCS rev 11/21/03
UpdateDimensions();

// let the file deal with the oldest date TCS 6/27/99
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
gDBFile->UpdateEarlyLateDates(mModDate);

// no need to pass it any further- this is the end of the line
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

CanBeDeleted TCS 12/27/00

can this item be deleted? If it is included in any assemblies, it can't be.
*********************************************************************************/
Boolean CUnitCost::CanBeDeleted(const Boolean giveMessage) const
{
if (mDependentArray.GetCount() > 0 || mEstimateArray.GetCount() > 0 ||
mSaleArray.GetCount() > 0 || mTransferArray.GetCount() > 0) // we have dependents
{
if (giveMessage)
{
TCS_ErrorAlert(TCS_GetMsgString(msgID_CantDeleteItem));
}
return false;
}
else // rev TCS 11/20/03
return true;
}
/*********************************************************************************

HasActiveStatus TCS 8/27/99

return which status types are considered active

*********************************************************************************/
/*Boolean CUnitCost::HasActiveStatus(const UInt8 inStatus) const
{
UInt8 testStatus = inStatus ? inStatus : mStatus;

switch (testStatus)
{
case status_Active:
case status_SpecialOrder:
return true;

default:
return false;
}
}*/
/*********************************************************************************

HasSpecialStatus TCS 8/27/99

return which status types are considered special

*********************************************************************************/
Boolean CUnitCost::HasSpecialStatus() const
{
return (mStatus == status_SpecialOrder);
}
/*********************************************************************************

IsTaxable TCS 7/23/01

return whether this item is taxable in the given transaction's breakdowns

*********************************************************************************/
Boolean CUnitCost::IsTaxable(const DBClass ownerClass) const
{
switch (ownerClass)
{
case id_MaterialPurchase:
case id_SubcontractorLog:
case id_OtherCost:
case id_InventoryUsed:
return mTaxPurchase;
break;

case id_Sale:
case id_Estimate:
return mTaxSale;
break;

default:
return false;
break;
}
}
/*********************************************************************************

HasFixedQuantity (static) TCS 11/26/03

return whether the given cost item includes a fixed quantity (same amount
no matter what quantity is used). That requires special handling in price
calculations. We use a static method so we can easily fetch info from the
server, and avoid fetching all the subassemblies.

*********************************************************************************/
Boolean CUnitCost::HasFixedQuantity(const DBClass classID, const DBid id)
{
if (classID == id_CostItem)
return false;
else if (classID != id_Assembly)
{
TCS_DebugAlert("Oops, bad id in CUnitCost::HasFixedQuantity!");
return false;
}

#if TCS_MULTIUSER
if (gIsClient)
{
// if this is a client, we pass it along to the server,
// to cut down on the number of objects that need to be retrieved
CTCS_NetworkMessage ioMessage(msg_HasFixedQuantity);
ioMessage.SetRecordID(id);

if (gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning))
{
return ioMessage.GetFirstByte();
}
else
return false;
}
#endif
CAssembly *assembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, id), CAssembly);

if (assembly)
{
DB_ObjectWatcher watcher (assembly);
return assembly->HasFixedQuantity();
}
else
return false;
}
/*********************************************************************************

GetEnumMenuID TCS 12/7/01

return the id of the menu used for the given tag. Suggested and modifier
fields use a checkbox to set their menu class ID

*********************************************************************************/
ResIDT CUnitCost::GetEnumMenuID(const TagType tag, const Boolean importing) const
{
if (tag == tag_suggestedqty)
{
if (mCalcDimension)
return id_CalcDimension;
else
return id_Dimension;
}
else if (tag == tag_labormodifier)
{
if (mCalcLaborModifier)
return id_CalcDimension;
else
return id_Dimension;
}
else if (tag == tag_materialmodifier)
{
if (mCalcMaterialModifier)
return id_CalcDimension;
else
return id_Dimension;
}
else
return THE_SUPERCLASS::GetEnumMenuID(tag, importing);
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

UpdateDependentItems

update any items which are dependent on this item. ThIs is usually called
when the price of this item has changed.

This method is called recursively. We use the updateCount value to make sure
we don't get caught in an infinite loop

*********************************************************************************/
Boolean CUnitCost::UpdateDependentItems(SInt32 &updateCount)
{
CCursorSpinner spinner;

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

SInt32 linkedItemCount = mDependentArray.GetCount();
// we can exit now if this item is not used in any assemblies
if (linkedItemCount < 1)
{
return true;
}

// we'll also exit now if we have recursed too deeply
if (updateCount > cRecursionLimit)
{
TCS_DebugAlert("Oops, too many linked items in CUnitCost::UpdateDependentItems!");
return false;
}
else
updateCount++; // increment the depth counter

// if we got here, there is at least one subassembly which is linked
// to us. We'll set up to loop through the subassembly list and
// force a recalc of each affected assembly's price
TObjectIDArrayIterator iter(mDependentArray);
DBid subID;
Boolean success = true;
CSubAssembly *subAssembly = nil;

// If we have more than a few linked items, show a progress window.
CProgressBar *progressBar = nil;

if (linkedItemCount > 20)
{
progressBar = CProgressBar::CreateBar(TCS_GetStockString(stockID_UpdatingLinkedPrices)); // rev TCS 12/27/00
}

// watch the dialog so it's automatically deleted at the end of this scope
CProgressBarWatcher watcher(progressBar);

if (progressBar)
{
progressBar->SetValue(0);
progressBar->SetMaxValue(linkedItemCount);
progressBar->SetWindowTitle(TCS_GetStockString(stockID_UpdatingPrices));
progressBar->SetTextStep(5);
progressBar->SetActionType(cEmptyString);
progressBar->Refresh();
}

while (iter.Next(subID) && success)
{
if (progressBar)
progressBar->Increment();
// find the subassembly
++spinner;
subAssembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID),
CSubAssembly);

if (subAssembly)
{ // have the subassembly deal with the price change
DB_ObjectWatcher subWatcher(subAssembly);
success = subAssembly->HandlePriceChange(GetComponentPrice(), updateCount); // rev TCS 9/23/99, 12/27/00
}
else
{
// item is missing. Report an error once
ReportMissingObject(id_SubAssembly, subID);
gStopShowingErrors = true;

// now let's clean up the array. Since this method may be called with
// the object in or out of the database, we have to be very careful TCS rev 11/20/03
if (IsInDatabase())
{
DB_ObjectTempRemover remover(this);
if (remover.WasRemoved())
mDependentArray.RemoveItemAt(iter.GetCurrentIndex());
}
else
{
mDependentArray.RemoveItemAt(iter.GetCurrentIndex());
}
}

// save changes every 100 items. TCS 9/25/02
if (iter.GetCurrentIndex() % 100 == 90)
gDBFile->SaveAllAndPurge();
}

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

UpdateDimensions TCS 9/3/02

update the reference in suggested quantities.

*********************************************************************************/
void CUnitCost::UpdateDimensions(const Boolean removeItem)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

if (mSuggestDimension)
{
DBClass dimensionClass = mCalcDimension ? id_CalcDimension : id_Dimension;
DB_PersistentObject *dimension = gDBFile->GetOneObject(dimensionClass, mSuggestDimension);

if (dimension)
{
DB_ObjectWatcher watcher (dimension);
dimension->AddToDependentArray(GetDBClassID(), GetDBID(), removeItem);
}
else
ReportMissingObject(dimensionClass, mSuggestDimension);
}

PostUseCount(id_Category, mCategory, removeItem); // TCS 11/21/03
PostUseCount(id_Category, mSubcategory, removeItem);
PostUseCount(id_CategorySystem, mCatSystem, removeItem);
PostUseCount(id_MarkupSystem, mMarkup, removeItem);
PostUseCount(id_UnitSize, mUnitSize, removeItem);
PostUseCount(id_ItemDiscount, mDiscount, removeItem);
PostUseCount(id_SubcontractType, mSubcontractType, removeItem);

if (mCalcDimension)
PostUseCount(id_CalcDimension, mSuggestDimension, removeItem);
else
PostUseCount(id_Dimension, mSuggestDimension, removeItem);
}
/*********************************************************************************

UpdateAllPrices (static) 8/3/01 moved 6/6/03

If we've just changed a markup system, we need to ask and possibly update
all cost items and assemblies that use this markup

*********************************************************************************/
void CUnitCost::UpdateAllUnitCosts(const DBid markupID)
{
#if TCS_MULTIUSER
if (gIsClient)
{
// if this is a client, we pass it along to the server TCS 6/6/03
CTCS_NetworkMessage ioMessage(msg_UpdateUnitCosts);
ioMessage.SetRecordID(markupID);

gApplication->BroadcastNetworkMessage(&ioMessage, cGiveWarning);

// no need to wait for a reply

return;
}
#endif
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
UInt8 saveResults;
DBid id;
CProgressBar *progressDialog = CProgressBar::CreateBar(TCS_GetStockString(stockID_UpdatingCostItems),
DLOG_ProgressWithStop);

if (progressDialog)
{
progressDialog->SetValue(0);
progressDialog->SetWindowTitle(TCS_GetStockString(stockID_UpdatingCostItems));
progressDialog->SetTextStep(5);
progressDialog->SetActionType(cEmptyString);
progressDialog->Refresh();
}

// set a watcher on the progress dialog so we don't leave it dangling TCS 8/4/01
CProgressBarWatcher watcher(progressDialog);

// clear out the memory cache, since we'll be loading many
// objects into memory and might as well have space for them TCS 12/5/02
gDBFile->SaveAllAndPurge();

// first, recalculate all cost items that use this
// markup system. We use extra braces to dump the objects quickly
{
TObjectIDArray costItemArray = DB_ListManager::GetIDArray(id_CostItem);
TObjectIDArrayIterator iterator(costItemArray);
CCostItem *costItem;

if (progressDialog)
progressDialog->SetMaxValue(costItemArray.GetCount());

while (iterator.Next(id))
{
if (progressDialog)
{
if (progressDialog->Increment() != progress_continue) // TCS 9/25/02
break;
}

costItem = TCS_SAFE_CAST(gDBFile->GetOneObject(id_CostItem, id),
CCostItem);

if (costItem)
{
saveResults = costItem->PrepareViewerDisplay();

DB_ObjectWatcher watcher(costItem);
costItem->RecalcNetPrices(markupID);

costItem->UpdateViewerDisplay(saveResults);
}

if (iterator.GetCurrentIndex() % 100 == 90) // TCS 9/25/02
gDBFile->SaveAllAndPurge();
}
}

// clear out the memory cache, since we'll be loading many
// objects into memory and might as well have space for them TCS 12/5/02
gDBFile->SaveAllAndPurge();

// now we update all assemblies, whether or not they use this markup.
// it will usually be faster to do that, rather than have the cost items
// update their dependents, since the latter may recalc more than once.
TObjectIDArray assemblyArray = DB_ListManager::GetIDArray(id_Assembly);
TObjectIDArrayIterator iterator(assemblyArray);
CAssembly *assembly;

if (progressDialog)
{
progressDialog->SetValue(0);
progressDialog->SetMaxValue(assemblyArray.GetCount()); // TCS 9/25/02
progressDialog->SetWindowTitle(TCS_GetStockString(stockID_UpdatingAssemblies));
progressDialog->SetMessage(TCS_GetStockString(stockID_UpdatingAssemblies));
progressDialog->Refresh();
}

while (iterator.Next(id))
{
if (progressDialog)
{
if (progressDialog->Increment() != progress_continue) // TCS 9/25/02
break;
}

assembly = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Assembly, id),
CAssembly);

if (assembly)
{
DB_ObjectWatcher assmWatcher(assembly);

saveResults = assembly->PrepareViewerDisplay();

assembly->RecalcPrice();
assembly->RecalcNetPrices();

assembly->UpdateViewerDisplay(saveResults);
}

if (iterator.GetCurrentIndex() % 100 == 90) // TCS 9/25/02
gDBFile->SaveAllChanges();
}
}
/*********************************************************************************

CleanUp TCS 1/9/03

clean up this object. We'll check our arrays for valid objects, and
remove any dud items.

*********************************************************************************/
SInt32 CUnitCost::CleanUp(const Boolean giveWarning)
{
TCS_ASSERTMsg(!IsInDatabase(), "Oops, CUnitCost::CleanUp called while in the database!");
SInt32 fixCount = 0;

TCS_TRY
{
fixCount = THE_SUPERCLASS::CleanUp();

fixCount += CheckValidArray(mDependentArray, id_SubAssembly, giveWarning);
fixCount += CheckValidArray(mEstimateArray, id_Estimate, giveWarning);
fixCount += CheckValidArray(mSaleArray, id_Sale, giveWarning);
fixCount += CheckValidArray(mInventoryUsedArray, id_InventoryUsed, giveWarning);
fixCount += CheckValidInfoArray(mTimeSpentArray, giveWarning);
fixCount += CheckValidInfoArray(mTransferArray, giveWarning);
}
TCS_CATCH {}

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

GetNumContacts TCS 1/9/03

return the number of records in the given array

*********************************************************************************/
SInt32 CUnitCost::GetNumContacts(const DBClass inClass) const
{
switch (inClass)
{
case id_Estimate:
return mEstimateArray.GetCount();
break;

case id_Sale:
return mSaleArray.GetCount();
break;

case id_InventoryTransfer:
return GetTransferCount(id_InventoryTransfer);
break;

default:
return 0;
break;
}
}
/*********************************************************************************

GetTransferCount TCS 1/9/03

return the number of records in the given array

*********************************************************************************/
SInt32 CUnitCost::GetTransferCount(const DBClass inClass) const
{
if (!inClass)
return mTransferArray.GetCount();

TObjectInfoArrayIterator iterator (mTransferArray);
SObjectInfo info;
SInt32 counter = 0;

while (iterator.Next(info))
{
if (info.classID == inClass)
counter++;
}

return counter;
}
/*********************************************************************************

GetInventoryValue TCS 1/7/01 rev 11/15/01

return the inventory value of this item for the given calc method

*********************************************************************************/
CMoney CUnitCost::GetInventoryValue(const UInt8 valuationType) const
{
switch (valuationType)
{
case calc_inventoryprice:
return mInventoryPrice;
break;

case calc_purchaseprice:
return mPurchasePrice;
break;

case calc_resaleprice:
return mResalePrice;
break;

case calc_reducedprice:
return mReducedPrice;
break;

case calc_projectprice:
return mProjectPrice;
break;

case calc_componentprice:
return mComponentPrice;
break;

default:
TCS_DebugAlert("Oops, bad case in CUnitCost::GetInventoryValue!");
return 0;
break;
}
}
/*********************************************************************************

GetEstimateCost TCS 12/26/01 rev 4/10/04

fill in unit cost and total cost. For a cost item it's simply the project cost.
Formerly called GetAdjustedUnitCosts.

*********************************************************************************/
Boolean CUnitCost::GetEstimateCost(DB_PersistentObject */*source*/, const UInt8 /*costArea*/,
const CMoney &quantity, CMoney &unitCost, CMoney &totalCost,
CMoney &hoursPerUnit, CMoney &totalHours)
{
unitCost = GetProjectPrice();
totalCost = unitCost * quantity;

hoursPerUnit = GetLaborHours(); // TCS 3/31/03
totalHours = hoursPerUnit * quantity;

return false; // doesn't have fixed costs
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

GetTransferArray TCS 1/9/03

fill in an array of transfers of the given class.

*********************************************************************************/
TObjectIDArray CUnitCost::GetTransferArray(const UInt8 inClass, const Boolean getOwners) const
{
TObjectIDArray outArray;
TObjectInfoArrayIterator iterator (mTransferArray);
SObjectInfo info;

while (iterator.Next(info))
{
if (info.classID == inClass)
outArray.Append(info.itemID);
}

if (getOwners)
return GetOwnerArray(inClass, outArray);
else
return outArray;
}
/*********************************************************************************

GetDependentArray TCS rev 1/9/03

fill in an array of dependent items.

*********************************************************************************/
TObjectIDArray CUnitCost::GetDependentArray(const Boolean getOwners) const
{
if (getOwners)
return GetOwnerArray(id_SubAssembly, mDependentArray);
else
return mDependentArray;
}
/*********************************************************************************

GetEstimateArray TCS rev 1/9/03

fill in an array of estimate items.

*********************************************************************************/
TObjectIDArray CUnitCost::GetEstimateArray(const Boolean getOwners) const
{
if (getOwners)
return GetOwnerArray(id_EstItemBreakdownEntry, mEstimateArray);
else
return mEstimateArray;
}
/*********************************************************************************

GetSaleArray TCS rev 1/9/03

fill in an array of sale items.

*********************************************************************************/
TObjectIDArray CUnitCost::GetSaleArray(const Boolean getOwners) const
{
if (getOwners)
return GetOwnerArray(id_SaleItemBreakdownEntry, mSaleArray);
else
return mSaleArray;
}
/*********************************************************************************

GetCustomerArray TCS 2/20/04

fill in an array of customers who have purchased this item.

*********************************************************************************/
TObjectIDArray CUnitCost::GetCustomerArray() const
{
TObjectIDArray outArray;
outArray.SetKeepSorted(true);

// fetch an array of sales, and get the customers for each
TObjectIDArray saleArray = GetSaleArray(true);
TObjectIDArrayIterator iter (saleArray);
DBid saleID, customerID;

while (iter.Next(saleID))
{
CSale *sale = TCS_SAFE_CAST(gDBFile->GetOneObject(id_Sale, saleID), CSale);

if (sale)
{
DB_ObjectWatcher watcher (sale);

if (!sale->HasVoidStatus())
{
customerID = sale->GetMainAccountID();

if (customerID && !outArray.ContainsItem(customerID))
outArray.Append(customerID);
}
}
}

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

RecalcNetPrices

recalculate the net prices of this cost item, from the current purchase
price and various markups.

This method is called with a markupID

*********************************************************************************/
Boolean CUnitCost::RecalcNetPrices(const DBid markupID)
{
// if a markupID is passed, the recalc will be done only if this item uses
// that markup system rev TCS 8/2/01
if (markupID && (markupID != mMarkup))
return false;

TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

Boolean hasMarkup = false;
UInt8 markupType;

// the markup system will calculate the latest pricing
if (mMarkup)
{
CMarkupSystem *markup =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_MarkupSystem, mMarkup), CMarkupSystem);

if (markup)
{ // there is a markup method in effect rev TCS 1/27/00
DB_ObjectWatcher watcher(markup);

markupType = markup->GetMarkupType(tag_resalemarkup); // rev TCS 3/11/02
if (markupType != markup_manual && markupType != markup_notavailable)
mResalePrice = markup->GetMarkedUpPrice(mPurchasePrice, tag_resalemarkup);

markupType = markup->GetMarkupType(tag_reducedmarkup);
if (markupType != markup_manual && markupType != markup_notavailable)
mReducedPrice = markup->GetMarkedUpPrice(mPurchasePrice, tag_reducedmarkup);

markupType = markup->GetMarkupType(tag_projectmarkup);
if (markupType != markup_manual && markupType != markup_notavailable)
mProjectPrice = markup->GetMarkedUpPrice(mPurchasePrice, tag_projectmarkup);

markupType = markup->GetMarkupType(tag_componentmarkup);
if (markupType != markup_manual && markupType != markup_notavailable)
mComponentPrice = markup->GetMarkedUpPrice(mPurchasePrice, tag_componentmarkup);

markupType = markup->GetMarkupType(tag_inventorymarkup);
if (markupType != markup_manual && markupType != markup_notavailable)
mInventoryPrice = markup->GetMarkedUpPrice(mPurchasePrice, tag_inventorymarkup);

hasMarkup = true;
}
else
{
ReportMissingObject(id_MarkupSystem, mMarkup); // TCS 6/4/01
return false;
}
}

if (!hasMarkup) // if no markup system, all prices are the same as purchase
{
mProjectPrice = mPurchasePrice;
mResalePrice = mPurchasePrice;
mComponentPrice = mPurchasePrice;
mReducedPrice = mPurchasePrice;
mInventoryPrice = mPurchasePrice;
}

// update the modification date TCS 4/26/99
UpdateModDate();

// flag us as dirty so changes are saved TCS 3/20/99
MakeDirty();

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

AddToDependentArray

add an item to our array of subassemblies that use this item.

*********************************************************************************/
Boolean CUnitCost::AddToDependentArray(const DBClass /*inClass*/, const DBid inID,
const Boolean removeIt)
{
if (!IsPosting()) // rev TCS 12/27/00
{
if (AddToObjectIDArray(mDependentArray, inID, removeIt))
{
UpdateUseCount(removeIt); // rev TCS 10/1/02
return true;
}
else
return false;
}
else
return true;
}/*********************************************************************************

RemoveFromDatabase

remove this object from the db. We need to override here because this
cost item may be included in some assemblies, in which case we need
to update those assemblies also

TCS removed this 12/27/00. We no longer allow deletion of cost items that
are included in assemblies, so this method is moot. If it is going to be allowed,
a lot more adjusting needs to be done in the affected assemblies.

*********************************************************************************/
/*Boolean CUnitCost::RemoveFromDatabase()
{
// sanity check
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));
// first let's get a list of those subassemblies which
// contain this cost item
CCursorSpinner spinner;
if (mSubassemblyArray.GetCount() > 0)
{ // some subassemblies are linked to this item, we need to
// get rid of them. Let's loop through the available
// subassemblies
TObjectIDArrayIterator iterator(mSubassemblyArray);
DBid subID;
CSubAssembly *sub = nil;
while (iterator.Next(subID))
{ // get the subassembly
sub = TCS_SAFE_CAST(gDBFile->GetOneObject(id_SubAssembly, subID),
CSubAssembly);
TCS_FailNILMsg(sub, TCS_GetErrString(errID_BadAssembly));
DB_ObjectWatcher subWatcher(sub);
// remove the sub from the db. We don't care about
// the return value here
sub->RemoveFromDatabase();
++spinner;
}
}
// finally, let the inherited method do the work
// of removing us from the db
return THE_SUPERCLASS::RemoveFromDatabase();
}*/
/*********************************************************************************

GetDiscountForCustomer BD 6/13/00

Given a customer discount ID, this method gets the item discount and returns
the percent value
*********************************************************************************/
CMoney CUnitCost::GetDiscountForCustomer(const DBid custDiscountID) const
{
CCustomerDiscount *dest =
TCS_SAFE_CAST(gDBFile->GetOneObject(id_CustomerDiscount, custDiscountID),
CCustomerDiscount);
if (dest)
{
DB_ObjectWatcher watcher(dest);
CMoney discountAmount = dest->GetDiscountValue(mDiscount);
return discountAmount;
}
else
return 0;
}
#if CAN_USE_MARK
#pragma mark -
#endif
/*********************************************************************************

AdjustInventory TCS 1/6/01

adjust the inventory quantity
*********************************************************************************/
void CUnitCost::AdjustInventory(const CMoney &changeAmount)
{
UInt8 results = PrepareViewerDisplay();

mInventoryQty += changeAmount;
MakeDirty();

// adjust the display
UpdateViewerDisplay(results);
}
/*********************************************************************************

HandleInventoryTransfer TCS 1/7/01

deal with an inventory transfer that includes this item, either in a breakdown
or in the cost item field.

This is called when inventory transfers are posted. It's also called when
purchases, sales and inventory use transactions are posted, if they contain
item breakdowns
*********************************************************************************/
void CUnitCost::HandleInventoryTransfer(const DBid transferID,
const UInt8 transferType, const CMoney &quantity,
const CMoney &value, const DBid mainAccount,
const DBid secondAccount, const CDate date,
const Boolean removeItem)
{
if (!mHasInventory)
return;

// store a reference to the transfer. Note that
// CCostItemBreakdownEntry::UpdateCostItem handles array
// updating for sales, purchases and inventory used rev TCS 1/10/02
if (transferType != inventory_used && transferType != inventory_sale &&
transferType != inventory_purchase)
{
AddToTransferArray(id_InventoryTransfer, transferID, transferType, removeItem);
}

DB_Account *inventoryAccount;

UInt8 sourceClass = DB_ClassDescriptor::GetCostAreaClass(transferType);

// initialize an inventory struct rev TCS 1/8/02
SInventoryInfo info;
info.itemClass = GetDBClassID();
info.itemID = GetDBID();
info.sourceClass = sourceClass;
info.sourceID = transferID;
info.currentQuantity = quantity;
info.startQuantity = quantity;
info.currentValue = value;
info.startValue = value;
info.dateAdded = date;

switch (transferType)
{
case inventory_purchase: // TCS 1/10/02
// increase the count here
if (removeItem)
AdjustInventory(-quantity);
else
AdjustInventory(quantity);

// add inventory value to the source account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
mainAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(transferType, info, removeItem);
}
break;

case inventory_sale: // TCS 1/10/02
case inventory_used:
case inventory_shrinkage:
// reduce the count here
if (removeItem)
AdjustInventory(quantity);
else
AdjustInventory(-quantity);

// subtract inventory value from the source account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
mainAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(transferType, info, removeItem);
}
break;

case inventory_transfer:
{ // no change of count here

// subtract inventory value from the source account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
mainAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(inventory_transferout, info, removeItem);
}
// add inventory value to the destination account
DB_Account *destAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
secondAccount), DB_Account);
if (destAccount)
{
DB_ObjectWatcher watcher(destAccount);
destAccount->HandleInventoryChange(inventory_transferin, info, removeItem);
}
}
break;

case inventory_revalue:
// no change of count here

// adjust inventory value in the source account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
mainAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(inventory_revalue, info, removeItem);
}
break;

case inventory_manufacture:
// increase the count here
if (removeItem)
AdjustInventory(-quantity);
else
AdjustInventory(quantity);

// add inventory value to the target account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
secondAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(inventory_manufacture, info, removeItem);
}

// adjust quantities of any components
HandleItemsManufactured(quantity, mainAccount, false, removeItem);
break;

case inventory_startup:
// increase the startup count
if (removeItem)
AdjustInventory(-quantity);
else
AdjustInventory(quantity);

// add inventory value to the target account
inventoryAccount = TCS_SAFE_CAST(gDBFile->GetOneObject(id_InventoryAccount,
secondAccount), DB_Account);
if (inventoryAccount)
{
DB_ObjectWatcher watcher(inventoryAccount);
inventoryAccount->HandleInventoryChange(inventory_startup, info, removeItem);
}
break;

default:
TCS_DebugAlert("Oops, bad case in CUnitCost::HandleInventoryTransfer!");
break;
}
}
/*********************************************************************************

FillContractSpecs TCS 5/2/02

fill in specs for a contract.
*********************************************************************************/
void CUnitCost::FillContractSpecs(CTextOutputStream &stream, const CMoney &quantity,
const CMoney &cost, const CMoney &modifier)
{
TCS_FailNILMsg(gDBFile, TCS_GetErrString(errID_BadFile));

CContractPreferences *prefs =
TCS_SAFE_CAST(gDBFile->GetFirstObject(id_ContractPrefs),
CContractPreferences);
TCS_FailNILMsg(prefs, TCS_GetErrString(errID_BadPrefs));
DB_ObjectWatcher watcher(prefs);

CTextString unitSizeString;

DB_PersistentObject *unitSize = gDBFile->GetOneObject(id_UnitSize, mUnitSize);

if (unitSize)
{
DB_ObjectWatcher sizeWatcher (unitSize);
unitSizeString = unitSize->GetName();
}

CTextString specsText = prefs->DoSpecSubstitutions(mContractText, quantity,
mContractQuantDisplay, cost, mContractCostDisplay,
unitSizeString, modifier);

stream.WriteString(specsText);
stream.WriteEndOfLine();
}
/*********************************************************************************

FillDataReport TCS 9/6/02

fill in a diagnostic table that shows data field values.

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

if (version > 1) // TCS 3/6/04
{
// write the custom field count
const UInt8 numCustomFields = mCustomFieldData.GetCount();
FillFieldStockRow(table, stream, stockID_CustomFieldCount, cCharSize, SInt32(numCustomFields));

// write values of any custom fields
if (numCustomFields)
{
CTextString valueString, fieldName;
CCustomDataHolder *holder = nil;
TCustomDataArrayIterator iterator(mCustomFieldData);
while (iterator.Next(holder))
{
TCS_FailNILMsg(holder, TCS_GetErrString(errID_BadCustomField));
valueString = holder->GetCString();
fieldName = DB_ObjectClassInfo::GetCustomFieldName(GetDBClassID(), iterator.GetCurrentIndex());
fieldName.AppendWithParentheses(TCS_GetStockString(stockID_Custom));

FillFieldStringRow(table, stream, fieldName, valueString);
}
}
}

FillFieldArrayRow(table, stream, "mDependentArray", mDependentArray);
FillFieldArrayRow(table, stream, "mEstimateArray", mEstimateArray);
FillFieldArrayRow(table, stream, "mSaleArray", mSaleArray);
FillFieldArrayRow(table, stream, "mTimeSpentArray", mTimeSpentArray);
FillFieldArrayRow(table, stream, "mTransferArray", mTransferArray);
FillFieldArrayRow(table, stream, "mInventoryUsedArray", mInventoryUsedArray);

FillFieldStringRow(table, stream, tag_name, mFullName);
FillFieldStringRow(table, stream, tag_contracttext, mContractText, true);

if (version > 4)
FillFieldStringRow(table, stream, tag_upccode, mUPCCode);

FillFieldTagRow(table, stream, tag_purchaseprice, cMoneySize, mPurchasePrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_projectprice, cMoneySize, mProjectPrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_resaleprice, cMoneySize, mResalePrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_reducedprice, cMoneySize, mReducedPrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_componentprice, cMoneySize, mComponentPrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_inventoryprice, cMoneySize, mInventoryPrice.GetCurrencyString());
FillFieldTagRow(table, stream, tag_weight, cMoneySize, mWeight.GetNumberString()); // TCS 10/27/02

FillFieldObjectIDRow(table, stream, tag_category, mCategory, id_Category);
FillFieldObjectIDRow(table, stream, tag_subcategory, mSubcategory, id_Category);
FillFieldObjectIDRow(table, stream, tag_catsystem, mCatSystem, id_CategorySystem);
FillFieldObjectIDRow(table, stream, tag_markup, mMarkup, id_MarkupSystem);
FillFieldObjectIDRow(table, stream, tag_unitsize, mUnitSize, id_UnitSize);
FillFieldObjectIDRow(table, stream, tag_discount, mDiscount, id_ItemDiscount);
FillFieldObjectIDRow(table, stream, tag_suggestedqty, mSuggestDimension, id_Dimension);
FillFieldObjectIDRow(table, stream, tag_subcontracttype, mSubcontractType, id_SubcontractType);

FillFieldTagRow(table, stream, tag_reorderamount, cLongSize, mReorderAmount);

FillFieldEnumRow(table, stream, tag_status, mStatus, MENU_CostItemStatus);
FillFieldEnumRow(table, stream, tag_contractcostdisplay, mContractCostDisplay, MENU_ContractNumberDisplay);
FillFieldEnumRow(table, stream, tag_contractquantdisplay, mContractQuantDisplay, MENU_ContractNumberDisplay);

FillFieldBitRow(table, stream, tag_taxsale, mTaxSale, true);
FillFieldBitRow(table, stream, "mTaxPurchase", mTaxPurchase);
FillFieldBitRow(table, stream, tag_hasinventory, mHasInventory);
FillFieldBitRow(table, stream, "mRestockingCharge", mRestockingCharge);
FillFieldBitRow(table, stream, tag_calcdimension, mCalcDimension);
if (GetDBClassID() == id_Assembly)
FillFieldBitRow(table, stream, tag_canmanufacture, mCanManufacture);
else
FillFieldBitRow(table, stream, TCS_GetStockString(stockID_Unused), mCanManufacture);
FillFieldBitRow(table, stream, TCS_GetStockString(stockID_Unused), mContractAsList);
FillFieldBitRow(table, stream, tag_alwaysshow, mAlwaysShow);

FillFieldBitRow(table, stream, tag_printflagged, mPrintFlagged, true);
FillFieldBitRow(table, stream, tag_flagged, mFlagged);

if (GetDBClassID() == id_Assembly)
{
FillFieldBitRow(table, stream, tag_calclabormod, mCalcLaborModifier);
FillFieldBitRow(table, stream, tag_calcmaterialmod, mCalcMaterialModifier);
}
else
{
FillFieldBitRow(table, stream, TCS_GetStockString(stockID_Unused), mCalcLaborModifier);
FillFieldBitRow(table, stream, TCS_GetStockString(stockID_Unused), mCalcMaterialModifier);
}
FillFieldBitRow(table, stream, tag_includeinstarter, mIncludeInStarterFile); // TCS 12/5/02
FillFieldBitRow(table, stream, tag_allowvariable, mAllowVariablePrices);
FillFieldStockRow(table, stream, stockID_Padding, -2, SInt32(mUCFiller));
FillFieldStockRow(table, stream, stockID_Padding, cCharSize, SInt32(mUCPadding));

FillFieldTagRow(table, stream, tag_picture, cLongSize, mPicture);

FillFieldTagRow(table, stream, tag_inventoryqty, cMoneySize, mInventoryQty.GetNumberString());
FillFieldTagRow(table, stream, tag_itemcode, cMoneySize, mItemCode.GetNumberString());

FillFieldTagRow(table, stream, tag_creationdate, cDateSize, mCreationDate.GetCString());
FillFieldTagRow(table, stream, tag_moddate, cDateSize, mModDate.GetCString());
}