{************************************************************
 ***                                                      ***
 ***                          UPDATE UNIT                 ***
 ***                       v4.0e & v4.1 to v4.2           ***
 ***********************************************************}
{$I compflgs}
{Provides all the old pre-v4.3 objects and routines required to
do the update, and an update procedure

	Does a full jimmy update due to the new letter objects taking up more space.
	Combines Directory Categories into Directory Search Codes.
	Converts directory entries to new addressing system.
{}

unit upfrom4;

interface

procedure UpdateFrom40;

implementation


uses
{$IFDEF Windows}
			winmsgs,
{$ELSE}
			tuimsgs, tuiapp,
{$ENDIF}
			fixit, {for jimmy file methods}
			update,

			indexes,
			scodes,
			kbooks,{}
			address, country,
			kdirctry,
			kusers,
			files,
			strings,
			vat,
			multcurr,
			tasks,
			dosutils,
			kamsetup,
			devices, printers,
			minilib,
			menus, tui, help, views,
			kcompany, kperson,
			kmemship,

			kestimat, kinvoice,
			kforsale,
			kstroms,
			kporder,

			idindex,

		indxutil,
		jimhooks,
		jimmys,
		status,

		alljimmy,
		notes,
		objects,

		upv4objs,
		upv4inv,

		dbg,

		kletter,{}
		kqnote,{}
		kdiary,{}
		kstock,
		kcompsup,
		kMNote,
		smoney,
			ordproc,
			kjob,
			dattime, global;



type
	{========= OLD COSTED SCODES ==================}
	PSimpleCostedScodeItem = ^TSimplecostedscodeitem;
	Tsimplecostedscodeitem = object(TSCodeItem)
		Cost : TSimpleMoney;
		constructor Load(var S : TDataStream);
	end;


constructor TSimpleCostedSCodeItem.Load;
begin inherited Load(S);  Cost.Load(S); end;

const
 RSimpleCostedSCode : TStreamRec = (
	 ObjType : srSimpleCostedSCode;
	 VmtLink : Ofs(TypeOf(TSimpleCostedSCodeItem)^);
	 Load : @TSimpleCostedSCodeItem.Load;
	 Store : @TSimpleCostedSCodeItem.Store
 );


procedure v41JimmyConverter(var Jimmy : PJimmy; var RecSize : word); far;
var Letter : PLetter;{}
		HistoryNote : PHistoryNote;{}
		AltAddress : PADdress;{}
		Person : PPerson;
		Company : PCompany;
		MemberShip : PMembership;
		Job : PJob;
		Estimate : PEStimate;
		Invoice : PINvoice;
		StromsGoods : PStromsGoods;
		POrder : PPorder; {purchase order}
		PakSlip : PPackingSlip;

		WPTextFile, TextFile : PTextStream;
		Hook : PHook;{}
		HookID, NewTextRec, TextRec : longint;{}
		TextItem : PTextItem;{}
		OldLine, NewLine : integer;
		I,Un,Ba : integer;
		S : string;
		L : longint;
		telNo : array[1..4] of string;

		LatestDate : TDate;
		v4Job : Pv4Job;
		Hardware : PHardware;

	procedure NewHook(HJimmy, ParentJimmy : PJimmy; var HeadID :longint; htType : byte);
	var Hook : PHook;
			hkType : byte;
			Key : longint;
			HookToID,SubHookToID : PLongint;
			InsBias : boolean;

	begin
		FileADmin(fiHooks)^.LogOn;
		HJimmy^.GetHookTo(htType, HookToID, SubHookToID, hkType, Key, InsBias);

		New(Hook, init);
		Hook^.SortKey := Key;
		Hook^.OwnerID := ParentJimmy^.RecNo;
		Hook^.srType 	:= HJimmy^.srtype;
		Hook^.JimmyID := HJimmy^.RecNo;
		HookFile^.Insert(HeadID, Hook, InsBias);
		dispose(Hook, done);
		FileADmin(fiHooks)^.LogOff;
	end;

begin
{	if typeof(Jimmy^)<>typeof(Tv4Product) then begin
		Jimmy^.Deleted := true;
	end;{}
	if Jimmy^.Deleted then begin
		dispose(Jimmy, done);
		Jimmy := nil;
		exit;
	end;

	if typeof(Jimmy^)=typeof(THardware) then RecSize := 350;
	if typeof(Jimmy^)=typeof(TSoftware) then RecSize := 350;
	if typeof(Jimmy^)=typeof(THistoryNote) then RecSize := 40; {now 50}

	{=============== LETTERS ============================}
	if (typeof(Jimmy^)=typeof(TEDLtr)) or (typeof(Jimmy^)=typeof(TWPLtr))
			or (typeof(Jimmy^)=typeof(TStdEdLetter)) or (typeof(Jimmy^)=typeof(TStdWPLetter)) then begin

		if (Pv4Letter(Jimmy)^.ToWho = -1) then begin
			dispose(Jimmy, done);
			Jimmy := nil;
			exit; {don't bother}
		end;
		{special case of sbs database, rubbish pointing to 0}
{		if (Pv4Letter(Jimmy)^.ToWho = 0) then begin
			dispose(Jimmy, done);
			Jimmy := nil;
			exit; {don't bother}
{		end;{}

		ThinkingOn('Converting letter');
		Debug.Write('Converting letter ID'+N2Str(Jimmy^.RecNo)+'...');

		New(Letter, init(nil));
		Letter^.ToWho := Pv4Letter(Jimmy)^.ToWho;
{		Person := PPerson(GetJimmy(Letter^.ToWho)); hmmm, can't load from old file as the srtype has been overwritten}
{		if Person<>nil then begin
			Letter^.ToCoy := Person^.ContactFor;
			Letter^.ToAdd := Person^.AddressID;
			dispose(Person, done);
		end;{}
		Letter^.ByWho := Pv4Letter(Jimmy)^.ByWho;
		Letter^.ReWho := Pv4Letter(Jimmy)^.ReWho;
		Letter^.Date.SetToDate(Pv4Letter(Jimmy)^.Date);
		Letter^.DateSent.SetToDate(Pv4Letter(Jimmy)^.Date);
		Letter^.Codes := Pv4Letter(Jimmy)^.Codes;
		Letter^.Ref := Pv4Letter(Jimmy)^.Ref;
		Letter^.Header := Pv4Letter(Jimmy)^.Header;
		if Pv4Letter(Jimmy)^.Date.Blank then Letter^.Status := leEdited else Letter^.Status := leSent;
		Letter^.TextLoaded := False;
		Letter^.RecNo := Jimmy^.RecNo;

		if (typeof(Jimmy^)=typeof(TEDLtr)) or (typeof(JImmy^)=typeof(TStdEdLetter)) then begin
			Letter^.EditorType := edInternal;
			if typeof(Jimmy^)=typeof(TEDLtr) then
				Letter^.TextBody := PEdLtr(Jimmy)^.Body^.First
			else
				Letter^.StdForm := Letter^.Header;
		end else begin
			Debug.Write('...Moving WP...');
			{wp letter - have to copy text chain from wpltrs.dat}
			Letter^.EditorType := edWP51;
			if (typeof(JImmy^)=typeof(TStdWPLetter)) then
				Letter^.StdForm := Letter^.Header
			else begin
				TextRec := PWPLtr(Jimmy)^.FirstTextRec;
				if TextRec<>-1 then begin
					New(TextFile, init('LETTERS.DAT', TLetterItemSize, StreamBufSize));
					New(WPTextFile, init('WPLTRS.DAT', TLEtterItemSize, StreamBufSize));
					Letter^.TextBody := TextFile^.NoRecs; {set to end of file}
					NewTextRec := TextFile^.NoRecs;
					while TextRec<>-1 do begin
						TextItem := PTextItem(WPTextFile^.GetNext(TextRec));
						if TextItem<>nil then begin
							TextFile^.PutNext(NewTextRec, TextItem, (TextRec<>-1));
							dispose(TextItem, done)
						end else begin
							Debug.Write('...WP Text item nil pre '+N2Str(TextRec)+', terminating...');
							TextRec := -1;
						end;
					end;
					dispose(TextFile, done);
					dispose(WPTextFile, done);
				end;
			end;
		end;

		Debug.Writeln('...done');
		dispose(Jimmy, done);
		Jimmy := Letter;
		ThinkingOff;
	end;{}

	{================== DIARY EVENTS TO HISTORY NOTES =====================}

	{convert diary events, which were used for a while as history notes, to
		history notes.  There has been no proper diary pre v4.1, so don't worry
		about index pointers, etc}
	if typeof(Jimmy^)=typeof(TDIaryEvent) then begin

		{special case of sbs database, rubbish pointing to 0}
		if (PDiaryEvent(Jimmy)^.ForWho = 0) then begin
			dispose(Jimmy, done);
			Jimmy := nil;
			exit; {don't bother}
		end;

		Debug.Write('Converting Diary Event -> History Note ID'+N2Str(Jimmy^.RecNo)+'...');
		New(HistoryNote, init(nil));
		with HistoryNote^ do begin
			ForWho := PDiaryEvent(Jimmy)^.ForWho;
			ByWho 	:= PDiaryEvent(Jimmy)^.ByWho;
			Date.SetToDate(PDiaryEvent(Jimmy)^.Date);
			Code := PDiaryEvent(Jimmy)^.What;
			Outcome := PDiaryEvent(Jimmy)^.Outcome;

			dispose(Notes, done);
			Notes := PDiaryEvent(Jimmy)^.Notes;
			New(PDiaryEvent(Jimmy)^.Notes, init);

			RecNo := Jimmy^.RecNo;
		end;

		Debug.Writeln('...done');
		dispose(Jimmy, done);
		Jimmy := HistoryNote;
	end;{}

	{==================== HARDWARE =====================}
	{Just a check for obsolete people for NSS}
	if typeof(Jimmy^)=typeof(THardware) then begin

		{owner deleted/whatever}
		if (PHardware(Jimmy)^.ForWho = -1) or (GetNewIDPtr(PHardware(Jimmy)^.ForWho)=-1) then begin
			Debug.Write('Hardware ID'+N2Str(Jimmy^.RecNo)+' disposing');{}
			dispose(Jimmy, done);
			Jimmy := nil;
			exit; {don't bother}
		end else begin
			Debug.Write('Hardware ID'+N2Str(Jimmy^.RecNo));{}
		end;
	end;


	{================== JOB SHEETS =====================}
	{one of the old estimate/invoice/job lot that used a separate tree for
	the items....}
	if typeof(Jimmy^)=typeof(Tv4Job) then begin

		{owner deleted/whatever}
		if (Pv4Job(Jimmy)^.ForWho = -1) or (GetNewIDPtr(Pv4Job(Jimmy)^.ForWho)=-1) then begin
			dispose(Jimmy, done);
			Jimmy := nil;
			exit; {don't bother}
		end;

		New(Job, init(nil));
		Debug.Write('Converting Job sheet ID'+N2Str(Jimmy^.RecNo));{}
		with Job^ do begin
			Date.SetToDate(PV4Job(Jimmy)^.CallDate);
			LastPrint.SetToDate(Date);
			ForWho := Pv4Job(Jimmy)^.ForWho;
			REf := S2Num(TrimToNum(Pv4Job(Jimmy)^.OurRef));
			OldRef := -1;

			ByWho 	:= Pv4Job(Jimmy)^.ByWho;
			ForWhoRef := NewStr(Pv4Job(Jimmy)^.CustRef);

			VisitDate.SetToDate(Pv4Job(Jimmy)^.VisitDate);
			VisitTime := Pv4Job(Jimmy)^.VisitTime;

			dispose(Notes, done);
			Notes := Pv4Job(Jimmy)^.Notes;
			New(Pv4Job(Jimmy)^.Notes, init);

			Outcome := Pv4Job(Jimmy)^.Outcome;
			Invoiced := Pv4Job(Jimmy)^.Invoiced;

			RecNo := Jimmy^.RecNo;
			SetIDPtr(srJobsheet, Ref, RecNo); {ignore no ref}

			{hmmm.... load tree...}
			PutJimmy(Job);
			JimmyfyInvoiceNodes(Pv4Job(Jimmy)^.RootNode, Jimmy^.RecNo, Ptr2Items,L);{}
		end;

		Debug.Writeln('...done');{}
		dispose(Jimmy, done);
		Jimmy := Job;
	end;{}

	{================== PURCHASE ORDERS =====================}
	{one of the old estimate/invoice/job lot that used a separate tree for
	the items....}
	if typeof(Jimmy^)=typeof(Tv4PurchaseOrder) then begin

		New(POrder, init(nil));
		Debug.Write('Converting Purchase Order ID'+N2Str(Jimmy^.RecNo));
		with Porder^ do begin
			LastPrint.SetToDate(Pv4PurchaseOrder(Jimmy)^.LastPrint);
			if not LastPrint.Blank then State := State or osSent;

			with TotallerGroup do begin
				SubTotal.Value := Pv4PurchaseOrder(Jimmy)^.Total.Value;
				VAT.VAlue := Pv4PurchaseOrder(Jimmy)^.VAT.Value;
				Total.Value := VAT.VAlue + SubTotal.Value;
			end;

			Ref				:= S2Num(Pv4PurchaseOrder(Jimmy)^.OurRef);
			OldRef := -1;
			Date.Settodate(Pv4PurchaseOrder(Jimmy)^.Sentdate);
			ForWho    := Pv4PurchaseOrder(Jimmy)^.ToWho;
			ForWhoRef	:= NewStr(Pv4PurchaseOrder(Jimmy)^.toRef);

			dispose(Notes, done);
			Notes := Pv4PurchaseOrder(Jimmy)^.Comment;
			New(Pv4PurchaseOrder(Jimmy)^.Comment, init);

			RecNo := Jimmy^.RecNo;
			SetIDPtr(srPurchaseOrder, Ref, RecNo); {ignore no ref}

			{hmmm.... load tree...}
			PutJimmy(POrder);
			JimmyfyInvoiceNodes(Pv4PurchaseOrder(Jimmy)^.RootNode, Jimmy^.RecNo, Ptr2Items,L);
		end;

		Debug.Writeln('...done');
		dispose(Jimmy, done);
		Jimmy := POrder;
	end;{}

	{================== PACKING SLIP =====================}
	{one of the old estimate/invoice/job lot that used a separate tree for
	the items....}
	if typeof(Jimmy^)=typeof(Tv4PackingSlip) then begin

		New(PakSlip, init(nil));
{		Debug.Write('Converting Packing Slip ID'+N2Str(Jimmy^.RecNo));{}
		with PakSlip^ do begin
			LastPrint.SetToDate(Pv4PackingSlip(Jimmy)^.SentDate);
			if not LastPrint.Blank then State := State or osSent;

			Ref				:= S2Num(TrimToNum(Pv4PackingSlip(Jimmy)^.Ref));
			OldRef := -1;
			Date.Settodate(Pv4PackingSlip(Jimmy)^.Sentdate);
			ForWho    := Pv4PackingSlip(Jimmy)^.ToWho;

			dispose(Notes, done);
			Notes := Pv4PackingSlip(Jimmy)^.Comment;
			New(Pv4PackingSlip(Jimmy)^.Comment, init);

			RecNo := Jimmy^.RecNo;
			if Ref<>0 then SetIDPtr(srPackingSlip, Ref, RecNo); {ignore no ref}

			{hmmm.... load tree...}
			PutJimmy(PakSlip);
			JimmyfyInvoiceNodes(Pv4PackingSlip(Jimmy)^.RootNode, Jimmy^.RecNo, Ptr2Items,L);
		end;

{		Debug.Writeln('...done');{}
		dispose(Jimmy, done);
		Jimmy := PakSlip;
	end;{}

	{===================== ESTIMATE ==================}
	if (typeof(Jimmy^)=typeof(Tv4Estimate)) then begin
		New(Estimate, init(nil));
		Debug.Write('Converting Estimate ID'+N2Str(Jimmy^.RecNo));
		with Estimate^ do begin
			Date.SetToDate(PV4Estimate(Jimmy)^.LastPrint);
			LastPrint.SetToDate(Date);
			if not LastPrint.Blank then State := State or osSent;
			ForWho := Pv4Estimate(Jimmy)^.ToWho;
			REf := S2Num(TrimToNum(Pv4Estimate(Jimmy)^.Ref));
			OldRef := -1;

			dispose(Notes, done);
			Notes := Pv4Estimate(Jimmy)^.Comment;
			New(Pv4Estimate(Jimmy)^.Comment, init);

			with TotallerGroup do begin
				{set multicurrency from simple money...}
				SubTotal.Value := Pv4Estimate(Jimmy)^.Total.Value;
				VAT.Value := Pv4Estimate(Jimmy)^.VAT.Value;
				Total.Value := VAT.VAlue + SubTotal.Value;
			end;

			RecNo := Jimmy^.RecNo;
			SetIDPtr(srEstimate, Ref, RecNo); {ignore no ref}

			{hmm.... load tree...}
{			Debug.Write('Estimate convertion #'+N2Str(RecNo)+' Ptr2Items='+N2Str(Ptr2Items),'');{}
			PutJimmy(Estimate); {give items a proper jimmy to hook to}
			JimmyfyInvoiceNodes(Pv4Estimate(Jimmy)^.RootNode, Jimmy^.RecNo, Ptr2Items,L);
{			Recalculate;will be done by reconstruct}
		end;

		Debug.Writeln('...done');
		dispose(Jimmy, done);
		Jimmy := Estimate;
	end;

	{================== INVOICE =====================}
	if (typeof(Jimmy^)=typeof(Tv4Invoice)) then begin
		New(Invoice, init(nil));
{		Debug.Write('Converting Invoice ID'+N2Str(Jimmy^.RecNo));{}
		with Invoice^ do begin
			Date.SetToDate(PV4Invoice(Jimmy)^.LastPrint);
			LastPrint.SetToDate(Date);
			if not LastPrint.Blank then State := State or osSent;
			ForWho := Pv4Invoice(Jimmy)^.ToWho;
			REf := S2Num(TrimToNum(Pv4Invoice(Jimmy)^.Ref));
			DaysDiscount := Pv4Invoice(Jimmy)^.DaysDiscount;
			OldRef := -1;

			dispose(Notes, done);
			Notes := Pv4Invoice(Jimmy)^.Comment;
			New(Pv4Invoice(Jimmy)^.Comment, init);

			with TotallerGroup do begin
				{set multicurrency from simple money...}
				SubTotal.Value := Pv4Invoice(Jimmy)^.Total.Value;
				VAT.Value := Pv4Invoice(Jimmy)^.VAT.Value;
				Total.Value := VAT.VAlue + SubTotal.Value;
			end;

			if LastPrint.Blank then Due.Clear else Due.Value := Pv4Invoice(Jimmy)^.Due.Value;

			RecNo := Jimmy^.RecNo;
{will be done in reconstruct			SetIDPtr(srInvoice, Ref, RecNo); {ignore no ref}

			{hmm.... load tree...}
			PutJimmy(Invoice);
			JimmyfyInvoiceNodes(Pv4Invoice(Jimmy)^.RootNode, Jimmy^.RecNo, Ptr2Items,Ptr2Payments);
{			Recalculate; will be done by reconstruct}
		end;

{		Debug.Writeln('...done');{}
		dispose(Jimmy, done);
		Jimmy := Invoice;
	end;

	{================== PRODUCT =====================}
	if (typeof(Jimmy^)=typeof(Tv4Product)) then begin
		New(StromsGoods, init);
{		Debug.Write('Converting Goods ID'+N2Str(Jimmy^.RecNo));{}
		with StromsGoods^ do begin
			Categories := Pv4Product(Jimmy)^.Categories;
			Desc := Pv4Product(Jimmy)^.Desc;
			Code := Pv4Product(Jimmy)^.Code;

			SupplierID := Pv4Product(Jimmy)^.Supplier;

			{set units/etc table}
			for Un := 0 to 3 do begin
				Units[Un] 			:= Pv4Product(Jimmy)^.Units[Un];
				QuotedCost[Un].Init;
				QuotedCost[Un].Value := Pv4Product(Jimmy)^.QuotedCost[Un].inPence;

				for Ba := 1 to 3 do begin
					PriceBand[Ba].Band := 		Pv4Product(Jimmy)^.PriceBand[Ba].Band;
					PriceBand[Ba].Price[Un].Init;
					PriceBand[Ba].Price[Un].Value := Pv4Product(Jimmy)^.PriceBand[Ba].Price[Un].inPence;
				end;
			end;

			RecNo := Jimmy^.RecNo;
		end;

		dispose(Jimmy, done);
		Jimmy := StromsGoods;
	end;


	{============= MORE-ABOUT ADDRESSES TO ALT ADDRESSES ===============}
	if typeof(Jimmy^)=typeof(TAltAddress) then begin
		{---- Alternative addresses ----------}
		New(AltAddress, init(nil));
{		Debug.Write('Converting Address ID'+N2Str(Jimmy^.RecNo));{}
		with AltAddress^ do begin
			ForWho := PAltAddress(Jimmy)^.WhoFor;
			ForDates.Start.SetToDate(PalTAddress(Jimmy)^.DateRange.Start);
			ForDates.Finish.SetToDate(PalTAddress(Jimmy)^.DateRange.FInish);

			apType := PAltAddress(Jimmy)^.apType;
			adType := adUK;

			Country := PAltAddress(JImmy)^.Country;

			OldLine := 1; NewLine := 1;
			for OldLine :=1 to 7 do
				if PAltAddress(Jimmy)^.AddressLine[OldLine]<>'' then begin
					Line[NewLine] := NewStr(PAltAddress(Jimmy)^.AddressLine[OldLine]);
					inc(NewLine);
				end;

			Line[NewLine] := NewStr(PAltAddress(Jimmy)^.Postcode);
			RecNo := Jimmy^.RecNo;
		end;

{		Debug.Writeln('...done');{}
		dispose(Jimmy, done);
		Jimmy := AltAddress;
	end;

	{================ PERSONS ==================================}
	if typeof(Jimmy^)=typeof(Tv4Person) then begin

		{NSS - delete old entries}
{		FileAdmin(fiHooks)^.LogOn;
		LatestDate.SetToDate(Pv4Person(Jimmy)^.DOReg);
		HookID := Pv4Person(Jimmy)^.Ptr2More;
		Hook := PHook(HookFile^.GetAt(HookID));
		while Hook<>nil do begin
			if Hook^.srtype = srHardware then begin
				Hardware := PHardWare(JimmyStream^.GetAt(Hook^.JimmyID));
				if Hardware=nil then begin
					Debug.Write('ERROR: Could not retrieve H/W ('
											+N2Str(JimmyStream^.Status)+'/'+N2Str(JimmyStream^.ErrorInfo)+')');
					JimmyStream^.Reset;
				end else begin
					if Hardware^.DOExpiry.days>LatestDate.Days then LatestDate.SetToDate(Hardware^.DOExpiry);
					dispose(Hardware, done);
				end;
			end;
			HooKID := Hook^.NextID;
			dispose(Hook, done);
			Hook := PHook(HookFile^.GetAt(HookID));
		end;

		{check just the first (ie latest) entry of the history}
{		HookID := Pv4Person(Jimmy)^.Ptr2History;
		Hook := PHook(HookFile^.GetAt(HookID));
		if Hook<>nil then begin
			if Hook^.srtype = srv4Job then begin
				v4Job := Pv4Job(JimmyStream^.GetAt(Hook^.JimmyID));
				if v4Job=nil then begin
					Debug.Write('ERROR: Could not retrieve v4Job ('
											+N2Str(JimmyStream^.Status)+'/'+N2Str(JimmyStream^.ErrorInfo)+')');
					Jimmystream^.Reset;
				end else begin
					if v4Job^.VisitDate.days>LatestDate.Days then LatestDate.SetToDate(v4Job^.VisitDate);
					dispose(v4Job, done);
				end;
			end;
			dispose(Hook,done);
		end;
		FileAdmin(fiHooks)^.LogOff;

		if LatestDate.Year<1995 then begin
			Debug.Write('..losing Person..');
			dispose(Jimmy, done);
			Jimmy := nil;
			exit;
		end;{}


		{---- New Person structure -----}
		New(Person, init(nil));
{		Debug.Write('Converting Person ID'+N2Str(Jimmy^.RecNo));{}
		with Person^ do begin

			DOReg.SetToDate(Pv4Person(Jimmy)^.DOReg);
			Ptr2History 	:= Pv4Person(Jimmy)^.Ptr2History;
			Ptr2More 			:= Pv4Person(Jimmy)^.Ptr2More;
			Ptr2Addresses := -1;

			Dat2Idx 			:= Pv4Person(Jimmy)^.Dat2Idx;
			AliasIdx 			:=  Pv4Person(Jimmy)^.AliasIDx;
			ArchiveIdx 		:= Pv4Person(Jimmy)^.ArchiveIdx;
			for I := 1 to 5 do CategoryIdx[I] := -1;
			AddressID 		:= -1;

			Surname := Pv4Person(Jimmy)^.Surname;
			ForName := Pv4Person(Jimmy)^.ForName;
			Title   := Pv4Person(Jimmy)^.Title;
			DearName:= Pv4Person(Jimmy)^.DearName;

			ContactFor 	:= Pv4Person(Jimmy)^.ContactFor;
			JobTitle 		:= Pv4Person(Jimmy)^.AddressLine[1];
			Dept     		:= Pv4Person(Jimmy)^.AddressLine[2];

			for I := 1 to 4 do TelNo[I] := Pv4Person(Jimmy)^.TelNo[I];

{			Ptr2Inv := Pv4Person(Jimmy)^.Ptr2Inv;           {Pointer to invoice to}
			RefID := Pv4Person(Jimmy)^.Ptr2Ref;           {Pointer to referrer}

			dispose(Comment, done);
			Comment := Pv4Person(Jimmy)^.COmment;
			New(Pv4Person(Jimmy)^.Comment, init);

			CategoryCodes  := Pv4Person(Jimmy)^.CategoryCodes;

			FileADmin(fiHooks)^.LogOn;

			{create alternative addresses & put at end of jimmy file - pointers will be converted as with all others}
			New(ADdress, init(nil));
			Address^.ForWho :=Pv4Person(Jimmy)^.RecNo;
			Address^.apType := apAny;
			Address^.adType := adUK;

			for I := 3 to 7 do Address^.Line[I-2] := NewStr(Pv4Person(Jimmy)^.AddressLine[I]);
			Address^.Line[6] :=NewStr(Pv4Person(Jimmy)^.Postcode);
			Address^.Country := Pv4Person(Jimmy)^.Country;

			{add telephone numbers}
			L := 1;
			for I := 1 to 4 do begin
				if (pos('FAX',TelNo[I])>0) or (L=4) then
					Address^.Fax := NewStr(TelNo[I])
				else
					if TelNo[I]<>'' then begin
						Address^.Tel[L] := NewStr(TelNo[I]);
						inc(L);
					end;
			end;

			if not Address^.Blank then begin
				PutJimmy(Address); {gets added to the end of the old jimmy stream}
				AddressID := Address^.RecNo;

				{hook address}
				newHook(Address, Person, Person^.Ptr2Addresses,1 );
			end;
			dispose(Address, done);
			Address := nil;

			{add email to address hooks}
			if delspaceR(Pv4Person(Jimmy)^.email)<>'' then begin
				New(Address, init(nil));
				Address^.ForWho := Jimmy^.RecNo;
				Address^.apType := apAny;
				Address^.adType := ademail;

				S := Pv4Person(Jimmy)^.email;

				Address^.Line[1] := NewStr(Copy(S,1,pos('@',S+'@')));
				Address^.Line[2] := NewStr(Copy(S,pos('@',S+'@'), length(S)));

				PutJimmy(Address); {gets added to the end of the old jimmy stream}

				newHook(Address, Person, Person^.Ptr2Addresses, 1);

				dispose(Address, done);
				Address := nil;
			end;

			FileAdmin(fiHooks)^.LogOff;

		end;
{		Debug.Writeln('...done');{}
		dispose(JImmy, done);
		Jimmy := Person;
	end;

	{===================== COMPANIES ======================}
	if typeof(Jimmy^)=typeof(Tv4Company) then begin
		{---- New Company structure -----}
{		Debug.Write('Converting Company ID'+N2Str(Jimmy^.RecNo));{}
		New(Company, init(nil));
		with Company^ do begin

			DOReg.SetToDate(Pv4Company(Jimmy)^.DOReg);
			Ptr2History := Pv4Company(Jimmy)^.Ptr2History;
			Ptr2More :=    Pv4Company(Jimmy)^.Ptr2More;
			Ptr2Addresses := -1;

			Dat2Idx := Pv4Company(Jimmy)^.Dat2Idx;
			AliasIdx :=  Pv4Company(Jimmy)^.AliasIDx;
			ArchiveIdx := Pv4Company(Jimmy)^.ArchiveIdx;
			for I := 1 to 5 do CategoryIdx[I] := -1;
			AddressID := -1;

			CompanyName := Pv4Company(Jimmy)^.CompanyName;
			Alias := Pv4COmpany(Jimmy)^.Alias;

			for I := 1 to 4 do TelNo[I] := Pv4Company(Jimmy)^.TelNo[I];


{			Ptr2Inv := Pv4Company(Jimmy)^.Ptr2Inv;           {Pointer to invoice to}
			RefID := Pv4Company(Jimmy)^.Ptr2Ref;           {Pointer to referrer}

			dispose(Comment, done);
			Comment := Pv4Company(Jimmy)^.COmment;
			New(Pv4Company(Jimmy)^.Comment, init);

			CategoryCodes  := Pv4Company(Jimmy)^.CategoryCodes;

			FileADmin(fiHooks)^.LogOn;

			{create alternative addresses & put at end of jimmy file - pointers will be converted as with all others}
			New(Address, init(nil));
			Address^.ForWho := Jimmy^.RecNo;
			Address^.apType := apAny;
			Address^.adType := adUK;

			for I := 1 to 5 do Address^.Line[I] := NewStr(Pv4Company(Jimmy)^.AddressLine[I]);
			Address^.Line[6] :=NewStr(Pv4Company(Jimmy)^.Postcode);
			Address^.Country := Pv4Company(Jimmy)^.Country;

			{add telephone numbers}
			L := 1;
			for I := 1 to 4 do begin
				if (pos('FAX',TelNo[I])>0) or (L>3) then
					Address^.Fax := NewStr(TelNo[I])
				else
					if TelNo[I]<>'' then begin
						Address^.Tel[L] := NewStr(TelNo[I]);
						inc(L);
					end;
			end;

			if not Address^.Blank then begin
				PutJimmy(Address); {gets added to the end of the old jimmy stream}
				AddressID := Address^.RecNo;

				newHook(Address, Company, Company^.Ptr2Addresses, 1);
			end;
			dispose(Address, done);
			Address := nil;

			{change contact pointer array to hooks}
			for I := 1 to 4 do begin
				if Pv4Company(Jimmy)^.Contact[I]<>-1 then begin
					New(Hook, init);
					Hook^.SortKey := 0;
					Hook^.OwnerID := Jimmy^.RecNo;
					Hook^.srType := srPerson;
					Hook^.JimmyID := Pv4Company(Jimmy)^.Contact[I];
					HookFile^.Insert(Ptr2Contacts, Hook, biEnd);
					dispose(Hook, done);{}
				end;
			end;

			{add email to address hooks}
			if delspaceR(Pv4Company(Jimmy)^.email)<>'' then begin
				New(Address, init(nil));
				Address^.ForWho := Jimmy^.RecNo;
				Address^.apType := apAny;
				Address^.adType := ademail;

				S := Pv4Company(Jimmy)^.email;

				Address^.Line[1] := NewStr(Copy(S,1,pos('@',S+'@')));
				Address^.Line[2] := NewStr(Copy(S,pos('@',S+'@'), length(S)));

				PutJimmy(Address); {gets added to the end of the old jimmy stream}

				newHook(Address, Company, Ptr2Addresses, 1);
				dispose(Address, done);
				Address := nil;
			end;

			FileAdmin(fiHooks)^.LogOff;
		end;

{		Debug.Writeln('...done');{}
		dispose(Jimmy, done);
		Jimmy := Company;
	end; {company}

	{================ MEMBERSHIP ==================================}
{no longer exists	if typeof(Jimmy^)=typeof(Tv4Member) then begin
		{---- New Member structure - Person/Membership split -----}
{		New(Person, init(nil));
		DebugNote('Converting Member ID'+N2Str(Jimmy^.RecNo));
		with Person^ do begin

			DOReg.SetToDate(Pv4Member(Jimmy)^.DOReg);
			Ptr2History 	:= Pv4Member(Jimmy)^.Ptr2History;
			Ptr2More 			:= Pv4Member(Jimmy)^.Ptr2More;
			Ptr2Addresses := -1;

			Dat2Idx 			:= Pv4Member(Jimmy)^.Dat2Idx;
			AliasIdx 			:=  Pv4Member(Jimmy)^.AliasIDx;
			ArchiveIdx 		:= Pv4Member(Jimmy)^.ArchiveIdx;
			for I := 1 to 5 do CategoryIdx[I] := -1;
			AddressID 		:= -1;

			Surname := Pv4Member(Jimmy)^.Surname;
			ForName := Pv4Member(Jimmy)^.ForName;
			Title   := Pv4Member(Jimmy)^.Title;
			DearName:= Pv4Member(Jimmy)^.DearName;

			for I := 1 to 2 do TelNo[I] := Pv4Member(Jimmy)^.TelNo[I];
			TelNo[4] := Pv4Member(Jimmy)^.TelNo[3]; {cons clubs card number}

{			Ptr2Inv := Pv4Member(Jimmy)^.Ptr2Inv;           {Pointer to invoice to}
{			Ptr2Ref := Pv4Member(Jimmy)^.Ptr2Ref;           {Pointer to referrer}

{			CategoryCodes  := Pv4Member(Jimmy)^.SearchCodes;
		end; {with person^}

{		FileADmin(fiHooks)^.LogOn;

		{create alternative addresses & put at end of jimmy file - pointers will be converted as with all others}
{		New(ADdress, init(nil));
		Address^.ForWho :=Pv4Member(Jimmy)^.RecNo;
		Address^.apType := apAny;
		Address^.adType := adUK;

		for I := 1 to 3 do Address^.Line[I] := NewStr(Pv4Member(Jimmy)^.AddressLine[I]);
		for I := 4 to 5 do Address^.Line[I] := NewStr(Pv4Member(Jimmy)^.AddressLine[I+1]);
		Address^.Line[6] :=NewStr(Pv4Member(Jimmy)^.Postcode);
		Address^.Country := Pv4Member(Jimmy)^.Country;

		if not Address^.Blank then begin
			PutJimmy(Address); {gets added to the end of the old jimmy stream}
{			Person^.AddressID := Address^.RecNo;

			newHook(Address, Person, Person^.Ptr2Addresses,1 );
		end;
		dispose(Address, done);

		{create membership details}
{		New(Membership, init(nil));
		with Membership^ do begin
			ForWho := Pv4Member(jimmy)^.RecNo;
			MemNum := Pv4Member(jimmy)^.MemNum;{}
{			if MemNum='' then
				MemNum := Pv4Member(jimmy)^.TelNo[3];{conservative club}
{			Category := Pv4Member(jimmy)^.CategoryCode;
			DOJoining.SetToDate(Pv4Member(jimmy)^.DOJoining);
			DOExpiry.SetToDate(Pv4Member(jimmy)^.DOExpiry);
		end;

		PutJimmy(Membership);
		NewHook(Membership, Person, Person^.Ptr2More, 1);
		dispose(Membership, done);

		FileAdmin(fiHooks)^.LogOff;

		dispose(JImmy, done);
		Jimmy := Person;
	end;{}


end;

{**************************************
 **         CONVERT DATABASE        ***
 **************************************}
procedure UpdateFrom40;
var	CatIdxRec, IdxRec : longint;
		FullDirIndexItem, CatDirIndexItem : PIndexItem;
		Jimmy : PJimmy;
		ProBox : PProgressBox;
		S : string;
		Cat : byte;
		User : PUser;
		I : integer;
		NewSCode, Scode : PSCodeItem;
		Ver : TVer;

begin
	ThinkingOn('Updating From ~4.0');

	{---- New status/anti piracy -----}
	ProgramStatus.SetMaxUsers(1);
	Today.SetToToday; ProgramStatus.SetTrialStart(Today);
	{ought now to check for old anti-piracy file and delete/update
		status and setup as nec}

	{---- CURRENCIES -----------------}
	ThinkingOn('Creating currency Codes');
	with ScodeCollection[scCurrency]^ do begin
		LogOn;
		Insert(New(PCurrencyScode, init('STE',	'Sterling Pounds',	156, #156, False)));
		Insert(New(PCurrencyScode, init('DOL',	'American Dollars',	36, #36, False)));
		Insert(New(PCurrencyScode, init('KSH',	'Kenyan Shillings',	 75, 'KShs', False)));
		Insert(New(PCurrencyScode, init('DM',		'Deutsch Marks',			68, 'DM', False)));
		Insert(New(PCurrencyScode, init('FF',		'French Francs',	 	70, 'FF', False)));
		StoreCodes(lkOff);
		LogOff;
	end;
	ThinkingOff;

	DeleteFile(DataPAth+'DIARY.IDX'); {no events are kept from v4.1}
	DeleteFile(DataPath+'*.FIL'); {delete old filters - no longer valid}
	DeleteFile(DataPath+'*.PFM'); {delete old paper form defs - no longer valid}
	DeleteFile(DataPath+'*.LFM'); {delete old label form defs - no longer valid}

	{renaming...}
	CopyFile(DataPath+'PRODCAT.IDX', DataPath+'FORSALE.IDX');	DeleteFile(DataPath+'KDIRCAT.IDX');
	CopyFile(DataPath+'PRODCODE.IDX', DataPath+'GOODSREF.IDX');	DeleteFile(DataPath+'KDIRCAT.IDX');

	{--- Run through Jimmys updating ----}
{{		ForAllJimmys('RE-CONSTRUCT','Re-hooking & indexing',DoJimmyStoreSelf,OnNilDoStd);{}
	FullJimmyUpdate(False, v41JimmyConverter, True);{Stroms- withoutg reconstruction}

{	DeleteFile(DataPath+'JIMMYS.OLD');{}

	DeleteFile(DataPath+'WPLTRS.DAT');
	DeleteFile(dataPath+'KDIARY.IDX'); {just to tidy up}

	{--- New Directory index structure - one category file}
	{delete old ones}
	DeleteFile(DataPath+'DIRARC.IDX');
	DeleteFile(DataPath+'DIRCAT.IDX');

	DeleteFile(DataPath+'KDIRARC.IDX');
	DeleteFile(DataPath+'DIRCAT4.IDX');
	DeleteFile(DataPath+'DIRCAT5.IDX');
	DeleteFile(DataPath+'DIRCAT6.IDX');
	DeleteFile(DataPath+'DIRCAT7.IDX');
	DeleteFile(DataPath+'DIRCAT8.IDX');
	DeleteFile(DataPath+'DIRCAT9.IDX');

	{no longer used/renamed}
	DeleteFile(DataPAth+'PRODCAT.IDX'); {index pointers are reset in converter}
	DeleteFile(DataPAth+'PRODCODE.IDX'); {index pointers are reset in converter}

	{Idiot, don't do this here as it'll get rid of all newly stored stuff}
{	DeleteFile(DataPAth+'DIARY.IDX'); {no events are kept from v4.1}

	{--- Create new subindexes -----}
	{fixit.pas - does re-indexing for all directory categories}
{	Debug.Write('Creating new subindexes...');
	ReIndex(fiFullDirIdx);
	Debug.Writeln('..done');{now done in storeself, stage III of update}

	Debug.Write('Creating default SuperUser...');
	New(User, init);
	with User^ do begin
		Surname := SuperUserName;
		Access := $FFFF;
	end;
	User^.StoreSelf;
	dispose(User, done);
	Debug.Writeln('...done');

	Debug.Write('Creating scodes...');
	{--- check std directory codes -----}
	ThinkingOn('Creating std Search Codes');
	with ScodeCollection[scDirectoryCategory]^ do begin
		LogOn;
		{remove old category types (ie 4-9)}
		if GetSCode(scDirectoryCategory, '4')<>nil then	Free(GetSCode(scDirectoryCategory, '4'));
		if GetSCode(scDirectoryCategory, '5')<>nil then	Free(GetSCode(scDirectoryCategory, '5'));
		if GetSCode(scDirectoryCategory, '6')<>nil then	Free(GetSCode(scDirectoryCategory, '6'));
		if GetSCode(scDirectoryCategory, '7')<>nil then	Free(GetSCode(scDirectoryCategory, '7'));
		if GetSCode(scDirectoryCategory, '8')<>nil then	Free(GetSCode(scDirectoryCategory, '8'));
		if GetSCode(scDirectoryCategory, '9')<>nil then	Free(GetSCode(scDirectoryCategory, '9'));

		if GetSCode(scDirectoryCategory, 'STA')=nil then Insert(New(PDirCatScodeItem, init('STA','Staff/Users',0)));
		if GetSCode(scDirectoryCategory, 'CUS')=nil then Insert(New(PDirCatScodeItem, init('CUS','Customers',0)));
		if GetSCode(scDirectoryCategory, 'SUP')=nil then Insert(New(PDirCatScodeItem, init('SUP','Suppliers',0)));

		StoreCodes(lkOff);
		LogOff;
	end;
	ThinkingOff;

	{---- update country scodes -------}
	{still to do...}
	ThinkingOn('Creating country codes');
	with ScodeCollection[scCountries]^ do begin
		LogOn;
		FreeAll;

		Insert(New(PCountryScode, init('UK','United Kingdom','44','00')));
		Insert(New(PCountryScode, init('USA','United States America','??','??')));
		Insert(New(PCountryScode, init('KY','Kenya','254','000')));

		StoreCodes(lkOff);
		LogOff;
	end; {}
	ThinkingOff;
	DeleteFile(DataPath+'KCOUNTRY.SC'); {old one}


	Debug.Writeln('...done');

	{--- update event (costed) scodes ----}
	ThinkingOn('Converting Event Codes');
	New(ScodeCollection[scTempFixit], init('KEVENTS.SC', 'Old Events', nil));
	ScodeCollection[scEvents]^.LogOn;
	with ScodeCollection[scTempFixit]^ do begin
		LogOn; {load}
		for I := 0 to Count-1 do begin
			SCode := PSCodeItem(At(i));
			if SCode^.Description = nil then SCode^.Description := NewStr(' '); {safety}
			NewScode := New(PCostedSCodeItem, init(Scode^.Code, Scode^.Description^));
			PCostedScodeItem(NewScode)^.Cost.Value := PSimpleCostedSCodeItem(Scode)^.Cost.Value;
			ScodeCollection[scEvents]^.Insert(NewScode);
		end;
		LogOff;
	end;
	ScodeCollection[scEvents]^.StoreCodes(lkOff);
	ScodeCollection[scEvents]^.LogOff;
	dispose(ScodeCollection[sctempfixit], done);
	DeleteFile(DataPath+'KEVENTS.SC');
	ThinkingOff;

	{--- update action (costed) scodes ----}
	ThinkingOn('Converting Action Codes');
	New(ScodeCollection[scTempFixit], init('KACTION.SC', 'Old Actions', nil));
	ScodeCollection[scActions]^.LogOn;
	with ScodeCollection[scTempFixit]^ do begin
		LogOn; {load}
		for I := 0 to Count-1 do begin
			SCode := PSCodeItem(At(i));
			if SCode^.Description = nil then SCode^.Description := NewStr(' '); {safety}
			NewScode := New(PCostedSCodeItem, init(Scode^.Code, Scode^.Description^));
			PCostedScodeItem(NewScode)^.Cost.Value := PSimpleCostedSCodeItem(Scode)^.Cost.Value;
			ScodeCollection[scActions]^.Insert(NewScode);
		end;
		LogOff;
	end;
	ScodeCollection[scActions]^.StoreCodes(lkOff);
	ScodeCollection[scActions]^.LogOff;
	dispose(ScodeCollection[sctempfixit], done);
	DeleteFile(DataPath+'KACTION.SC');
	ThinkingOff;

	ThinkingOn('Renaming some code files');
	{--- copy/rename outcome codes -----}
	CopyFile(DataPath+'KOUTCOME.SC', DataPath+'OUTCOME.SC');
	DeleteFile(DataPath+'KOUTCOME.SC');

	{--- copy/rename software codes -----}
	CopyFile(DataPath+'KSWTITLE.SC', DataPath+'SWTITLE.SC');
	DeleteFile(DataPath+'KSWTITLE.SC');

	{full of old codes - might as well re-input}
	DeleteFile(DataPath+'KFLOPPYS.SC');
	ThinkingOff;

	{other tidying up}
	DeleteFile(DataPath+'WPLTRS.DAT');
	DeleteFile(DataPath+'INVITEMS.NDE');

	ProgramStatus.SetDataVer(ProgVer);

	PauseMessage('UPDATE','Done',hcNoContext);
	ThinkingOff;
end;

begin
	RegisterType(RSimpleCostedSCode);

end.
