{**********************************************************************
 ***                                                                ***
 ***                     INVOICE REPORTS & ANALYSIS                 ***
 ***                                                                ***
 **********************************************************************}
{$I compdirs}
unit KInvRpts;

INTERFACE

{**********************************
 **       REPORTS               ***
 **********************************}

procedure OverdueUnpaidInvoices;
procedure AllUnpaidInvoices;
{procedure MakeOverdueLetters;{}
procedure UnpaidInvoices(PrintType: byte);{used internally}

IMPLEMENTATION

uses 	kinvoice,
			kinvsetu,
			global, setup, tui,
			files,
			multcurr,
			app,
			tuimsgs,
			tasks,
			payments,
			jimmys, jimhooks, jimindxs,
			dattime,
			minilib,
			menus,
			{$IFDEF kwplink}
			kwplink,
			{$ENDIF}
			drivers,
			indexes,
			kdirctry,
			objects,
			devices, printers,
			tuiedit, inpdnt, dialogs, views;



{*************************************************************************
 ***                                                                   ***
 ***                         UNPAID INVOICES REPORT                    ***
 ***                                                                   ***
 *************************************************************************}
{Lists all invoices that have been sent but not paid}
const
	uiPrintCurrent = $01;
	uiPrintLetters = $10;

procedure AllUnpaidInvoices;
begin 	UnpaidInvoices(uiPrintCurrent); end;

procedure OverdueUnpaidInvoices;
begin 	UnpaidInvoices(0); end;

procedure OverdueLetters;
begin 	UnpaidInvoices(uiPrintLetters) end;


procedure UnpaidInvoices(PrintType : byte);
var
	ProBox : PProgressBox;
	Rec : longint;
	DirectoryItem : PDirectoryItem;
	Invoice : PInvoice;
	S,Refs,Ref,InvSummary : string;
	OverdueCurrent, OverdueAmount1, OverdueAmount2, OverdueAmount3 : TMoney;
	OverdueTotalC, OverdueTotal1, OverdueTotal2, OverdueTotal3 : TMoney;
	OldestInvoiceLevel, LetterLevel : byte; {works out level of letter}
	OverdueTotal,Amount : Tmoney;
{	StdLEtter : PStdLetter;{}
	NumFound : longint;
{	SummaryOutput : POutputStream;{}
	Letters,PrintCurrent : boolean;

begin
	Letters := (PrintType and uiPrintLetters)>0;
	PrintCurrent := (PrintType and uiPrintCurrent)>0;

	{=========== SET UP HEADERS =====================}
	if PrintCurrent then S := 'UNPAID' else S := 'OVERDUE';

	Printer^.FormCodes^.Clear;

	if Letters then
		{--- Letters ---}
		ProBox := NewProgressBox('REMINDER LETTERS','',mfCancelButton,hcNoContext)
	else begin
		{--- Report ---}
		ProBox := NewProgressBox(S+' ACCOUNTS REPORT','',mfCancelButton,hcNoContext);
		Printer^.FormCodes^.SetStr('RTITLE',S+' ACCOUNTS REPORT');
		{make up list header}
		S := ' Name					';
		if PrintCurrent then S := S +'Current' else S := S+space(7);
		S := S 	+PadSpaceL(N2Str(InvoiceSetup.OverDuePeriod[1])+' days',9)
						+PadSpaceL(N2Str(InvoiceSetup.OverDuePeriod[2])+' days',9)
						+PadSpaceL(N2Str(InvoiceSetup.OverDuePeriod[3])+' days',9)
						+PadSpaceL('Total',10);
		Printer^.FormCodes^.SetStr('LISTHDR', S);

		Printer^.StartPrint('ODUEINVS','REPORT'); {}
	end;

	ThinkingAllowed := False; {reduce flickering}

	FileAdmin(fiJimmys)^.LogOn;
	FileAdmin(fiHooks)^.LogOn;
	FileAdmin(fiFullDirIdx)^.LogOn;

	OverdueTotalC.Init;
	OverdueTotal1.Init;
	OverdueTotal2.Init;
	OverdueTotal3.Init;
	OverdueTotal.Init;

	OverdueCurrent.Init;
	OverdueAmount1.Init;
	OverdueAmount2.Init;
	OverdueAmount3.Init;

	{============ RUN THROUGH FULL DIRECTORY CHECKING INVOICES ==============}
	Rec := 0; NumFound := 0;
	while Rec <(Stream(fiFullDirIdx)^.NoRecs-1) do begin
		if (Rec mod 10)=0 then ProBox^.Update('Looking through Directory'+
																								'Found '+N2Str(NumFound)+' Done',Rec,Stream(fiFullDirIdx)^.NoRecs-1);
		DirectoryItem := PDirectoryItem(PIndexedJimmyStream(Stream(fiFullDirIdx))^.GetJimmyAtIdx(Rec));

		if (DirectoryItem <> nil) then begin
			if (DirectoryItem^.Gotbyix = 1) then begin {ignore aliases, etc}

				Refs := ''; InvSummary := ''; Ref := ''; {for setting up codes}
				LetterLevel := 0;
				OldestInvoiceLevel := 0;

				{clear totals for this person}
				OverdueCurrent.Clear;
				OverdueAmount1.Clear;
				OverdueAmount2.Clear;
				OverdueAmount3.Clear;

{				SummaryOutput := nil; {used for building summary statement}

				{Run through chain}
				{works from end to finish of chain so that overdue letter level starts
				at worst}
				Invoice := PInvoice(HookFile^.GetLast(DirectoryItem^.GetFirstHookPtr(hkHistory), srInvoice));

				while Invoice<>nil do begin

					if not (Invoice^.Date.Blank) {sent...}
						and (not Invoice^.Due.Blank) then begin {..and not yet paid}
						{---- OVERDUE ---}

						{Total up}
						{Now the headings are given by specific days, but if the days discount
						are to override the first period, then there may be some invoices
						due to be paid that are actually *less* old than the first period.
						(we need not concern ourselves with any past it).  So therefore we
						shall need an extra line "Overdue" without a level...}
						{uses the "current" column, so if doing a "print current" list then
						it will combine with current invoices... not quite right}
						if {InvoiceSetup.DDOVerride and{} (Invoice^.OverdueLevel(Today)=1) then begin
							if (Today.Days - Invoice^.Date.Days)<InvoiceSetup.OverduePeriod[1] then
								OverdueCurrent.Add(Invoice^.Due)
							else
								OverdueAmount1.Add(Invoice^.Due);
						end else

							case Invoice^.OverdueLevel(Today) of
								0 : if PrintCurrent then OverdueCurrent.Add(Invoice^.Due);
								1 : OverdueAmount1.Add(Invoice^.Due);
								2 : OverdueAmount2.Add(Invoice^.Due);
								3 : OverdueAmount3.Add(Invoice^.Due);
							end;


						{Calculations, etc for letters from all invoices}
{						if Letters then begin
							{Build up reference numbers of invoices for letters}
							{do whether actually producing a letter for this invoice or
							not - so that you get a *full* statement for *all* outstanding
							invoices for whatever letter}
{							if PrintCurrent or (Invoice^.OverdueLevel(Today)>0) then begin
								if Ref='' then Ref := Invoice^.Ref; {last reference number (ie oldest)}
{								Refs := Refs + ', ' +Invoice^.Ref;
							end;

							{make up summary block - in forms path, so can include "<FORM STATEMNT> in letter}
{							if SummaryOutput=nil then New(SummaryOutput, init(FormsPath + 'STATEMNT',@NulFilter, @NulPaper));
							Invoice^.Print(prOneLine, SummaryOutput);

							if Invoice^.Due.inPence>0 then begin {don't deal with credits}
								{mark level of oldest *overdue* invoice}
{								if Invoice^.OverdueLevel(Today)>OldestInvoiceLevel then OldestInvoiceLevel := Invoice^.OverdueLevel(Today);

								{now, we want to write a letter if *any* invoice of the
								oldestinvoicelevel has not had a letter written already}
{								if OldestInvoiceLevel>0 then
									if Invoice^.OverdueLevel(Today)=OldestInvoiceLevel then
										if Invoice^.OverdueLtr[OldestInvoiceLevel]=False then begin

											{set type of letter to be printed}
{											LetterLevel := OldestInvoiceLevel;

											{mark invoice as letter printed - actual letter produced below, once all invoices in history have been checked}
{											Invoice^.OverdueLtr[LetterLevel] := True;
											JimmyStream^.PutAt(Invoice^.RecNo, Invoice);
										end;

							end; {something positive due}

{						end; {if letters}

					end; {if invoice overdue}

					Dispose(Invoice, Done);

					Invoice := PInvoice(HookFile^.GetPrevJimmy);

				end; {while through history}

{				if SummaryOutput<>nil then begin dispose(SummaryOutput, done); SummaryOutput := nil; end;

				{================ OUTPUT =========================================}
				if (not OverdueCurrent.Blank) or (not OverdueAmount1.Blank) or
						(not OverdueAmount2.Blank) or (not OVerdueAmount3.Blank) then begin
						{some were outstanding for this person}

{					if Letters then begin

						if LetterLevel>0 then begin
							inc(NumFound);

							{---- Produce letter -------------------}
{							if InvSummary<>'' then InvSummary := Copy(InvSummary, 3, length(InvSummary)); {del leading CRLF}
							if Refs<>'' then Refs := Copy(Refs, 3, length(Refs)); {del leading comma & space}

{{$IFDEF kwplink}
{					StdLetter := CreateStdWPLetter(@DirectoryItem^.RecNo);{not done yet - need to be able to set codes below...}
{{$ELSE}
{							StdLetter := CreateStdEdLetter(@DirectoryItem^.RecNo);
{{$ENDIF}
{							StdLetter^.Header := 'OVERDUE'+N2Str(LetterLevel)+'.STL';
							PrintStream.SetCode('INVREF',Ref);
							PrintStream.SetCode('INVREFS',Refs);

							{These ones could be set once, but get reset at end of letter}
{							PrintStream.SetCode('ODD1',N2Str(InvoiceSetup.OverDuePeriod[1]));
							PrintStream.SetCode('ODD2',N2Str(InvoiceSetup.OverDuePeriod[2]));
							PrintStream.SetCode('ODD3',N2Str(InvoiceSetup.OverDuePeriod[3]));

							Amount.Clear;
							Amount.SetToPence(OverdueAmount1.inPence+OverdueAmount2.inPence+OverdueAmount3.inPence);
							PrintStream.SetCode('ODTOT',Amount.Text(mtValue)); {overdue total}
{							Amount.Add(OverdueCurrent);
							PrintStream.SetCode('TOT',Amount.Text(mtValue)); {account total}
{							PrintStream.SetCode('TOTC',OverdueCurrent.Text(mtValue));
							PrintStream.SetCode('TOT1',OverdueAmount1.Text(mtValue));
							PrintStream.SetCode('TOT2',OverdueAmount2.Text(mtValue));
							PrintStream.SetCode('TOT3',OverdueAmount3.Text(mtValue));
							StdLetter^.Print(prFullPage, @PrintStream);
							dispose(StdLetter, done);

							{Create a label - deferred}
{							DirectoryItem^.DeferLabel(Today, adInvoice, 1);
						end;

					end else{} begin
						inc(NumFound);

						{----- Print Overdue report line ----------}
						S := SetLength(DirectoryItem^.GetName(naReport,0),28);
						if not OverdueCurrent.Blank then S := S + PadSpaceL(OverdueCurrent.Text(mtValue),9) else S := S+space(9);
						if not OverdueAmount1.Blank then S := S + PadSpaceL(OverdueAmount1.Text(mtValue),9) else S := S+space(9);
						if not OverdueAmount2.Blank then S := S + PadSpaceL(OverdueAmount2.Text(mtValue),9) else S := S+space(9);
						if not OverdueAmount3.Blank then S := S + PadSpaceL(OverdueAmount3.Text(mtValue),9) else S := S+space(9);

						{Work out running totals}
						OverdueTotalC.Add(OverdueCurrent);
						OverdueTotal1.Add(OverdueAmount1);
						OverdueTotal2.Add(OverdueAmount2);
						OverdueTotal3.Add(OverdueAmount3);

						{Total overdue}
						OverdueCurrent.Add(OverdueAmount1);
						OverdueCurrent.Add(OverdueAmount2);
						OverdueCurrent.Add(OverdueAmount3);

						S := S + PadSpaceL(OverdueCurrent.Text(mtValue),10);
						Printer^.writeln(S);

						{overall total}
						OverdueTotal.Add(OverdueCurrent);
					end;

				end;
			end; {if ix=1}
			dispose(DirectoryItem, done);
		end; {if dir not nil}


		{Cancel}
		if ProBox^.Command = cmCancel then begin
			if not Letters then Printer^.Writeln('...cancelled'); {only do if doing report}
			break;
		end;

		Rec := Rec +1;
	end; {while loop}

	{---- Report Totals -------}
	if not Letters then begin
		Printer^.Writeln(space(28+9+9+9+10)+'  -------');

		S := '<B+>'+PadSpaceL('TOTALS',28);

		if PrintCurrent or (not OverdueTotalC.Blank) then
			S := S + PadSpaceL(OverdueTotalC.Text(mtValue),9)
		else
			S := S + space(9);

		S := S+ PadSpaceL(OverdueTotal1.Text(mtValue),9)
					+ PadSpaceL(OverdueTotal2.Text(mtValue),9)
					+ PadSpaceL(OverdueTotal3.Text(mtValue),9)
					+ PadSpaceL(OverdueTotal.Text(mtValue),10)+'<B->';

		Printer^.WriteCodedStr(S);
		Printer^.Writeln('');


		Printer^.EndPrint;

	end;

	ThinkingAllowed := True;

	Desktop^.Delete(Probox);
	Dispose(ProBox, done);

	FileAdmin(fiJimmys)^.LogOff;
	FileAdmin(fiHooks)^.LogOff;
	FileAdmin(fiFullDirIdx)^.LogOff;

end;

{*************************************************************************
 ***                                                                   ***
 ***                      STATEMENTS                                   ***
 ***                                                                   ***
 *************************************************************************}
procedure PrintStatementFor(const DirectoryID : longint;
														const Full : boolean;
														const DateRange : TDateRange;
														const Device : PDEviceStream);
var	Directory : PDirectoryItem;
		Invoice : PInvoice;
		Payment : PPayment;
		Total,TotalDue,TotalPaid : TMoney;
		InvFindRec : longint;
		InvFindsrType : word;
		NothingDone : boolean;

begin
	THinkingOn('Printing');

	FileAdmin(fiHooks)^.LogOn;

	Total.Init; TotalPaid.Init; TotalDue.Init;
	NothingDone := True;

	{get directory}
	Directory := PDirectoryItem(GetJimmy(DirectoryID));
	Directory^.SetFormCodes(Device^.FormCodes);

	Invoice := PInvoice(HookFile^.GetLast(Directory^.Ptr2Accounts, srInvoice));

	while Invoice<>nil do begin

		if (Full or (Invoice^.Due.Value<>0)) and (DateRange.InRange(Invoice^.Date)) then begin

			InvFindRec := HookFile^.FindRec;
			InvFindsrType := HookFIle^.FindsrType;

			if NothingDone then begin
				Device^.StartPrint('STATEMNT',''); {NB - for some reason, this sets HookFile^.Find* to nil}
				NothingDone := False;
			end;

			Invoice^.SetFormCodes(Device^.FormCodes);
			Device^.PrintForm('STATEMNT.FRM');

			Total.Add(Invoice^.TotallerGroup.Total);
			TotalDue.Add(Invoice^.TotallerGroup.Total);

			{print payments}
			Payment := PPayment(HookFile^.GetLast(Invoice^.Ptr2Payments,srPayment));
			while Payment<>nil do begin
				Payment^.SetFormCodes(Device^.FormCodes);
				Device^.PrintForm('STATEPAY.FRM');

				TotalDue.Subtract(Payment^.Paid);
				TotalPaid.Add(Payment^.Paid);

				dispose(Payment, done);
				Payment := PPayment(HookFile^.GetPrevJimmy);
			end;

			{restore}
			HookFile^.FindRec := InvFindRec;
			HookFIle^.FindsrType := InvFindsrType;
		end; {full print, or invoice unpaid, and in specified date range}

		dispose(Invoice, done);
		Invoice := PInvoice(HookFIle^.GetPrevJimmy);
	end;

	if not NothingDone then begin
		Device^.FormCodes^.Insert(new(PMoneyFormCode, init('TOTAL', Total)));
		Device^.FormCodes^.Insert(new(PMoneyFormCode, init('TDUE', TotalDue)));
		Device^.FormCodes^.Insert(new(PMoneyFormCode, init('TPAID', TotalPaid)));

		Device^.EndPrint;
	end else
		PauseMessage('STATEMENT','Nothing to print!',hcNoContext);

	FileAdmin(fiHooks)^.LogOff;
	dispose(Directory, done);

	ThinkingOff;
end;

{===========  GET PRINTTYPE FOR STATEMENT PRINT =================}
type
	TStatementPrintType = record
		Editor : word;
		Full : word; {full or unpaid only}
		DateRange : TDateRange;
	end;

function GetStatementPrintType(var PrintType : TStatementPrintType) : word;
var	EditBox : PEditBox;
		R : TREct;
		C : word;

begin
	R.ASsign(0,0,43,13);
	New(EditBox, init(R, 'Print Statement',nil));

	with EditBox^ do begin
		Options := Options or ofCentered;

		{-- Editor --}
		R.XYLD(9,2,21,2); Insert(New(PERadioButtons, init(R,
												NewSITem('~I~nternal',
												NewSItem('~W~ordPerfect',nil)))));
		{$IFNDEF kwplink} PCluster(Current)^.EnableMask := PCLuster(Current)^.EnableMask and not Exp2(edWP51);{} {$ENDIF}
		AddLabel('Use', Current);

		{-- full ---}
		R.XYLD(9,5,21,2); Insert(New(PERadioButtons, init(R,
												NewSITem('~F~ull History',
												NewSItem('~U~npaid Only',nil)))));
		AddLabel('Full', Current);

		{-- Date Range --}
		Insert(New(PSKipBytes, init(2))); {for beginning of date range}
		R.XYLD( 9,8,12,1); Insert(New(PInputDate, init(R)));  AddLabel('~R~ange', CUrrent);
		R.XYLD(25,8,12,1); Insert(New(PInputDate, init(R)));  AddLabel('-', CUrrent);

		InsOKButton(10, 		Size.Y-3, @PrintType);
		InsCancelButton(21, Size.Y-3);

		EndInit;

		SelectNext(False);
	end;

	GetStatementPrintType := Desktop^.ExecView(EditBox);

	dispose(EditBox, done);
end;


{========== PRINTS STATEMENT FOR CURRENT LIST ============}
procedure PrintListStatement(P : pointer); far;
var List : PHookViewer;
		Device : PDeviceStream;
		Directory : PDirectoryItem;
		R : TREct;
		PrintType : TStatementPrintType;
		Title : string;

begin
	if GetStatementPrintType(PrintType)<>cmOK then exit;

	List := PHookViewer(P);

	case PrintType.Editor of
		0 : Device := PDeviceStream(Printer);
		{$IFDEF kwplink}
		1 : Device := PDeviceStream(WPStream);
		{$ENDIF}
	else
		Device := nil; {safety}
	end;

	case PrintType.Full of
		0 : Title := ' Full Account History '+PrintType.DateRange.Text(daAbbr);
		1 : Title := ' Unpaid Invoices '+PrintType.DateRange.Text(daAbbr);
	end;

	PrintStatementFor(List^.DummyParentJimmy^.RecNo,
										PrintType.Full = 0,
										PrintType.DateRange,
										Device);
end;


procedure PrintAllStatements; far;
var PrintType : TStatementPrintType;
		Device : PDeviceStream;
		Directory : PDirectoryItem;
		IdxRec : longint;
		ProBox : PProgressBox;

begin
	if GetStatementPrintType(PrintType)<>cmOK then exit;

	ProBox := NewProgressBox('PRINTING STATEMENTS','For Directory Entry ',mfCancelButton,hcNoContext);

	case PrintType.Editor of
		0 : Device := PDeviceStream(Printer);
		{$IFDEF kwplink}
		1 : Device := PDeviceStream(WPStream);
		{$ENDIF}
	else
		Device := nil; {safety}
	end;

	FileAdmin(fiFullDirIdx)^.LogOn;
	FileAdmin(fiJimmys)^.LogOn;

	for IdxRec := 0 to Stream(fiFullDirIdx)^.NoRecs-1 do begin

		if IdxREc mod 10 = 0 then ProBox^.Update('',IdxRec, Stream(fiFullDirIdx)^.NoRecs-1);

		Directory := PDirectoryItem(PIndexedJImmyStream(Stream(fiFullDirIdx))^.GetJimmyAtIdx(IdxRec));

		if Directory<>nil then begin
			if not Directory^.GotByAlias(Directory^.gotbyix) then begin

				PrintStatementFor(Directory^.RecNo,
													PrintType.Full = 0,
													PrintType.DateRange,
													Device);
			end;

			dispose(Directory, done);
		end;
	end;

	FileAdmin(fiFullDirIdx)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;

	dispose(ProBox, done);

end;


begin
	{Add overdue/unsent reports to main reports/outstanding menu}
{$IFDEF kdirctry}
	RegisterTask(DesktopTasks, cmOverdueUnpaidInvoices, @OverdueUnpaidInvoices);
	RegisterTask(DesktopTasks, cmAllUnpaidInvoices, @AllUnpaidInvoices);
	RegisterTask(DesktopTasks, cmPrintAllStatements, @PrintAllStatements);
{	RegisterTask(DesktopTasks, cmOverdueLtrs, @MakeOverdueLetters);{}
	RegisterWithList(lsAccounts, '~P~rint',
		NewItem('~S~tatement', ksStatement, kbStatement, cmPrintStatement, hcNoContext, nil), PrintListStatement);
	RegisterWithList(lsHistory, '~P~rint',
		NewItem('~S~tatement', ksStatement, kbStatement, cmPrintStatement, hcNoContext, nil), PrintListStatement);

{$ENDIF}
end.
