{*************************************************************************
 ***                                                                   ***
 ***                     v4 OBSOLETE INVOICE/ESTIMATES                 ***
 ***                                                                   ***
 *************************************************************************}
{$I compflgs}
unit UPv4Inv;

interface

uses objects, files, scodes, notes, dattime, smoney, global, jimmys;

type
	{==== NODES & DERIVITIVES ========}
	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;

		procedure CommonInit; virtual;
		destructor Done; virtual;

		constructor Load(Var S : TStream);
		procedure Store(var S : TStream); {provide store for rtype reg}
	end;


	PInvoiceNode = ^TInvoiceNode;
	TInvoiceNode = object(TNodeItem)

		{Editor Fields}
		Total : TSimpleMoney;
		DiscountPerc : single;  {Modifier - margin/discount}
		Discount : TSimpleMoney;
		DiscountedTotal : TSimpleMoney;  {calculated}
		VATCode : TSCode; {VAT sentence code}
		VAT   : TSimpleMoney;  {VAT on total - stored in case of rate changes}
		DiscountedTotalincVAT : TSimpleMoney; {calculated}
		NomCat : TSCode;

		DiscountType : boolean; {True: by percentage, False: by amount}

		{Calculated fields - not stored}
		TotalincVAT : TSimpleMoney;

		NodeTotal : TSimpleMoney;    {Total including subnodes}
		NodeDiscount : TSimpleMoney;
		NodeDiscountedTotal : TSimpleMoney;
		NodeVAT : TSimpleMoney;
		NodeTotalincVAT : TSimpleMoney;
		NodeDiscountedTotalincVAT : TSimpleMoney;


		v4Date : TDate; {rubbish/dummy for load versions 1 & 2- the date will need to be passed to descendants}

		constructor Load(var S : TDataStream);

		procedure CalculateTotals;												virtual;
		procedure CalculateDiscountedTotal;
		procedure CalculateVAT;
		procedure CalculateTotalincVAT;
		procedure CalculateNodeTotal;							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 : TSimpleMoney; {Just used for input}
		InputNomCat : TSCode;

		constructor Load(var S : tDatastream);
	end;{}

	Pv4CodedInvNode = ^Tv4CodedInvNode;
	Tv4CodedInvNode = object(TInvoiceNode)

		Date : TDate;
		Code : string[3];        {Event code}
		CodeLine : string[12];   {STring of treatment codes}
		Comment : PFreeTextData;

		BasePrice : TSimpleMoney;
		Quantity : string[10];
		PriceEach : TSimpleMoney;

		{-- Methods --}
		procedure CommonInit; virtual;
		destructor Done; virtual;

		constructor Load(var S : TDataStream);
	end;


	PFreeTextInvNode = ^TFreeTextInvNode;
	TFreeTextInvNode = object(TInvoiceNode)

		FreeText : PFreeTextData;

		destructor Done; virtual;
		constructor Load(var S : TDataStream);
	end;


	PProductInvNode = ^TProductInvNode;
	TProductInvNode = object(TInvoiceNode)

		ProductID : longint;

		Quantity : string[10];
		Units    : TScode;

		PriceEach : TSimpleMoney;

		constructor Load(var S : TDataStream);
	end;

	PHWInvNode = ^THWInvNode;
	THWInvNode = object(TInvoiceNode)

		HardWareID : longint;

		{-- Methods --}
		constructor Load(var S : TDataStream);
	end;


	{======== ESTIMATE =============}
	Pv4Estimate = ^Tv4Estimate;
	Tv4Estimate = object(TJimmy)

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

		ToWho    : longint;
		ReWho    : longint;

		RootNode : longint;

		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;

	{========= INVOICE ==========}
	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;


	{============= JOB ===============}
	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];

		RootNode : longint;

		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;

	Pv4PurchaseORDER = ^Tv4PurchaseORDER;
	Tv4PurchaseOrder = object(TJimmy)

		OurRef		: string[5];
		ToRef 		: string[10];

		ToWho    : longint;

		RootNode : longint;

		Comment   : PFreeTextData;            {Text on Order}

		SentDate	: TDate;
		LastPrint : TDate;

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

		Due			   	: TSimpleMoney;								{Balance still outstanding}

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

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

 Pv4PackingSlip = ^Tv4PackingSlip;
 Tv4PackingSlip = object(TJimmy)

		Ref				: string[5];

		ToWho    : longint;

		RootNode : longint;

		Comment   : PFreeTextData;

		SentDate	: TDate;

		procedure CommonInit; virtual;
		destructor Done; virtual;
		constructor Load(var S : TDataStream);

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

procedure JimmyfyInvoiceNodes(const RootNode,OrderID : longint; var FirstItemID, FirstPmntID : longint);


implementation

uses payments, ordproc,
			kamsetup,
			kcompsup,
			help,
			dbg,
			vat, lstrings, kforsale,jimhooks, minilib, tuimsgs;

{**************************************
 **         JIMMY CONVERTER         ***
 **************************************}
{for use with the update.pas units FullJimmyUpdate}
procedure JimmyfyInvoiceNodes(const RootNode,OrderID : longint; var FirstItemID,FirstPmntID : longint);
var NodeStream : PDataStream;

	procedure LoadBranch(ParentJimmyID, NodeID : longint; var NewItemID,NewPmntID : longint);
	var WorkNode : PNodeItem;
			OrderJimmy : PJimmy; {new coded order item, jimmy}
			Hook{,PrevHook{} : PHook;
			W : word;
			L : longint;

		procedure MakeHook;
		begin
			{create new hook}
			New(Hook, init);
			with Hook^ do begin
				Expanded := True;

				SortKey := 0;

				OwnerID := OrderID;
				srType := OrderJimmy^.srtype;
				JimmyID := OrderJimmy^.RecNo;
			end;
		end;

	begin
{		PrevHook := nil;{}
		WorkNode := nil;
		NewItemID := -1;
		newPmntID := -1;

{		if (NodeID > NodeStream^.NoRecs) or (NodeID<-1) then begin
			DebugNote('NodeID = '+N2Str(NodeID)+' out of range...','');
			NodeID := -1;
		end;

		{get first and peers}
		while NodeID<>-1 do begin

			WorkNode := PNodeItem(NodeStream^.GetAt(NodeID));

{			DebugNote('Loading Branch Node #'+N2Str(NodeID)+' for Order #'+N2Str(orderId),'');{}

			if WorkNode = nil then begin
				DBaseMessage(NodeStream, 'JIMMYFYING INV NODES'#13'Could not retrieve node #'+N2Str(NodeID),mfError,hcNoContext);
				NodeID := -1;
			end else begin
				OrderJimmy := nil;
				Hook := nil;

{				DebugNote('Node Ptrs:  N'+N2Str(workNode^.Disk.Next)
														+' P'+N2Str(WorkNode^.Disk.Prev)
														+' C'+N2Str(WorkNode^.Disk.FirstChild),'');{}

				{convert to coded order item jimmy}
				if typeof(WorkNode^) = typeof(Tv4CodedInvNode) then begin
					OrderJimmy := New(PCodedOrderItem, init(NIL));

					with PCodedOrderItem(OrderJimmy)^ do begin
{						DebugNote('Node is CodedOrderItem','');{}

						Date.SetToDate(		 Pv4CodedInvNode(WorkNode)^.Date);
						Code 						:= Pv4CodedInvNode(WorkNode)^.Code;
						CodeLine 				:= Pv4CodedInvNode(WorkNode)^.CodeLine;
						dispose(Comment, done);
						Comment 				:= Pv4CodedInvNode(WorkNode)^.Comment;
						New(Pv4CodedInvNode(WorkNode)^.Comment, init);

						BasePrice.Value	:= Pv4CodedInvNode(WorkNode)^.BasePrice.Value;
						Quantity 				:= Pv4CodedInvNode(WorkNode)^.Quantity;
						PriceEach.VAlue	:= Pv4CodedInvNode(WorkNode)^.PriceEach.Value;{}
					end;
				end;

				{convert to Goods order item jimmy}
				if typeof(WorkNode^) = typeof(TProductInvNode) then begin
					OrderJimmy := New(PGoodsOrderItem, init(NIL));

					with PGoodsOrderItem(OrderJimmy)^ do begin
{						DebugNote('Node is GoodsOrderItem','');{}

						GoodsID := PProductInvNode(WorkNode)^.ProductID;
						Quantity := PProductInvNode(WorkNode)^.Quantity;
						Units := PProductInvNode(WorkNode)^.Units;
						PriceEach.Value := PProductInvNode(WorkNode)^.PriceEach.Value;
					end;
				end;

				{convert to free text order item jimmy}
				if typeof(WorkNode^) = typeof(TFreetextInvNode) then begin
					OrderJimmy := New(PFreeTextOrderItem, init(NIL));

					with PFreeTextOrderItem(OrderJimmy)^ do begin
{						DebugNote('Node is FreeTextOrderItem','');{}

            dispose(FreeTExt, done);
						FreeText := PFreeTextInvNode(WorkNode)^.FreeText;
						New(PFreeTextInvNode(WorkNode)^.FreeText, init);
					end;
				end;

				{convert to payment jimmy}
				if typeof(WorkNode^) = typeof(TPaymentNode) then begin
					OrderJimmy := New(PPayment, init(NIL));

					with PPayment(OrderJimmy)^ do begin
{						DebugNote('Node is Payment','');{}

						Date.SetToDate(PPaymentNode(WorkNode)^.Date);
						WhoPaidID := PPaymentNode(WorkNode)^.From;
						Paid.Value :=	-PPaymentNode(WorkNode)^.Total.Value;
					end;
				end;

				{Hardware item - convert to direct hook pointer}
				if typeof(WorkNode^) = typeof(THWInvNode) then begin
					OrderJimmy := New(PPickHWOrderItem, init(nil));

					PPickHWOrderItem(OrderJimmy)^.HardwareID := PHWInvNode(WorkNode)^.HardwareID;
				end;

				if (OrderJimmy=nil) and (Hook=nil) then begin
					{=== TYPE OF ITEM NOT TRAPPED! =============}
					nODEsTREAM^.SeekRec(NodeID);
					NodeStream^.Read(W, 2);
					ProgramWarning('JIMMYFYING INV NODES'#13'No typeof check for WorkNode #'+N2Str(NodeID)+' sr'+N2Str(W),hcNoContext);
					NodeID := -1;
				end else begin

					if OrderJimmy<>nil then begin

						if OrderJimmy^.srType = srPayment then begin
							{=========== PAYMENT NODES ================}
							PPayment(OrderJimmy)^.ForOrderID := OrderID;
							PutJImmy(OrderJimmy);

							MakeHook;

							HookFile^.Insert(NewPmntID, Hook, biStart)

						end else begin

							{========== ORDER NODES =================}
							{set up std fields}
							with POrderItem(OrderJimmy)^ do begin
								ForOrder 			:= OrderID;
								ParentItemID 	:= ParentJimmyID;

								with PriceGroup do begin
									Price.Value 	:= Pv4CodedInvNode(WorkNode)^.Total.Value;
									VATSCode 			:= Pv4CodedInvNode(WorkNode)^.VATCode;
									VAT.Value 		:= Pv4CodedInvNode(WorkNode)^.VAT.Value;
									if (VAT.Value<>0) and (delspaceR(VATSCode)='') then VATSCode := ProgramSetup.Get(siDefaultVAT,'STD');
									Total.Value 	:= Pv4CodedInvNode(WorkNode)^.TotalincVAT.Value;

									if Pv4CodedInvNode(WorkNode)^.NomCat<>'' then
										NomCat 			:= Pv4CodedInvNode(WorkNode)^.NomCat;

									if Pv4CodedInvNode(WorkNode)^.DiscountType = True then begin
										{by percentage}
										CashDiscount.Rate 	:= Pv4CodedInvNode(WorkNode)^.DiscountPerc;
										CashDiscount.ByRate := True;
										CashDiscount.Amount.Value := Pv4CodedInvNode(WorkNode)^.Discount.Value;
									end else begin
										{by amount}
										CashDiscount.Rate 	:= Pv4CodedInvNode(WorkNode)^.DiscountPerc;
										CashDiscount.ByRate := False;
										CashDiscount.Amount.Value := Pv4CodedInvNode(WorkNode)^.Discount.Value;
									end;

									if VATSCode<>'' then begin
										VATRate := PVATRateSCodeItem(GetSCode(scVATRates, VATScode))^.Rate;
										{real value that was used}
										if (Price.Value<>0) and ((Price.Value-CashDiscount.Amount.Value)<>0) then
											VATRate	:= VAT.Value*100/(Price.Value-CashDiscount.Amount.Value);{}
									end;
								end;

								PutJimmy(OrderJimmy);

{								DebugNote('Stored at '+N2Str(Recno),'');{}
							end;

							if Hook=nil then MakeHook; {if not hardware item above}

							{Get all children}
							if WorkNode^.Disk.FirstChild <> -1 then
								LoadBranch(OrderJimmy^.RecNo, WorkNode^.Disk.FirstChild, Hook^.ChildID,L); {assume payments only in root}

							{store hook at end of line}
							HookFile^.Insert(NewItemID, Hook, biEnd);
						end; {not payment node}

{						DebugNote('Stored Hook at '+N2Str(Hook^.RecNo)+' P'+N2Str(Hook^.PrevID)
													+' C'+N2Str(Hook^.ChildID),'');{}

						dispose(Hook, done);  Hook := nil;
						dispose(OrderJimmy, done);

					end; {orderjimmy not nil}

					NodeID := WorkNode^.Disk.Next;
				end; {if item was dealt with}

				dispose(WorkNode, done);

			end; {if worknode<>nil}

			if (NodeID > NodeStream^.NoRecs) or (NodeID<-1) then begin
				Debug.Write('NodeID = '+N2Str(NodeID)+' out of range...');
				NodeID := -1;
			end;

		end; {while nodeid<>-1}

{		if PrevHook<>nil then dispose(PrevHook, done);{}
	end; {end loadbranch}

begin
	New(NodeStream ,init('INVITEMS.NDE', 1, StreamBufSize));

	FileAdmin(fiHooks)^.LogOn;

	LoadBranch(-1, RootNode, FirstItemID, FirstPmntID);

	FileAdmin(fiHooks)^.LogOff;
	dispose(NodeStream, done);
end;


{********************************************************
 ***                                                  ***
 ***           NODES & TREE READING                 ) ***
 ***                                                  ***
 ********************************************************}

const
	{--- Required for Stream ----}
	Rv4CodedInvNode : TStreamRec = (
		ObjType : srv4CodedInvNode;
		VmtLink : Ofs(TypeOf(Tv4CodedInvNode)^);
		Load : @Tv4CodedInvNode.Load;
		Store : @Tv4CodedInvNode.Store{}
	);

const
	{--- Required for Stream ----}
	RProductInvNode : TStreamRec = (
		ObjType : srProductInvNode;
		VmtLink : Ofs(TypeOf(TProductInvNode)^);
		Load : @TProductInvNode.Load;
		Store : @TProductInvNode.Store
	);

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

const
	{--- Required for Stream ----}
	RFreeTextInvNode : TStreamRec = (
		ObjType : srFreeTextInvNode;
		VmtLink : Ofs(TypeOf(TFreeTextInvNode)^);
		Load : @TFreeTextInvNode.Load;
		Store : @TFreeTextInvNode.Store
	);

const
	{--- Required for Stream ----}
	RHWInvNode : TStreamRec = (
		ObjType : srv4HWInvNode;
		VmtLink : Ofs(TypeOf(THWInvNode)^);
		Load : @THWInvNode.Load;
		Store : @THWInvNode.Store
	);

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;
begin end;

constructor TInvoiceNode.Load;
var Ver : byte;
		Rubbish : longint;
		OldNomCat : word; {old ver had a word not a scode}

begin
	S.Read(Ver, 1);

{	CommonInit;  {set to nil, etc - done by inherited load}
	VATCode := '';
	DiscountType := True; {default to %}

	case Ver of
		1 : begin
			inherited Load(S);
			S.Read(Rubbish, 4);
			v4Date.Load(S);
			Total.SetTo(0);
			VAT.SetTO(0);
			DiscountPerc := 0;
			Discount.SetTo(0);
		end;
		2 : begin
			inherited Load(S);
			v4Date.Load(S);{}
			Total.Load(S);
			VAT.Load(S);
{			S.Read(Discount, sizeof(Discount));{}
			S.Read(OldNomCat, 2); if OldNomCat>0 then NomCat := N2Str(OldNomCat) else NomCat := '';
			DiscountPerc := 0;
			Discount.SetTo(0);
		end;
		3 : begin
			inherited Load(S);
			Total.Load(S);
			VAT.Load(S);
			S.Read(DiscountPerc, sizeof(DiscountPerc));{}
			Discount.Load(S);
			S.Read(NomCat, sizeof(NomCat));
			VATCode := ProgramSetup.Get(siDefaultVAT,'STD');
		end;
		4 : begin
			{added VAT Code & DiscountType}
			inherited Load(S);
			Total.Load(S);
			S.Read(VATCode, sizeof(VATCOde));
			VAT.Load(S);
			S.Read(DiscountPerc, sizeof(DiscountPerc));{}
			Discount.Load(S);
			S.Read(NomCat, sizeof(NomCat));
			S.Read(DiscountType, sizeof(DiscountType));{}
		end;
	else
		{OH DEAR OH DEAR.  Lowest byte gets stored first, so ver now contains, if
		reading the old version, any number as it is the low byte of the disk.next pointer...}
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TInvoiceNode.Load, upv4Inv.pas',mfError,hcNoContext);

		CommonInit;
		{Old chain item version}
		S.Seek(S.GetPos-1);  {Recover from having read ver}
		S.Read(Disk.Next, 4);
		S.Read(Disk.Prev, 4);
		S.Read(Rubbish, 4);  {Was pointer to invoice}
{		Disk.Parent := -1;{}
		Disk.FirstChild := -1;
		v4Date.Load(S);
		Total.SetTo(0);
		VAT.SetTO(0);
		DiscountPerc := 0;{}
		Discount.SetTo(0);
	end;

	CalculateDiscountedTotal;
	CalculateTotalincVAT;{}
end;


procedure TInvoiceNode.CalculateTotals;
begin
  {Once total has been calculated, a call to CalculateTotals will set up
	everything else (Discounted tota, VAT, etc) ready for TInvoiceNode.Edit}
  CalculateDiscountedTotal;

	CalculateVAT;

	CalculateTotalincVAT;
end;

procedure TInvoiceNode.CalculateVAT;
var
	VATSCodeItem : PVATRateScodeItem;
begin
	VATSCodeItem := PVATRateScodeItem(GetSCode(scVATRates, VATCode));

	if VATSCodeItem<>nil then
		VAT.SetTo(DiscountedTotal.inPounds * VATSCodeItem^.Rate/100)
	else
		VAT.SetTo(0); {nothing entered}
end;


procedure TInvoiceNode.CalculateDiscountedTotal;
begin
	if DiscountType then {by percentage}
		Discount.SetTo(Total.inPounds * DiscountPerc / 100)
	else
		if Total.Blank then DiscountPerc := 0 else DiscountPerc := Discount.inPounds/Total.inPounds;

	DiscountedTotal.SetToPence(Total.inPence - Discount.inPence);
end;

procedure TInvoiceNode.CalculateTotalincVAT;
begin
	{Set VAT Rate - VAT always calculated on discounted total}
{	if DiscountedTotal.inPence<>0 then VATRate := (VAT.inPence/DiscountedTotal.inPence)*100 else VATRate := 0;{}

	{Calculate totals.  Stored both with and without discount for period discounts}
	TotalincVAT.SetToPence(Total.inPence + VAT.inPence);
	DiscountedTotalincVAT.SetToPence(DiscountedTotal.inPence + VAT.inPence);
end;

procedure TInvoiceNode.CalculateNodeTotal;
var WorkItem : PInvoiceNode;
begin
	NodeTotal.SetTo(Total.inPounds);
	NodeVAT.SetTo(VAT.inPounds);
	NodeDiscountedTotal.SetTo(DiscountedTotal.inPounds);
	NodeDiscount.SetTo(Discount.inPounds);

	{Total children's node totals -> recursive}
	WorkItem := PInvoiceNode(Heap.FirstChild);
	while WorkItem<>nil do begin
		WorkItem^.CalculateNodeTotal;

		NodeTotal.Add(WorkItem^.NodeTotal);
		NodeVAT.Add(WorkItem^.NodeVAT);
		NodeDiscountedTotal.Add(WorkItem^.NodeDiscountedTotal);
		NodeDiscount.Add(WorkItem^.NodeDiscount);

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

	NodeTotalincVAT.SetToPence(NodeTotal.inPence + NodeVAT.inPence);
	NodeDiscountedTotalincVAT.SetToPence(NodeDiscountedTotal.inPence + NodeVAT.inPence);
end;


{*****************************
 ***  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(v4Date);
		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;

{*****************************
 ***  CODED ITEM           ***
 *****************************}
procedure Tv4CodedInvNode.CommonInit;
begin
	inherited CommonInit;
	New(Comment, init);
end;

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

constructor Tv4CodedInvNode.Load;
var Ver : byte;
		Com : string[20]; {old comment line}
begin
{	CommonInit; done by inherited load}
	S.Read(Ver, 1);

{	Date.Load(S);{}
	inherited Load(S);

	case Ver of
		1 : begin
			Date.SetToDate(v4Date);
			ReadSCode(S, @Code);
			ReadSCLine(S, @CodeLine, 12);
			Quantity := S.ReadFixedStr(5);
			PriceEach.Load(S);  BasePrice.SettoPence(0);
			VAT.Load(S);
			S.Read(NomCat, 2);
			S.Read(Com, sizeof(Com)); LSAppendStr(Comment^.text, Com);
			if S2Real(Quantity) = 0 then begin
				BasePRice.SetToPence(PriceEach.inPence);
				PriceEach.SetToPence(0);
			end;
			CalculateTotals; {calculate old versions only - new ones have totals stored}
		end;
		2 : begin
			{Base price added}
			Date.SetToDate(v4Date);
			ReadSCode(S, @Code);
			ReadSCLine(S, @CodeLine, 12);
			BasePrice.Load(S);
			Quantity := S.ReadFixedStr(5);
			PriceEach.Load(S);
			S.Read(Com, sizeof(Com)); LSAppendStr(Comment^.text, Com);
		end;
		3 : begin
			{Comment made into note form}
			Date.Load(S);
			ReadSCode(S, @Code);
			ReadSCLine(S, @CodeLine, 12);
			BasePrice.Load(S);
			Quantity := S.ReadFixedStr(5);
			PriceEach.Load(S);
			Comment^.Load(S);
		end;
		4 : begin
			{Quantity extended to 10 to cover large units}
			Date.Load(S);
			ReadSCode(S, @Code);
			ReadSCLine(S, @CodeLine, 12);
			BasePrice.Load(S);
			Quantity := S.ReadFixedStr(10);
			PriceEach.Load(S);
			Comment^.Load(S);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'Tv4CodedInvNode.Load, upv4Inv.pas',mfError,hcNoContext);
	end;

	Quantity := DelSpaceR(Quantity); {just for display/etc purposes}
end;

{*****************************
 ***  FREE TEXT NODE       ***
 *****************************}
destructor TFreeTextInvNode.Done;
begin
	dispose(FreeText, done);
	inherited Done;
end;

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

	inherited Load(S);

	New(FreeText, init);

	case Ver of
		1 : begin
			FreeText^.Load(S);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'Tv4FreeTextInvNode.Load, upv4Inv.pas',mfError,hcNoContext);
	end;
end;

{*****************************
 ***  PRODUCT NODE         ***
 *****************************}
constructor TProductInvNode.Load;
var Ver : byte;
begin
	S.Read(Ver, 1);
	Units := '';

	inherited Load(S);

	case Ver of
		1 : begin
			S.Read(ProductID,4);
			Quantity := S.ReadFixedStr(5);
			PriceEach.Load(S);
		end;
		2 : begin
			{Quantity extended to 10}
			S.Read(ProductID,4);
			Quantity := S.ReadFixedStr(10);
			PriceEach.Load(S);
		end;
		3 : begin
			{Units added}
			S.Read(ProductID,4);
			Quantity := S.ReadFixedStr(10);
			Units := S.ReadFixedStr(3);
			PriceEach.Load(S);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TproductInvNode.Load, upv4Inv.pas',mfError,hcNoContext);
		fail;
	end;

	Quantity := delspaceR(Quantity);
end;


{*****************************
 ***  HARDWARE NODE        ***
 *****************************}
constructor THWInvNode.Load;
var Ver : byte;
begin
	S.Read(Ver, 1);

	inherited Load(S);

	case Ver of
		1 : begin
			S.Read(HardwareID,4);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'THWInvNode.Load, upv4Inv.pas',mfError,hcNoContext);
		fail;
	end;
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;
	New(Comment, init);
end;


destructor Tv4Estimate.Done;
begin
	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);

			S.Read(RootNode, 4);

			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);

			S.Read(RootNode, 4);

			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);

			S.Read(RootNode, 4);

			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,hcNoContext);
		fail;
	end;

end;

function Tv4Estimate.RecSize;
begin RecSize := 60; 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);


			S.Read(RootNode, 4);

			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);

			S.Read(RootNode, 4);

			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);

			S.Read(RootNode, 4);

			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,hcNoContext);
		fail;
	end;
end;

procedure Tv4Invoice.CalculateTotals;
begin
	if LastPrint.Blank 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 := 100; end;

function Tv4Invoice.srType;
begin srType := srv4Invoice; 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;

	New(Notes, init);
end;

destructor Tv4Job.Done;
begin
	dispose(Notes, 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}
	LockTerminal := 0;

	case Ver of
		5: begin
      {17/3/94 Changed to v3 type of jobs/invoices/etc - added lists of job items}
			S.Read(LockTerminal, 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;

			S.Read(RootNode, 4);

			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;

			S.Read(RootNode, 4);

			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));

			{Convert old markers to new task items - wastes space if read
			and not saved - such as list displays....}
			ProgramError('UP4INV.PAS - Oh dear, this does get called, sor'' it aat',hcNoContext);
{			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;{}

			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,hcNoContext);
		fail; {return nil pointer}
	end; {case}
end;

{********************************************************
 ***                                                  ***
 ***           Purchase Orders                        ***
 ***                                                  ***
 ********************************************************}
const
	{--- Required for Stream ----}
	Rv4PurchaseOrder : TStreamRec = (
		ObjType : srv4PurchaseOrder;
		VmtLink : Ofs(TypeOf(Tv4PurchaseOrder)^);
		Load : @Tv4PurchaseOrder.Load;
		Store : @Tv4PurchaseOrder.Store
	);

function Tv4PurchaseOrder.RecSize;
begin RecSize := 100; end;

function Tv4PurchaseOrder.srType;
begin srType := srv4PurchaseOrder; end;

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

	New(Comment, init);
end;

destructor Tv4PurchaseOrder.Done;
begin
	dispose(Comment, done);

	inherited Done;
end;

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

begin
	CommonInit;

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

	case ver of
		1 : begin
			S.Read(OurRef, sizeof(OurRef));
			ToREf := '';
			S.Read(ToWho, 4);

			S.Read(RootNode, 4);

			SentDate.Load(S);
			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);
			Due.Load(S);
		end;
		2 : begin
			{added their ref number}
			S.Read(OurRef, sizeof(OurRef));
			S.Read(ToRef, sizeof(ToRef));
			S.Read(ToWho, 4);

			S.Read(RootNode, 4);

			SentDate.Load(S);
			LastPrint.Load(S);

			Comment^.Load(S);

			Total.Load(S);
			VAT.Load(S);
			Due.Load(S);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not understood'#13#10'Tv4PurchaseOrder.Load',mfError,hcNoContext);
		fail; {return nil pointer}
	end;

end;

{********************************************************
 ***                                                  ***
 ***           Packing Slip                           ***
 ***                                                  ***
 ********************************************************}
const
	{--- Required for Stream ----}
	Rv4PackingSlip : TStreamRec = (
		ObjType : srv4PackingSlip;
		VmtLink : Ofs(TypeOf(Tv4PackingSlip)^);
		Load : @Tv4PackingSlip.Load;
		Store : @Tv4PackingSlip.Store
	);

function Tv4PackingSlip.RecSize;
begin RecSize := 100; end;

function Tv4PackingSlip.srType;
begin srType := srv4PackingSlip; end;

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

	New(Comment, init);
end;

destructor Tv4PackingSlip.Done;
begin
	dispose(Comment, done);

	inherited Done;
end;

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

begin
	CommonInit;

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

	case ver of
		1 : begin
			S.Read(Ref, sizeof(Ref));
			S.Read(ToWho, 4);

			S.REad(RootNode, 4);

			SentDate.Load(S);

			Comment^.Load(S);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not understood'#13#10'Tv4PackingSlip.Load',mfError,hcNoContext);
		fail; {return nil pointer}
	end;

end;




BEGIN
	RegisterType(Rv4Invoice);
	RegisterType(Rv4Estimate);
	RegisterType(Rv4Job);
	RegisterType(Rv4PackingSlip);
	RegisterType(Rv4PurchaseOrder);

	RegisterType(RPaymentNode);{}
	RegisterType(Rv4CodedInvNode);{}
	RegisterType(RFreeTextInvNode);{}
	RegisterType(RProductInvNode);{}
	RegisterType(RHWInvNode);{}
end.

