{*************************************************************************
 ***                                                                   ***
 ***                     OBSOLETE JIMMYS                               ***
 ***                                                                   ***
 *************************************************************************}
{$I compflgs}

{NB OldContact record in TOldVerCompany.Load is almost certainly wrong...}

{OLD OBJECTS - For use with the update program.  Old Objects no longer
used but that may exist in older systems}

{When an update, involving a complete jimmy file read & write, is done then
all the old code for old jimmy data versions is no longer required.  So
such objects are descended here, and somehow we have to reregister the
object sr type....}
unit UOldObjs;

INTERFACE

uses kdirctry, inpfname, dattime, notes, scodes, kcompany,
			objects, smoney, kperson, jimmys, files;


type
	POldVerCompany = ^TOldVerCompany;
	TOldVerCompany = object(TCompany)
		AddressLine : array[1..5] of string[35];
		Postcode : string[9];
		Country : TSCode;
		email : string[25];
		Contact : array[1..4] of longint;
		Comment : PFreeTextData;
		function RecSize : Word; virtual;
		constructor Load(var S : TDataStream);
		destructor Done; virtual;
	end;


type
	POldVerPerson = ^TOldVerPerson;
	TOldVerPerson = object(TPerson)
		AddressLine : array[1..7] of string;
		Postcode : string;
		Country : TSCode;
		email : string;
		Comment : PFreeTextData;
		constructor Load(var S : TDataStream);
		function RecSize : Word; virtual;
		destructor Done; virtual;
	end;

type
	PAltAddress = ^TAltAddress;
	TAltAddress = object(TJimmy)

		WhoFor : longint;

		DateRange : TDateRange;

		apType 		: word;

		AddressLine   : array[1..7] of string[35];
		Postcode  : string[9];
		Country   : TScode;

		TelNo     : array[1..2] of string[25];

{		constructor Init(const Param : Pointer);{}

		constructor Load(var S : TDataStream);
		procedure   Store(var S : TDataSTream);

		function RecSize : word; virtual;
		function srType : word; virtual;
 end;

{============= OLD v1-4 SPLIT LETTER TYPES ==================}
{const
	Tv4LetterSize = 50;{}

	{--- Base type ----}
	Pv4Letter = ^Tv4Letter;
	Tv4Letter = object(TJimmy)

		Date      : TDate;                 {Date of writing}
		ToWho     : longint;               {Pointer to who it is to}
		ReWho     : longint;
		ByWho     : longint;               {Who typed it}
		Codes     : string[12];            {Description codes}
		Ref       : string[6];             {Any old reference string}
		Header    : string[8];             {Header form name}
		Copies    : byte;                  {No. copies}

		Ext 			 : string;
		PAth      : string;
		FileEditor : TFileEditorProc;
		DescendantsrType    : word;

{		constructor Init(Params : pointer);{}
		destructor Done; virtual;
		procedure CommonInit; virtual;

		constructor Load(var S : TDataStream);
	end;

	{--- std letter base type ----}
	PStdLetter = ^TStdLetter;
	TStdLetter = object(Tv4Letter)

		{Uses header form as form name}

		procedure CommonInit; virtual;

		constructor  Load(var S : TDataStream);
		procedure Store(var S : TDataStream);
		function RecSize : word; virtual;
	end;

	{--- Editor letter ---}
	PEDLtr = ^TEDLtr;
	TEDLtr = object(Tv4Letter)

		Body      : PLetterData;
		Width     : longint;  {width of line}

		procedure CommonInit; virtual;
		destructor Done; virtual;

		constructor Load(var S : TDataStream);
		procedure   Store(var S : TDataSTream);
		function RecSize : word;         virtual;
		function srType : word; virtual;

	end;

	{--- std editor letter ---}
	PStdEdLetter = ^TStdEdLetter;
	TStdEdLetter = object(TStdLetter)
		function srType : word; virtual;
	end;

	{--- wordperfect letter ----}
	PWPLtr = ^TWPLtr;
	TWPLtr = object(Tv4Letter)

		FirstTextRec : longint;             {Ptr to first in chain of text blocks}

		Loaded : boolean;                   {Marker for whether text is loaded}

		WPVer : byte;                        {version of WP used at the time of storage}
																			 {50 = 5.0, 51 = 5.1, 60 = 6.0 etc}
{		constructor Init(P : pointer);{}
		procedure CommonInit; virtual;

		constructor Load(var S : TDataStream);
		procedure   Store(var S : TDataSTream);
		function RecSize : word; virtual;
		function srType : word; virtual;

		procedure   LoadText(InsBegin4Edit : boolean);
	end;

	{--- std wp letter ---}
	PStdWPLetter = ^TStdWPLetter;
	TStdWPLetter = object(TStdLetter)
		procedure CommonInit; virtual;

		function srType : word; virtual;
	end;

	{============== v4EstimateS/INVOICES =================}
{const
	TNodeItemSize = 20;{}

	PNodeItem = ^TNodeItem;
	TNodeItem = object(TObject)

		RecNo : longint; {not stored - points to self's ID for nodeviews use}

		Disk : record
			Next : longint;
			Prev : longint; {probably not used much - except for deletions - but missing parent poitner for that too...}
			FirstChild : longint;
		end;
		Heap : record
			Next : PNodeItem;
			Prev : PNodeItem;
			FirstChild : PNodeItem;
			Parent : PNodeItem;
		end;

		Expanded : boolean;

		constructor Init;
		procedure CommonInit; virtual;
		destructor Done; virtual;

		constructor Load(Var S : TStream);
		procedure Store(Var S : TStream);
	end;

	TNodeTreeForEachAction = procedure(Node : PNodeItem);


	{modified to specifically load invoice nodes}
	PNodeTree = ^TNodeTree;
	TNodeTree = object
		RootNodeID : longint;
		RootNode : PNOdeItem;
		DummyRoot : PNodeItem; {often useful to have a single head to a tree (with just children)
														let owning object set as it may be special (eg invoicenode)}
		Loaded : boolean;
{		fiType : word;{}

		constructor Init{(NfiType : word){};
		destructor Done;

		procedure Load(Var S : TStream); {loads just rootnode bit}

		procedure SetDummyRoot(NDUmmyRoot : PNodeItem); {for owner to set}

		procedure LoadTree;

		procedure ForEach(Action : TNodeTreeForEachAction); {expects a procedure of type Name(Node : PNodeItem); far;}
	end;

	Pv4Estimate = ^Tv4Estimate;
	Tv4Estimate = object(TJimmy)

		Ref				: string[5];
		FromRef 	: string[5];

		ToWho    : longint;
		ReWho    : longint;

		ItemTree : TNodeTree;{}

		Comment   : PFreeTextData;            {Text on estimate}

		LastPrint : TDate;

		{Automatic fields - also appearing on edit box - some stored}
		Total     	: TSimpleMoney;                {Total excl VAT}
		DiscountedTotal : TSimpleMoney;							{discounted total excl VAT}
		VAT       	: TSimpleMoney;                {VAT on amount}

		procedure   CommonInit; virtual;    {Extra initialisation, shared between init & load}
		destructor Done; virtual;

		constructor Load(var S : TDataStream);
		function RecSize : word; virtual;
		function srType : word; virtual;

		procedure Calculate; virtual;{}
	end;

	Pv4Invoice = ^Tv4Invoice;
	Tv4Invoice = object(Tv4Estimate)

		DaysDiscount : byte; {days to end}
		SentDate	: TDate;

		Due			   	: TSimpleMoney;

		Paid				: TSimpleMoney;
		TotalincVAT	: TSimpleMoney;

		WrittenOff: boolean;              {Given up marker}

		OverdueLtr : array[1..3] of boolean;							{Bit markers for overdue letters sent}

		LastPaymentDate : TDate;

		constructor Load(var S : TDataStream);
		function RecSize : word; virtual;
		function srType : word; virtual;

		procedure CalculateTotals; virtual;{}
	end;

{	PPaymentNode = ^TPaymentNode;
	TPaymentNode = object(TInvoiceNode)

		Date : TDate;
		From : longint; {who paid?}

		{Fields used for input only - cos we need to arrange date and from before
		amuont on screen, but amount already available in TInvoiceNode}
{		InputTotal : TMoney; {Just used for input}
{		InputNomCat : TSCode;

		procedure CalculateNodeTotal; virtual;

		constructor Load(var S : tDatastream);
		procedure Store(var S : TDataStream);
		function RecSize : word; virtual;
		function srType : word; virtual;
	end;{}

type
 Pv4Job = ^Tv4Job;
 Tv4Job = object(TJimmy)

		ForWho		: longint;
		CustRef		: string[20];
		ByWho			: longint;     {Engineer/subcontractor}
		OurRef		: string;  {Job ref No}
		CallDate	: TDate;
		VisitDate : TDate;
		VisitTime : string[15];

		ItemTree : TNodeTree;

		Notes     : PFreeTextData;
		Outcome   : TSCode;

		Invoiced  : boolean;

		{-- Methods --}
		procedure CommonInit; virtual;
		destructor Done; virtual;
		constructor Load(var S : TDataStream);

		function RecSize : word; virtual;
		function srType : word; virtual;
	end;

	Pv4Product = ^Tv4Product;
	Tv4Product = object(TJimmy)

		CategoryIdx : longint;
		Category2Idx : longint;
		CodeIdx : longint;

		Categories : string[11];
		Desc : string[20];
		Code : string[20];			{for catalogue code/etc reference}

		Units : array[0..3] of TSCode;

		Supplier : longint;
		Quoter    : string[20];  {name of quoter}
		QuoteDate : TDate;
		QuotedCost : array[0..3] of TSimpleMoney;

		PriceBand : array[1..3] of record
			Band : TSCode;
			Margin : integer;
			Price : array[0..3] of TSimpleMoney;
		end;

		NumInStock : array[0..3] of integer;       {Number in stock}
		OnOrder		: array[0..3] of integer;       {committed}
		Sold		 : array[0..3] of integer;       {committed}
		WarningLevel : array[0..3] of integer;			{Warning level for number left in stock}

		Location : string[11]; {Sentence code line}

		constructor Load(var S : TDataStream);
		function RecSize : word; virtual;
		function srType : word; virtual;
 end;



IMPLEMENTATION


uses minilib, global, lstrings, editfile, dos, kwplink, address, tuimsgs;

const
	{make sure this one is registered first, so that it puts the new one}
 ROldSCodeItem : TStreamRec = (
	 ObjType : OldsrSCode;
	 VmtLink : Ofs(TypeOf(TSCodeItem)^);
	 Load : @TSCodeItem.Load;
	 Store : nil
 );


{********************************************************
 ***                                                  ***
 ***           OLD COMPANY (v1-4 PRE-ADDRESSING)      ***
 ***                                                  ***
 ********************************************************}

const
	 {--- Required for Stream ----}
	 ROldVerCompany : TStreamRec = (
		 ObjType : srOldCompany;
		 VmtLink : Ofs(TypeOf(TOldVerCompany)^);
		 Load : @TOldVerCompany.Load;
		 Store : @TOldVerCompany.Store
	 );

function TOldVerCompany.RecSize : Word;
begin RecSize := 450 + 30; end;

destructor TOldVerCompany.Done;
begin
	dispose(Comment, done);
	inherited Done;
end;

constructor TOldVerCompany.Load;
var CategoryCode : TSCOde;
		Ver : byte;
		I : integer;
		OldContact : array[1..4] of record
			Desc : string[10];
			FilePtr : longint;
		end;
		OldComment : string;
		TempS : string;
		R : longint;

begin
	Ptr2Addresses := -1;
	Ptr2Ref := -1;
	Ptr2Inv := -1;

	New(Comment, init);
	for I := 1 to 5 do CategoryIdx[I] := -1;

	R := S.GetPos-2;

	S.Read(Ver, 1);
	case Ver of
		8: begin
			{15/12/93 Splitting of Company vs Person, multi-index, etc}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}

			CategoryIdx[1] := -1;;

			{Company name also stored in index}
			CompanyName  := S.ReadStr;
			Alias := S.ReadStr;{}
{DebugNote('UOLDOBJS','Reading Old Company ver 8 "'+CompanyName+'" #'+N2Str(R));{}

			S.Read(CategoryCode, sizeof(CategoryCode)); CategoryCode := delspace(CategoryCode);

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 5 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;

			email := '';

			for I := 1 to 4 do begin
				S.Read(OldContact, sizeof(OldContact));
				Contact[I] := OldCOntact[I].FilePtr;
			end;

			CategoryCodes := delspace(delspaceR(CategoryCode) + ' '+S.ReadStr);
			OldComment  := S.ReadStr; LSAppendStr(Comment^.Text, OldComment); Comment^.Loaded := True;
		end;
		9: begin
			{28/7/94 Added text comment}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}

			CategoryIdx[1] := -1;;

			{Company name also stored in index}
			CompanyName  := S.ReadStr;
			Alias := S.ReadStr;{}
{DebugNote('UOLDOBJS','Reading Old Company ver 9 "'+CompanyName+'" #'+N2Str(R));{}

			S.Read(CategoryCode, sizeof(CategoryCode)); CategoryCode := delspace(CategoryCode);

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 5 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;

			email := '';


			for I := 1 to 4 do begin
				S.Read(OldContact, sizeof(OldContact));
				Contact[I] := OldCOntact[I].FilePtr;
			end;

			CategoryCodes := delspace(delspaceR(CategoryCode) + ' '+S.ReadStr);
			Comment^.Load(S);
		end;
		10: begin
			{15/8/94 Removed description field from contact - leave user to put that
			in job title field, which then appears on inputdir line}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}

			CategoryIdx[1] := -1;;

			{Company name also stored in index}
			CompanyName  := S.ReadStr;
			Alias := S.ReadStr;{}
{DebugNote('UOLDOBJS','Reading Old Company ver 10 "'+CompanyName+'" #'+N2Str(R));{}

			S.Read(CategoryCode, sizeof(CategoryCode)); CategoryCode := delspace(CategoryCode);

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 5 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;
{DebugNote('UOLDOBJS  Reading tels of '+CompanyName,TelNo[1]+#13+TelNo[2]+#13+TelNo[3]);{}

			email := '';

			for I := 1 to 4 do S.Read(Contact[I], sizeof(Contact[I]));

			CategoryCodes := delspace(delspaceR(CategoryCode) + ' '+S.ReadStr);
			Comment^.Load(S);
		end;

		11: begin
			{17/7/95 added email address}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}


			CategoryIdx[1] := -1;;

			{Company name also stored in index}
			CompanyName  := S.ReadStr;
			Alias := S.ReadStr;{}
{DebugNote('UOLDOBJS','Reading Old Company ver 11 "'+CompanyName+'" #'+N2Str(R));{}

			S.Read(CategoryCode, 4); CategoryCode := delspace(CategoryCode);

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 5 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;

			email := S.ReadStr;

			for I := 1 to 4 do S.Read(Contact[I], sizeof(Contact[I]));

			CategoryCodes := delspace(delspaceR(CategoryCode) + ' '+S.ReadStr);
			Comment^.Load(S);
		end;
	else
		ProgramError('Loading Company'#13'Unrecognised ver '+N2Str(Ver));
	end;
end;


{********************************************************
 ***                                                  ***
 ***           v1-4 PERSON (PRE-ADDRESSING)           ***
 ***                                                  ***
 ********************************************************}

const
	 {--- Required for Stream ----}
	 ROldVerPerson : TStreamRec = (
		 ObjType : srOldPerson;
		 VmtLink : Ofs(TypeOf(TOldVerPerson)^);
		 Load : @TOldVerPerson.Load;
		 Store : @TOldVerPerson.Store
	 );

function TOldVerPerson.RecSize : Word;
begin RecSize := 500 + 30; end;


destructor TOldVerPerson.Done;
begin
	dispose(Comment, done);
	inherited Done;
end;

constructor TOldVerPerson.Load;
var CategoryCode : TSCode;
		Ver : byte;
		I : integer;
begin
	Ptr2Addresses := -1;
	S.Read(Ver, 1);
	New(Comment, init);
	for I := 1 to 5 do CategoryIdx[I] := -1;

	case Ver of
		9 : begin
			{23/7/94 Changed comment string to an inputString type (See inptext)}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}

			CategoryIdx[1] := -1;;

			{Surname/Forename also stored in index}
			Surname  := S.ReadStr;
			ForName  := S.ReadStr;
			Title    := S.ReadStr;
			DearName := S.ReadStr;
			S.Read(ContactFor, 4);
			S.Read(CategoryCode, 4); CategoryCode := delspaceR(CategoryCode); {was category}

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 7 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;

			email := '';
			for I := 1 to 4 do if pos('@',TelNo[I])>0 then email := TelNo[I];

			CategoryCodes := delspace(delspaceR(CategoryCode) + ' '+S.ReadStr);
			Comment^.Load(S);

			S.Read(Ptr2Inv, 4);
			S.Read(Ptr2Ref, 4);

		end;
		10 : begin
			{23/7/94 Changed comment string to an inputString type (See inptext)}
			{15/12/95 Combined category/search codes}

			CommonInit;

			S.Read(Dat2Idx, sizeof(dat2idx));
			S.Read(AliasIdx, sizeof(AliasIdx));
			S.Read(CategoryIdx[1], 4);
			S.Read(ArchiveIdx, sizeof(ArchiveIdx));

			DOReg.Load(S);

			S.Read(Ptr2History, 4);
			S.Read(Ptr2More, 4);
			S.Read(Lock,1);       {NB Lock position fixed - see Load/StoreLock below}
			S.Read(Deleted, sizeof(Deleted));

			RecNo := -1;  {not set yet}

			CategoryIdx[1] := -1;;

			{Surname/Forename also stored in index}
			Surname  := S.ReadStr;
			ForName  := S.ReadStr;
			Title    := S.ReadStr;
			DearName := S.ReadStr;
			S.Read(ContactFor, 4);
			S.Read(CategoryCode, 4); CategoryCode := delspaceR(CategoryCode); {was category}

			{and change category code to new code standards}
			if CategoryCode = '4' then CategoryCode := 'CUS';
			if CategoryCode = '5' then CategoryCode := 'PRO';
			if CategoryCode = '6' then CategoryCode := 'SUP';
			if CategoryCode = '7' then CategoryCode := 'STA';

			for I := 1 to 7 do AddressLine[I] := S.ReadStr;
			PostCode := S.ReadStr;
			S.Read(Country, sizeof(Country));

			for I := 1 to 4 do TelNo[I] := S.ReadStr;

			email := S.ReadStr;

			CategoryCodes := delspace(CategoryCode + ' '+S.ReadStr);
			Comment^.Load(S);

			S.Read(Ptr2Inv, 4);
			S.Read(Ptr2Ref, 4);

		end;

	else
		ProgramError('Loading Person'#13'Unrecognised ver '+N2Str(Ver));
	end;
end;


{********************************************************
 ***                                                  ***
 ***           v1-4 ALT ADDRESSES                     ***
 ***                                                  ***
 ********************************************************}
const
	{--- Required for Stream ----}
	RAltAddress : TStreamRec = (
		ObjType : srAltAddress;
		VmtLink : Ofs(TypeOf(TAltAddress)^);
		Load : @TAltAddress.Load;
		Store : @TAltAddress.Store
	);


{Just changed the srtype, still quite a few around, on my database anyway}
	{--- Required for Stream ----}
	Rv2AltAddress : TStreamRec = (
		ObjType : srv2AltAddress;
		VmtLink : Ofs(TypeOf(TAltAddress)^);
		Load : @TAltAddress.Load;
		Store : @TAltAddress.Store
	);{}



{constructor TAltAddress.Init;
begin
	inherited Init;
	if Param <> nil then WhoFor := Plongint(Param)^ else WhoFor := -1;
end;{}

function TAltAddress.RecSize : word;
begin RecSize := 380; end;

constructor TAltAddress.Load(var S : TDataStream);
var I : integer;
		Ver : byte;
		DearName, AddressName : string; {old and rubbish}

begin
	S.Read(Ver, 1);
	apType := apCorrespondence;

	case Ver of
	4 : begin
		{11/11/94 Checkbox adhtypes not radio button.}

		S.Read(WhoFor, 4);

		DateRange.Start.Load(S);
		DateRange.Finish.Load(S);

		S.Read(apType, 2);

		for I := 1 to 7 do AddressLine[I] := S.ReadStr;
		Postcode  := S.ReadStr;
		Country   := S.ReadStr;

    for I := 1 to 2 do TelNo[I]   := S.ReadStr;
  end;
	3 : begin

		S.Read(WhoFor, 4);

    DateRange.Start.Load(S);
    DateRange.Finish.Load(S);

		S.Read(apType, 2); apType := Exp2(apType); {convert from radio button format to checkbox bits}

		for I := 1 to 7 do AddressLine[I] := S.ReadStr;
    Postcode  := S.ReadStr;
		Country   := S.ReadStr;

    for I := 1 to 2 do TelNo[I]   := S.ReadStr;
  end;
  2 : begin

		S.Read(WhoFor, 4);

    DateRange.Start.Load(S);
    DateRange.Finish.Load(S);

		DearName  := S.ReadStr;
		AddressName := S.ReadStr;
		for I := 1 to 7 do AddressLine[I] := S.ReadStr;
    Postcode  := S.ReadStr;
		Country   := S.ReadStr;

		for I := 1 to 2 do TelNo[I]   := S.ReadStr;
  end;
  1 : begin

		S.Read(WhoFor, 4);

    DateRange.Start.Load(S);
		DateRange.Finish.Load(S);

		DearName  := S.ReadStr;
		AddressName := S.ReadStr;
		for I := 1 to 7 do AddressLine[I] := S.ReadStr;
		Postcode  := S.ReadStr;
    Country   := S.ReadStr;

    for I := 1 to 2 do TelNo[I]   := S.ReadStr;

	end else
		ProgramWarning('Version '+N2Str(Ver)+' not understood'#13'Alt Address Load');
	end;
end;

procedure TAltAddress.Store(var S : TDataStream);
begin end;

function TAltAddress.srtype;
begin srtype := srv2AltAddress; end;


{********************************************************
 ***                                                  ***
 ***           v1-4 LETTERS (SPLIT TYPES)             ***
 ***                                                  ***
 ********************************************************}

{********************************
 ***      ROOT LETTER         ***
 ********************************}
{constructor Tv4Letter.Init;
begin
	inherited Init;
	Date.SetToToday;
	ToWho := -1;      {Default's to who the letter is about}
{	ReWho := -1;      {Who the letter is about}
{	ByWho := -1;      {Ought to default to user}
{	Copies := 1;
	Header := 'LETTER';  {Default to Letter header}
	{EditorType := edNone;{}

{	if Params <> nil then ToWho := Plongint(Params)^;
end;

{Used by constructors Init and Load}
procedure Tv4Letter.CommonInit;
begin
	inherited CommonInit;
	SCodeAdmin[scLetters]^.LogOn;

	FileAdmin(fiJimmys)^.LogOn;

	{default to standard text type}
	Ext := 'HDR';
	Path := '';
	DescendantsrType := 0;
	FileEditor := EditTextFile;
end;

destructor Tv4Letter.Done;
begin
	SCodeAdmin[scLetters]^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;
	inherited Done;
end;


constructor Tv4Letter.Load(var S : TDataStream);
var Ver : byte;
begin
	S.Read(Ver, 1);

	case ver of
		1 : begin
			{This one would overflow if codes was too long}
			Date.Load(S);
			S.Read(ToWho, 4);
			S.Read(ByWho, 4);
			Codes := S.ReadStr;
			S.Read(Ref,   sizeof(Ref));
			S.Read(Header, sizeof(Header));
			S.Read(Copies, sizeof(Copies));
			S.Read(rewho, 4); {rubbish}
			S.Read(reWho, 4);
		end;
		2 : begin
			Date.Load(S);
			S.Read(ToWho, 4);
			S.Read(ByWho, 4);
			Codes := S.ReadFixedStr(12);
			Ref := S.ReadFixedStr(6);
			Header := S.ReadFixedStr(8);
			S.Read(Copies, sizeof(Copies));
			S.Read(rewho, 4); {rubbish}
			S.Read(ReWho, 4);
		end;
	end; {Case}

	Codes := DelSpace(Codes);
	CommonInit;  {Log on to JimmyStream & check codes are open}
end;

{********************************
 ***    STD ROOT LETTER       ***
 ********************************}
procedure TStdLetter.CommonInit;
begin
	inherited CommonInit;
	Ext := 'STL'; {override to provide standard forms for Header type}
	DescendantsrType := srStdEdLetter;
end;

constructor TStdLetter.Load(var S : TDAtaStream);
var Ver : byte;
begin
	S.Read(Ver, 1);
	if Ver =1 then begin
		{Ver1 was before stdletter was made a descendant of Tv4Letter, rather than a sister}
		CommonInit;
		Date.Load(S);
		S.Read(Header, sizeof(Header));
		S.Read(Copies, sizeof(Copies));
		S.Read(Towho, 4); {rubbish}
		S.Read(ToWho, 4);
		ReWho := ToWho;
		ByWho := -1;
	end else begin {the old load - as above - was ver 1.  The one below starts at ver 2 at this stage}
		S.Seek(S.GetPos-1);  {Wind back to read ver again}
		inherited Load(S);
	end;
end;

procedure TStdLetter.Store;
begin end;

function TStdLetter.Recsize;
begin RecSize := 50; end;

{********************************
 ***    EDITOR LETTER         ***
 ********************************}
const
	{--- Required for Stream ----}
	REdLtr : TStreamRec = (
		ObjType : srEdLtr;
		VmtLink : Ofs(TypeOf(TEdLtr)^);
		Load : @TEdLtr.Load;
		Store : @TEdLtr.Store
	);

{--- Inititalise - set ptrs to SC ---}
procedure TEdLtr.CommonInit;
begin
	inherited CommonInit;
	New(Body, init);
	DescendantsrType := srEdLtr;
end;

destructor TEDLtr.Done;
begin
	Dispose(body, done);
	inherited Done;
end;

constructor TEDLtr.Load(var S : TDataStream);
begin
	inherited Load(S);{}

	body^.Load(S);
end;

function TEDltr.RecSize;
begin RecSize := 60; end;

procedure TEDLtr.Store(var S : TDataStream);
begin end;

function TEDLtr.srtype;
begin srtype := srEDLtr; end;


{********************************
 ***    STD EDITOR LETTER     ***
 ********************************}

const
	{--- Required for Stream ----}
	RStdEdLetter : TStreamRec = (
		ObjType : srStdEdLetter;
		VmtLink : Ofs(TypeOf(TStdEdLetter)^);
		Load : @TStdEdLetter.Load;
		Store : @TStdEdLetter.Store
	);

function TStdEdLetter.srtype;
begin srtype := srStdEdLetter; end;


{********************************
 ***      WP LETTER           ***
 ********************************}

{--- Inititalise - set ptrs to SC ---}
{constructor TWPLtr.Init;
begin
	inherited Init(P);
	FirstTextRec := -1;
	WPVer := 0;
	Loaded := False;   {Will always need to load at least header}
{end;{}

procedure TWPLtr.CommonInit;
begin
	inherited CommonInit;
	Path := WPSetup.FormsPath;
	DescendantsrType := srWPLtr;
	FileEditor := EditWPFile;
end;


const
	{--- Required for Stream ----}
	RWPLtr : TStreamRec = (
		ObjType : srWPLtr;
		VmtLink : Ofs(TypeOf(TWPLtr)^);
		Load : @TWPLtr.Load;
		Store : @TWPLtr.Store
	);

constructor TWPLtr.Load(var S : TDataStream);
begin
	inherited Load(S);
	S.Read(FirstTextRec, 4);
	S.Read(WPVer, 1); if (WPVer<50) or (WPVer>60) then WPVer := 51; {default to 5.1}

	Loaded := False;
end;

procedure TWPLtr.Store(var S : TDataStream);
begin
end;

function TWPltr.RecSize;
begin RecSize := 55; end;

function TWPLtr.srtype;
begin srtype := srWPLtr; end;

{Convert text chain straight into WP letter}
procedure TWPLtr.LoadText(InsBegin4Edit : boolean);
var WPLtrFile : Text;
		WPLtrStream : PWPStream;
		TextFile : PTextStream;
		TextItem : pointer;
		TextRec : longint;
		Attr : word;
		Control : word;
    InString, OutString : string;


begin
 	ThinkingOn('Converting to WP');

	{---- Add Header ----}
	{Expand form to temp file on disk}
	New(WPLtrStream, init(WPSetup.LocalPath + Header));

	SetFormCodes(WPLtrStream^.FormCodes);
{	WPLtrStream^.SetCode('LM','<LM0>');                   {Cancel left margin code}
	WPLtrStream^.StartPrint('',WPSetup.FormsPath+Header);                 {Output standard form to file TEMP}

	{-- add beginner marker ---}
	if WPLtrStream^.FormFound then {only do beginner marker if header found}
		if InsBegin4Edit then WPLtrStream^.WriteStr(WPSetup.BeginMarker); {Mark end of header if editing - not if printing}

	if WPltrStream^.Status<>stOK then begin
		ProgramError('Could not create WP header');
		Loaded := False;  {Make sure marked as unloaded}
		ThinkingOff;
		exit;
	end;

	{---- Add Body ------}
	if FirstTextRec = -1 then begin               {If new}
		 WPLtrStream^.PrintForm(WPSetup.FormsPath + Header+'.FRM');        {add Standard form}
	end else begin
		 Outstring := ''; {for decoding each line below}
		 New(TextFile, init('WPLTRS.DAT', TLetterItemSize));
		 TextRec := FirstTextRec;
		 while TextRec > -1 do begin
				TextItem := TextFile^.GetNext(TextRec);
				if TextItem = nil then begin
					ProgramError('Could not retrieve Text Item for letter'
							+#13'FirstRec #'+N2Str(FirstTextRec));
					TextRec := -1;
				end else begin
					if TextRec = -1 then PTextItem(TextItem)^.Data := DelNulR(PTextItem(TextItem)^.Data);  {Delete last nuls if last one}

					if InsBegin4Edit then {if editing, just write out line as found}
						WPLtrStream^.writeStr(PTextItem(TextItem)^.Data)
					else begin
						{if not, need to decode any codes...}
						{split into two parts as Data may be 255 chars causing overflow if outstring has anything}
						OutString := Outstring + copy(PTextItem(TextItem)^.Data,1,100);
						WPLtrStream^.WriteCodedstr(OutString); {decodes & writes string, leaving outstring with remainder}

            if Length(OutString)>100 then begin
							WPltrStream^.WriteStr(Copy(Outstring,1,length(Outstring)-50)); {do up to last 50 chars}
							Outstring := Right(Outstring, 50);
						end;

						OutString := Outstring + copy(PTextItem(TextItem)^.Data,101,256);
						WPLtrStream^.WriteCodedstr(OutString); {decodes & writes string, leaving outstring with remainder}

						if Length(OutString)>100 then begin
							WPltrStream^.WriteStr(Copy(Outstring,1,length(Outstring)-50)); {do up to last 50 chars}
							Outstring := Right(Outstring, 50);
            end;

					end; {decoding & writing}
					Dispose(PTextItem(TextItem), done);
        end; {text item valid}
		 end; {while}
		 Dispose(TextFile, done);
		 if Outstring<>'' then
			WPLtrStream^.writecodedstr(OutString);
	end;


	WPLtrStream^.EndPrint; {tidy up}
	Dispose(WPLtrStream, done);

	{UnMark Archive bit, so we can tell if it's changed}
	Assign(WPLtrFile, WPSetup.LocalPath + Header);
	GetFAttr(WPLtrFile, Attr);
	Attr := Attr and not Archive;
	SetFAttr(WPLtrFile, Attr);

	Loaded := True;

	ThinkingOff;
end;


{********************************
 ***       STD WP LETTER      ***
 ********************************}
const
	{--- Required for Stream ----}
	RStdWPLetter : TStreamRec = (
		ObjType : srStdWPLetter;
		VmtLink : Ofs(TypeOf(TStdWPLetter)^);
		Load : @TStdWPLetter.Load;
		Store : @TStdWPLetter.Store
	);

procedure TStdWPLetter.CommonInit;
begin
	inherited CommonInit;
	Ext := 'STL';
	Path := WPSetup.FormsPath;
	DescendantsrType := srStdWPLetter;
	FileEditor := EditWPFile;
end;

function TStdWPLetter.srtype;
begin srtype := srStdWPLetter; end;



{********************************************************
 ***                                                  ***
 ***           v1-4 ESTIMATE/INVOICES (SPECIAL NODES) ***
 ***                                                  ***
 ********************************************************}
const
	v4InvoiceIDLength = 15; {length of ID index key string}

	Tv4EstimateSize = 60;
	Tv4InvoiceSize = Tv4EstimateSize + 40;
{	TPaymentNodeSize = TInvoiceNodeSize + 10;

{attaches a tree, headed by a rootnode, as a child to a node }
procedure AttachTree2Node(Tree, Node : PNodeItem);
begin
	Node^.Heap.FirstChild := Tree;
  while Tree<>nil do begin
  	Tree^.Heap.Parent := Node;
    Tree := Tree^.Heap.next;
  end;
end;

{*****************************
 ***     NODE ITEM         ***
 *****************************}
constructor TNodeItem.Init;
begin
	inherited Init;

	Disk.Next := -1;
	Disk.Prev := -1;
	Disk.FirstChild := -1;
{	Disk.Parent := -1;{}

	CommonInit;
end;

procedure TNodeItem.CommonInit; {Initialise non-file items}
begin
	Heap.Next := nil;
	Heap.Prev := nil;
	Heap.FirstChild := nil;
	Heap.Parent := nil;

	Expanded := True;

	RecNo := -1;
end;

destructor TNodeItem.Done;
begin
	{dispose of child & peer - effectively disposes of whole tree from this point}
	if Heap.firstchild<>nil then dispose(Heap.FirstChild, done);
	if Heap.Next<>nil then dispose(Heap.Next, done);{}

	inherited done;
end;

constructor TNodeItem.Load(var S : TStream);
var Rubbish : longint;
begin
	CommonInit;

	S.Read(Disk, sizeof(Disk));

	S.Read(Expanded, 1);
	S.Read(Rubbish, 3);  {what was the old parent pointer, now used for expanded}
end;

procedure TNodeItem.Store(var S : TStream);
begin end;


{************************************
 ***      NODE TREE ADMIN         ***
 ************************************}
constructor TNodeTree.init;
begin
	RootNodeID := -1;
	RootNode := nil;
	DummyRoot := nil;
	Loaded := True; {nothing to load}
end;

destructor TNodeTree.Done;
begin
	{assume if dummy root set, that rootnode will be its child}
	if DummyRoot<>nil then
		dispose(DummyRoot, done)
	else
		if RootNode<>nil then dispose(Rootnode, done);

	RootNode := nil;
end;

procedure TNodeTree.Load;
begin
	S.Read(RootNodeID, 4);
	RootNode := nil;
	Loaded := False;
end;

procedure TNodeTree.LoadTree;
var InvNodeStream : PDataStream;
begin
	ThinkingOn('Loading Tree');
	if RootNode<>nil then dispose(RootNode, done);
	if DummyRoot<>nil then DummyRoot^.Heap.FirstChild := nil;

	New(InvNodeStream, init('INVITEMS.NDE', 20)); {NodeItemSize}
	{load tree - see old trees TNOdeStream}
{	RootNode := PNodeStream(Stream(fiType))^.LoadTree(RootNodeID, DummyRoot);{}
	dispose(InvNodeStream, done);

	Loaded := True;
	ThinkingOff;
end;

procedure TNodeTree.SetDummyRoot;
begin
	if DummyRoot<>nil then begin
		DummyRoot^.Heap.FirstChild := nil;
		dispose(DummyRoot, done);
	end;

	DummyRoot := NDummyRoot;

	AttachTree2Node(RootNode, DummyRoot);
end;

procedure TNodeTree.ForEach;

	procedure DoNode(Node : PNodeItem);
	begin
		while Node<>nil do begin
			Action(Node);
			DoNode(Node^.heap.firstchild); {do children}
			Node := Node^.heap.next;
		end;
	end;

begin
	DoNode(RootNode); {start at root}
end;



{*****************************
 ***  ESTIMATE             ***
 *****************************}

const
	{--- Required for Stream ----}
	Rv4Estimate : TStreamRec = (
		ObjType : srv4Estimate;
		VmtLink : Ofs(TypeOf(Tv4Estimate)^);
		Load : @Tv4Estimate.Load;
		Store : @Tv4Estimate.Store
	);

procedure Tv4Estimate.CommonInit;  {Init shared betweeen load and init above}
begin
	inherited CommonInit;
	ItemTree.Init;
	ItemTree.SetDummyRoot(New(PNodeItem, init)); {set dummy root}
	New(Comment, init);
{	FileAdmin(fiInvoiceNodes)^.LogOn;{}
end;


destructor Tv4Estimate.Done;
begin
	ItemTree.Done;{}
{	FileAdmin(fiInvoiceNodes)^.LogOff;{}
	Dispose(Comment, done);
	inherited Done;
end;

constructor Tv4Estimate.Load(var S : TDataStream);
var Ver : byte;

begin
	CommonInit;

	{--- Load fields -----}
	S.Read(Ver, 1);

	DiscountedTotal.Clear;

	case ver of
		3 : begin
			{24/10/95 added discounted total to estimates}
			S.Read(Ref, sizeof(Ref));
			S.Read(FromRef, sizeof(FromRef));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			DiscountedTotal.Load(S);
			VAT.Load(S);
		end;
		2 : begin
			S.Read(Ref, sizeof(Ref));
			S.Read(FromRef, sizeof(FromRef));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);
		end;
		1 : begin
			FromRef := '';
			S.Read(Ref, sizeof(Ref));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);
	end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'Tv4Estimate.Load, UOldObjs.pas',mfError);
		fail;
	end;

end;

function Tv4Estimate.RecSize;
begin RecSize := Tv4EstimateSize; end;

function Tv4Estimate.srType;
begin srType := srv4Estimate; end;

procedure Tv4Estimate.Calculate;
{var InvoiceNode : PInvoiceNode;{}
begin
{	if not ItemTree.Loaded then ItemTree.LoadTree;

	{Set up single head to tree}
{	InvoiceNode := PInvoiceNode(ItemTree.DummyRoot);
	if InvoiceNode = nil then
		ProgramError('No Dummy Root Node', 'Could not calculate estimate/invoice')
	else begin
		InvoiceNode^.CalculateNodeTotal;

		Total.SettoPence(InvoiceNode^.NodeTotal.inPence);
		DiscountedTotal.SetToPence(InvoiceNode^.NodeDiscountedTotal.inPence);
		VAT.SetToPence(InvoiceNode^.NodeVAT.inPence);
	end;{}
end;


{*****************************
 ***  INVOICE              ***
 *****************************}

const
	{--- Required for Stream ----}
	Rv4Invoice : TStreamRec = (
		ObjType : srv4Invoice;
		VmtLink : Ofs(TypeOf(Tv4Invoice)^);
		Load : @Tv4Invoice.Load;
		Store : @Tv4Invoice.Store
	);

constructor Tv4Invoice.Load(var S : TDataStream);
var Ver : byte;
		B : byte;
		SeekPos : longint;

begin
	CommonInit;
	Paid.Init;
	LastPaymentDate.Clear;

	{--- Load fields -----}
	S.Read(Ver, 1);

	case Ver of
		7 : begin
			{changed the way due was calculated - now done from Paid (total of payments rec)
			and total depending on discountedtotal}
			S.Read(Ref, sizeof(Ref));
			S.Read(FromRef, sizeof(FromRef));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);

			DiscountedTotal.Load(S);
			S.Read(DaysDiscount, 1);
			SentDate.Load(S);
			Paid.Load(S);
			LastPaymentDate.Load(S);

			S.Read(B, 1);
			if (B and $02)=0 then WrittenOff:=False else WrittenOff := True;
			if (B and $04)=0 then OverdueLtr[1] := False else OverdueLtr[1] := True;
			if (B and $08)=0 then OverdueLtr[2] := False else OverdueLtr[2] := True;
			if (B and $10)=0 then OverdueLtr[3] := False else OverdueLtr[3] := True;

			CalculateTotals;
		end;

		6 : begin
			{added fromref & stored discountedtotal}
			S.Read(Ref, sizeof(Ref));
			S.Read(FromRef, sizeof(FromRef));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);

			DiscountedTotal.Load(S);
      S.Read(DaysDiscount, 1);
	    SentDate.Load(S);
			Due.Load(S);

			S.Read(B, 1);
			if (B and $02)=0 then WrittenOff:=False else WrittenOff := True;
			if (B and $04)=0 then OverdueLtr[1] := False else OverdueLtr[1] := True;
			if (B and $08)=0 then OverdueLtr[2] := False else OverdueLtr[2] := True;
			if (B and $10)=0 then OverdueLtr[3] := False else OverdueLtr[3] := True;

			Calculate; {re-calculate whole invoice - slow I expect!}
		end;

		5 : begin

			FromRef := '';
      DiscountedTotal.SetTo(0);

			S.Read(Ref, sizeof(Ref));
			S.Read(ReWho, 4);
			S.Read(ToWho, 4);

			ItemTree.Load(S);

			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);

			S.Read(DaysDiscount, 1);
			SentDate.Load(S);
			Due.Load(S);

			S.Read(B, 1);
			if (B and $02)=0 then WrittenOff:=False else WrittenOff := True;
			if (B and $04)=0 then OverdueLtr[1] := False else OverdueLtr[1] := True;
			if (B and $08)=0 then OverdueLtr[2] := False else OverdueLtr[2] := True;
			if (B and $10)=0 then OverdueLtr[3] := False else OverdueLtr[3] := True;

			Calculate; {re-calculate whole invoice - slow I expect!}

		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TInvoice.Load, UOldObjs.pas',mfError);
		fail;
	end;
end;

procedure Tv4Invoice.CalculateTotals;
begin
{	if not Sent then begin
		TotalincVAT.SetToPence(DiscountedTotal.inPence + VAT.inPence);
		Due.Clear;
	end else begin
		if Discountable(LastPaymentDate) then begin
			TotalincVAT.SetToPence(DiscountedTotal.inPence + VAT.inPence);
		end else begin
			TotalincVAT.SetToPence(Total.inPence + VAT.inPence);
		end;
		Due.SetToPence(TotalincVAT.inPence-Paid.inPence);
	end;{}
end;

{procedure Tv4Invoice.Calculate;
var	WorkNode : PInvoiceNode;
begin
	inherited Calculate; {sets total, vat and calulatesnodetotal for rootnode}

	{---- Work out amount due ---------}
{	Paid.SetTo(0);
	LastPaymentDate.Clear;

	if Sent then begin {only if sent}

		{we will assume that the amount is discountable unless at the end of
		calculating the payments made, there is still an amount outstanding,
		or if one of the payments is made outside the discountable date range}
{		WorkNode := PInvoiceNode(ItemTree.RootNode);

		while (WorkNode <> nil) and (typeof(WorkNode^)=typeof(TPaymentNode)) do begin
			Paid.SetToPence(Paid.inPence - WorkNode^.Total.inPence);  {paymentnode.total is negative}

{			if (PPaymentNode(WorkNode)^.Date.Days>LastPaymentDate.Days) then
				LastPaymentDate.SetToDate(PPaymentNode(WorkNode)^.Date);

			WorkNode := PInvoiceNode(WorkNode^.Heap.Next);
		end;

	end;
	CalculateTotals;
end;{}

function Tv4Invoice.RecSize;
begin RecSize := Tv4InvoiceSize; end;

function Tv4Invoice.srType;
begin srType := srv4Invoice; end;

{************************************************
 ***         INVOICE NODES                    ***
 ************************************************}
{const
	{--- Required for Stream ----}
{	RPaymentNode : TStreamRec = (
		ObjType : srPaymentNode;
		VmtLink : Ofs(TypeOf(TPaymentNode)^);
		Load : @TPaymentNode.Load;
		Store : @TPaymentNode.Store
	);


{*****************************
 ***  PAYMENT NODE         ***
 *****************************}

{constructor TPaymentNode.Load;
var Ver : byte;
begin
	S.Read(Ver, 1); {ver used to be set only in parent (ie invoicenode)}
{	if Ver<3 then begin
		S.Seek(S.GetPos-1); {move back over ver}
{		inherited Load(S);
		Date.SetToDate(OldVerDate);
		From := -1; {would be nice to set to parent's invoice...}
{	end else
		if (Ver = 3) then begin
			inherited Load(S);
			Date.Load(S);
			S.Read(From, 4);
		end;
end;

procedure TPaymentNode.Store;
begin end;

procedure TPaymentNode.CalculateNodeTotal;
begin
	{basically, don't calculate anything.  There aren't any children,
	and the total, etc of an invoice should not include payments.  As invoice
	totals are calculated from the nodetotal fields, and this routine is
	blanked so as not to provide them, payment nodes will not change the total.
	see tinvoice.calculate for how it deals with the amount due}
{end;

function TPaymentNode.RecSize;
begin RecSize := TPaymentNodeSize; end;

function TPaymentNode.srType;
begin srType := srPaymentNode; end;

{********************************************************
 ***                                                  ***
 ***           v1-4 JOB SHEETS                        ***
 ***                                                  ***
 ********************************************************}
const
	{--- Required for Stream ----}
	Rv4Job : TStreamRec = (
		ObjType : srv4Job;
		VmtLink : Ofs(TypeOf(Tv4Job)^);
		Load : @Tv4Job.Load;
		Store : @Tv4Job.Store
	);

function Tv4Job.RecSize;
begin RecSize := 120; end;

function Tv4Job.srType;
begin srType := srv4Job; end;

{Required by get as well}
procedure Tv4Job.CommonInit;
begin
	inherited CommonInit;

	ItemTree.init;

	FileAdmin(fiJimmys)^.LogOn;                    {For machines and printouts}
	FileAdmin(fiHooks)^.LogOn;                    {For machines and printouts}
{  FileAdmin(fiInvoiceNodes)^.LogOn;{}

	New(Notes, init);
end;

destructor Tv4Job.Done;
begin
	dispose(Notes, done);

{	FileAdmin(fiInvoiceNodes)^.LogOff;{}
	FileAdmin(fiHooks)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;                    {For machines and printouts}

	ItemTree.Done;

	inherited Done;
end;

constructor Tv4Job.Load(var S : TDataStream);
var I,L : integer;
		Ver : byte;
		MS : string;  {For reading "old" task into "new" memo string}
		OldPtr : longint; {for reading "old" back chain pointer}
		OldJobNo : string[20];
{$IFDEF khrdware}
		HWInvNode : PHWInvNode;
{$ENDIF}

begin
	CommonInit;

	S.Read(Ver, 1);

	Invoiced := False;
{	Dat2Idx := -1;         {there won't be any index items for the older
	versions - as soon as an index item is created, this store version will
	be too}
	Lock := 0;

  case Ver of
   	5: begin
      {17/3/94 Changed to v3 type of jobs/invoices/etc - added lists of job items}
      S.Read(Lock, 1);
			S.Read(OldPtr, 4);

		  S.Read(ForWho, 4);
			CustRef := S.ReadStr;
		  S.Read(ByWho, 4);
			OldJobNo := S.ReadStr;  OurRef := OldJobNo;

		  CallDate.Load(S);
 			VisitDate.Load(S);
			VisitTime := S.ReadStr;

			ItemTree.Load(S);

			Notes^.Load(S);

			S.Read(Outcome, sizeof(Outcome));
			S.Read(Invoiced, 1);

		end;
   	4: begin
      {17/3/94 Changed to v3 type of jobs/invoices/etc - added lists of job items}
		  S.Read(ForWho, 4);
			CustRef := S.ReadStr;
		  S.Read(ByWho, 4);
			OldJobNo := S.ReadStr;  OurRef := OldJobNo;

			CallDate.Load(S);
			VisitDate.Load(S);
			VisitTime := S.ReadStr;

			ItemTree.Load(S);

			Notes^.Load(S);

			S.Read(Outcome, sizeof(Outcome));
			S.Read(Invoiced, 1);
		end;
		3: begin
			{Added person pointer and memo type of fault}
			S.Read(OldJobNo     ,sizeof(OldJobNo)); OurRef := OldJobNo;
			CallDate.Load(S);
			VisitDate.Load(S);
			S.Read(VisitTime ,sizeof(VisitTime));

{$IFDEF khrdware}
			{Convert old markers to new task items - wastes space if read
			and not saved - such as list displays....}
			FileAdmin(fiInvoiceNodes)^.LogOn;
			for I := 1 to 6 do begin
				S.Read(OldPtr,4);{}
				if OldPtr<>-1 then begin
					New(HWInvNode, init);
					HWInvNode^.HardwareID := OldPtr;
					AddPeer2Node(ItemTree.RootNode, HWInvNode);
{					PNodeStream(FileAdmin(fiInvoiceNodes]^.FilePtr)^.InsertPeer(RootNodeID,-1,RootNodeID, PNodeItem(HWInvNode));{}
{					dispose(HWInvNode, done);{}
				end;
			end;
			FileAdmin(fiInvoiceNodes)^.LogOff;
			ItemTree.Loaded := True;
{$ENDIF}

      S.Read(CustRef,   sizeof(CustRef));
      S.Read(OldPtr, 4);{Old subcontracted}
      S.Read(ByWho, 4);

			Notes^.Load(S);

			S.Read(OldPtr, 4); {old pointer to chain}
			S.Read(ForWho, 4);
  	end;
   	2: begin
			{Added another 3 machines, and convert fault storage to string pointer}
			S.Read(OldJobNo     ,sizeof(OldJobNo)); OurRef := OldJobNo;
			CallDate.Load(S);
			VisitDate.Load(S);
			S.Read(VisitTime ,sizeof(VisitTime));
			for I := 1 to 6 do S.Read(OldPtr,4);

      S.Read(CustRef,   sizeof(CustRef));
			S.Read(OldPtr, 4);{}
      S.Read(ByWho, 4);

      for I := 1 to 4 do begin
				MS := S.ReadStr;
				{ANd somehow read them in}
				LSAppendStr(Notes^.Text, MS+#13#10);
			end;

			S.Read(OldPtr, 4);
			ForWho := -1;
		end;
		1: begin
			S.Read(OldJobNo     ,sizeof(OldJobNo)); OurRef := OldJobNo;
			CallDate.Load(S);
			VisitDate.Load(S);
			S.Read(VisitTime ,sizeof(VisitTime));
			for I := 1 to 3 do S.Read(OldPtr,4);

			S.Read(CustRef,   sizeof(CustRef));
			S.Read(OldPtr, 4);{}
			S.Read(ByWho, 4);

			for I := 1 to 4 do begin
				S.Read(MS, 41);
				{ := S.ReadStr; if MS = '' then MS := S.ReadStr; {Sometimes EReadStr returns '' instead of next line}
				{ANd somehow read them in}
				LSAppendStr(Notes^.Text, MS+#13#10);
			end;

			S.Read(OldPtr, 4);
			ForWho := -1;
		end
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not understood'#13#10'Tv4Job.Load',mfError);
		fail; {return nil pointer}
	end; {case}
end;

{********************************************************
 ***                                                  ***
 ***           v1-4 PRODUCTS                          ***
 ***                                                  ***
 ********************************************************}
const
	{--- Required for Stream ----}
	Rv4Product : TStreamRec = (
		ObjType : srv4Product;
		VmtLink : Ofs(TypeOf(Tv4Product)^);
		Load : @Tv4Product.Load;
		Store : @Tv4Product.Store
	);
function Tv4product.recsize;
begin recsize := 250; end;

function tv4product.srtype;
begin srtype := srv4product; end;


constructor Tv4Product.Load;
var Ver : byte;
		I,L : longint;

begin
	CommonInit;  {defaults, etc}
	S.Read(Ver, 1);
	RecNo := -1; {not set yet}
	Category2Idx := -1;

	for I := 0 to 3 do begin
		NumInStock[I] := 0;
		Sold[I] := 0;
		OnOrder[I] := 0;
		WarningLevel[I] := 0;
	end;

	{NB - Version 4 has a changed file structure - 250 bytes per product instead
	of 100 - so all others are strictly invalid - the record should have been converted}
	case Ver of
		7 : begin
			{6/12/95 Added 2nd index}
			S.Read(CategoryIdx, sizeof(CategoryIdx));
			S.Read(CodeIdx, sizeof(CodeIdx));
			S.Read(Lock, sizeof(Lock));
			S.Read(Category2Idx, 4);
			S.Read(Deleted, sizeof(Deleted));

			Categories := S.ReadStr;
			Desc := S.ReadStr;
			Code := S.ReadStr;

			for I := 0 to 3 do S.Read(Units[I], sizeof(TSCode));

			S.Read(Supplier, sizeof(Supplier));
			QuoteDate.Load(S);
			S.Read(Quoter, sizeof(Quoter));
			for I := 0 to 3 do QuotedCost[I].Load(S);

			for L := 1 to 3 do begin
				S.Read(PriceBand[L].Band, sizeof(TSCode));
				S.Read(PriceBand[L].Margin,2);
				for I := 0 to 3 do PriceBand[L].Price[I].Load(S);
			end;

			Location := S.ReadStr;
			for I := 0 to 3 do S.Read(NumInStock[I], sizeof(NumInStock[I]));
			for I := 0 to 3 do S.Read(OnOrder[I], sizeof(OnOrder[I]));
			for I := 0 to 3 do S.Read(Sold[I], sizeof(Sold[I]));
			for I := 0 to 3 do S.Read(WarningLevel[I], sizeof(WarningLevel[I]));
		end;
		6 : begin
			{30/11/94 Added unit stock bits}
			S.Read(CategoryIdx, sizeof(CategoryIdx));
			S.Read(CodeIdx, sizeof(CodeIdx));
			S.Read(Lock, sizeof(Lock));
			S.Read(Deleted, sizeof(Deleted));

			Categories := S.ReadStr;
			Desc := S.ReadStr;
			Code := S.ReadStr;

			for I := 0 to 3 do S.Read(Units[I], sizeof(TSCode));

			S.Read(Supplier, sizeof(Supplier));
			QuoteDate.Load(S);
			S.Read(Quoter, sizeof(Quoter));
			for I := 0 to 3 do QuotedCost[I].Load(S);

			for L := 1 to 3 do begin
				S.Read(PriceBand[L].Band, sizeof(TSCode));
				S.Read(PriceBand[L].Margin,2);
				for I := 0 to 3 do PriceBand[L].Price[I].Load(S);
			end;

			Location := S.ReadStr;
			for I := 0 to 3 do S.Read(NumInStock[I], sizeof(NumInStock[I]));
			for I := 0 to 3 do S.Read(OnOrder[I], sizeof(OnOrder[I]));
			for I := 0 to 3 do S.Read(Sold[I], sizeof(Sold[I]));
			for I := 0 to 3 do S.Read(WarningLevel[I], sizeof(WarningLevel[I]));
		end;
		5 : begin
			{Stored desc & code as part of main record - not only in index}
			S.Read(CategoryIdx, sizeof(CategoryIdx));
			S.Read(CodeIdx, sizeof(CodeIdx));
			S.Read(Lock, sizeof(Lock));
			S.Read(Deleted, sizeof(Deleted));

	    Categories := S.ReadStr;
			Desc := S.ReadStr;
			Code := S.ReadStr;

	    for I := 0 to 3 do S.Read(Units[I], sizeof(TSCode));

			S.Read(Supplier, sizeof(Supplier));
			QuoteDate.Load(S);
			S.Read(Quoter, sizeof(Quoter));
			for I := 0 to 3 do QuotedCost[I].Load(S);

			for L := 1 to 3 do begin
      	S.Read(PriceBand[L].Band, sizeof(TSCode));
				S.Read(PriceBand[L].Margin,2);
				for I := 0 to 3 do PriceBand[L].Price[I].Load(S);
			end;

			Location := S.ReadStr;
		end;
		4 : begin
      {Stored desc & code as part of main record - not only in index}
			S.Read(CategoryIdx, sizeof(CategoryIdx));
			S.Read(CodeIdx, sizeof(CodeIdx));
			S.Read(Lock, sizeof(Lock));
			S.Read(Deleted, sizeof(Deleted));

			S.Read(Categories, 4);
			Categories := Categories + S.ReadStr;
			Desc := S.ReadStr;
			Code := S.ReadStr;

	    for I := 0 to 3 do S.Read(Units[I], sizeof(TSCode));

			S.Read(Supplier, sizeof(Supplier));
			QuoteDate.Load(S);
			S.Read(Quoter, sizeof(Quoter));
			for I := 0 to 3 do QuotedCost[I].Load(S);

      for L := 1 to 3 do begin
      	S.Read(PriceBand[L].Band, sizeof(TSCode));
        PriceBand[L].Margin := 0;
      	for I := 0 to 3 do PriceBand[L].Price[I].Load(S);
 	   	end;

			Location := S.ReadStr;
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'Tv4Product.Load, UOldObjs.pas',mfError);
		fail;
	end; {case}
end; {proc}




{****************************************888
 ***           REGISTER ALL              ***
 *******************************************}

begin
	RegisterType(ROldSCodeItem);  {Register first, so it's there for retrieving but not for saving}

	RegisterType(ROldVerCompany); {remember to comment out registertype in company}
	RegisterType(ROldVerPerson); {ditto}

	RegisterType(Rv2AltAddress); {Register for streams}
	RegisterType(RAltAddress); {Register for streams}

	RegisterSCodeType(scLetters, 'KLetters.SC', 'Letter Types', StdScodeCreator);

	RegisterType(RWPLtr);
	RegisterType(RStdWPLetter);

	RegisterType(REdLtr);
	RegisterType(RStdEdLetter);

	RegisterType(Rv4Invoice);
	RegisterType(Rv4Estimate);

{	RegisterType(RPaymentNode){}
	RegisterType(Rv4Job);
	RegisterType(Rv4Product);
end.
