All pastes #97809 Raw Edit

Untitled

public text v1 · immutable
#97809 ·published 2006-07-24 02:26 UTC
rendered paste body
/*	$Id: clsAuditAccountingApp.cpp,v 1.2 1999/02/21 02:20:57 josh Exp $	*/
//
//	File:	clsAuditAccountingApp.cc
//
//	Class:	clsAuditAccountingApp
//
//	Author:	Michael Wilson (michael@ebay.com)
//
//	Function:
//
//
// Modifications:
//
#include "eBayDebug.h"
#include "eBayTypes.h"
#include "clsAuditAccountingApp.h"
#include "clsEnvironment.h"
#include "clsDatabase.h"
#include "clsMarketPlaces.h"
#include "clsMarketPlace.h"
#include "clsUsers.h"
#include "clsUser.h"
#include "clsAccount.h"
#include "clsAccountDetail.h"

#include <stdio.h>
#include <errno.h>
#include <time.h>

#define FLUSH_TO_END									\
	do													\
	{													\
		if (!fgets(buf, sizeof(buf), pFile))			\
		{												\
			done  = true;								\
			break;										\
		}												\
		recCount++;										\
		if (strncmp(buf, BeginRec, BeginRecLen) == 0)	\
			break;										\
	} while (1==1);					

static float RoundToCents(float it)
{
	float		itCents;
	int			itInt;

	itCents	= it * 100;
	itCents	= itCents + .5;
	itInt	= (int)itCents;
	it		= (float)itInt / 100;

	return it;
}

char *
split(register char *s, 
	  register const char *delim)
{
	register char	*spanp;
	register int	c, sc;
	char			*tok;
	static char		*last;

	if (s == NULL && (s = last) == NULL)
		return (NULL);

	/*
	 * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
	 */
		/*cont:*/
	c = *s++;

	if (c == 0) {           /* no non-delimiter characters */
			last = NULL;
			return (NULL);
	}
	tok = s - 1;

	/*
	 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
	 * Note that delim must have one NUL; we stop if we see that, too.
	 */
	for (;;) {
			/* c = *s++; ** pmo - move to bottom of loop */
			spanp = (char *)delim;
	do {
			if ((sc = *spanp++) == c) {
					if (c == 0)
							s = NULL;
					else
							s[-1] = 0;
					last = s;
					return (tok);
			}
	} while (sc != 0);
		c = *s++;       /* pmo - move to bottom of loop */
	}
/* NOTREACHED */
}

//
// Sort routine
//
static bool sort_account_detail_time(clsAccountDetail *pA, clsAccountDetail *pB)
{
	if (pA->mTime < pB->mTime)
		return true;

	return false;
}



clsAuditAccountingApp::clsAuditAccountingApp(unsigned char *pRequest)
{
	mpDatabase		= (clsDatabase *)0;
	mpMarketPlaces	= (clsMarketPlaces *)0;
	mpMarketPlace	= (clsMarketPlace *)0;
	mpUsers			= (clsUsers *)0;

	return;
}


clsAuditAccountingApp::~clsAuditAccountingApp()
{
	return;
}

// Important Defines
#define TIME_SIZE		32
#define	MEMO_SIZE		80	
#define	ITEM_SIZE		12
#define	ARRAY_SIZE		1	

const char *BeginRec		= "BEGIN\t";
const int	BeginRecLen		= 6;
const char *EndRec			= "END\t";
const int	EndRecLen		= 4; 

void clsAuditAccountingApp::Run(int BatchId)
{
	// File stuff
	char			*pFileName = "accounts.txt";
	FILE			*pFile;
	char			buf[1024];
	char			buf2[1024];
	char			*s;

	// Counters and stuff
	bool			done;
	bool			badRecord;
	int				recCount;
	int				rejectCount	= 0;
	int				acceptCount	= 0;
	int				userCount	= 0;

	// Things about the current user
	char			*pUserName;
	int				userNameLen;
	int				theAWId;

	char			*pItemId;
	char			*pComma;

	// The real current user and things about them.
	clsUser							*pUser;
	clsAccount						*pAccount;
	AccountDetailVector				vAccountDetail;
	AccountDetailVector::iterator	vADi;

	int					userRecCount;
	int					userRecFoundCount;
	int					userRecMissingCount;
	int					userRejectCount;
	int					userAcceptCount;

	char				*pEndUserName;

	// These fields represent the individual accounting
	// detail records. We don't use array insert because
	// recovery is SUCH a pain;
	int						theUserId;
	time_t					theTime;
	const struct tm			*pTheTimeAsTM;
	char					cTheTime[32];
	AccountDetailTypeEnum	theType;
	double					theAmount;
	int						memoLen;
	char					theMemo[MEMO_SIZE + 1];
	char					theItem[ITEM_SIZE + 1];

	// Date conversion
	struct tm				*pTheTime;

	// Arrays of things
	int				id[ARRAY_SIZE];
	char			date[ARRAY_SIZE][32];
	int				action[ARRAY_SIZE];
	float			amount[ARRAY_SIZE];
	int				transactionId[ARRAY_SIZE];
	char			memo[ARRAY_SIZE][MEMO_SIZE + 1];
	int				migrationBatchId[ARRAY_SIZE];

	int				iItem;
	unsigned int	itemTransactionId[ARRAY_SIZE];
	char			item[ARRAY_SIZE][ITEM_SIZE +1];

	if (!mpMarketPlaces)
		mpMarketPlaces	= gApp->GetMarketPlaces();

	if (!mpMarketPlace)
		mpMarketPlace	= mpMarketPlaces->GetCurrentMarketPlace();

	if (!mpUsers)
		mpUsers			= mpMarketPlace->GetUsers();

	if (!mpDatabase)
		mpDatabase	= gApp->GetDatabase();


	// File shenanigins
	pFile	= fopen(pFileName, "r");

	if (!pFile)
	{
		fprintf(stderr,
				"Error %s opening %s\n",
				strerror(errno), 
				pFileName);
		return;
	}

	// Ok, here we gooooo
	done			= false;
	recCount		= 0;

	// Prime the pump
	if (!fgets(buf, sizeof(buf), pFile))
	{
		done  = true;
	}
	recCount++;

	// Ok, here we go
	do
	{
		// Here, we expect a user record to be in the buffer,
		// which tells us which user(s) the feedback is for.
		// Figure out who the user is...
		if (memcmp(buf, BeginRec, sizeof(BeginRec)) != 0)
		{
			fprintf(stderr, "Begin record missing in <%s>\n", buf);
			exit(1);
		}

		pUserName	= buf + BeginRecLen;
		if ((s = split(pUserName, "\t")) && *s)
		{
			pUserName	= s;
			userNameLen	= strlen(pUserName);
		}
		else
		{
			fprintf(stderr, "No User Name found in <%s>\n", buf);
			FLUSH_TO_END;
			continue;
		}

		if (strlen(pUserName) > EBAY_MAX_USERID_SIZE)
		{
			fprintf(stderr, "User Name <%s> too long\n",
					pUserName);

			FLUSH_TO_END;

			continue;
		}

		// Let's find the user
		pUser	= mpUsers->GetUser(pUserName);
		if (!pUser)
		{
			printf("User <%s> NOT found. Flushing\n",
				 	 pUserName);

			FLUSH_TO_END;
			continue;

		}

		theUserId	= pUser->GetId();
		printf("Begin processing User <%s>/<%s>\n",
				pUserName, pUser->GetUserId());


		// Ok, we're ready. Let's get the user's account information
		pAccount	= pUser->GetAccount();

		// And the detail...
		pAccount->GetAccountDetail(&vAccountDetail);

		// Sort it, and position the iterator
		sort(vAccountDetail.begin(), vAccountDetail.end(),
			 sort_account_detail_time);

		vADi	= vAccountDetail.begin();

		// Now, let's fetch, decode, and handle those records
		userRecCount		= 0;
		userRecFoundCount	= 0;
		userRecMissingCount	= 0;
		userRejectCount		= 0;
		userAcceptCount		= 0;
		iItem				= 0;
		do
		{
			// Fetch the next record. If we're at EOF,
			// then just break out.
			badRecord	= false;
			if (!fgets(buf, sizeof(buf), pFile))
			{
				done	= true;
				break;
			}
			recCount++;
			strcpy(buf2, buf);

			// See if it's a signal record
			if (strncmp(buf, EndRec, EndRecLen) == 0)
			{
				pEndUserName	= buf + EndRecLen;
				if ((s = split(pEndUserName, "\t")) && *s)
				{
					pEndUserName	= s;
				}
				else
				{
					fprintf(stderr, "No Ending User Name found in <%s>\n", buf);
					continue;
				}

				if (strcmp(pEndUserName, pUser->GetUserId()) != 0)
				{
					fprintf(stderr, "Begin/End User name mismatch! Begin <%s>, End <%s>\n",
							pUser->GetUserId(), pEndUserName);
				}


				// Print out interesting things
				printf(
				"User <%s>, AW Account %d: %d/%d/%d/%d/%d\n",
				pUser->GetUserId(), theAWId, userRecCount, userRecFoundCount,
				userRecMissingCount, userAcceptCount, userRejectCount
						);

				// Bump the user count
				userCount++;

				// Look for the next Begin
				if (!fgets(buf, sizeof(buf), pFile))
				{
					done	= true;
					break;
				}

				// If we find a begin without an end, then
				// something's up. It's ok, we can recover
				// though
				if (strncmp(buf, BeginRec, BeginRecLen) == 0)
				{
					break;
				}
				else
				{
					fprintf(stderr, "End without Begin!\n");
					exit(1);
				}
			}

			// Make sure we didn't miss something
			if (strncmp(buf, BeginRec, BeginRecLen) == 0)
			{
				fprintf(stderr, "Warning! Begin found without End!\n");
				exit(1);
			}

			//	We know this isn't a delimiting record, so let's
			//	count it.
			userRecCount++;

			
			// Now the time
			if ((s = split(buf, "\t")) && *s)
			{
				theTime		= atol(s);
			}
			else
			{
				fprintf(stderr, "Can\'t find time! in <%s>\n",
						  buf2);
				badRecord	= true;
			}

			// Now, the "type". AuctionWeb only had 3 types --
			// "New", "C" and "D". We'll start with that, and
			// hope we can do better.
			if (!badRecord &&
				((s = split(NULL, "\t")) && *s))
			{
				// If it's a "new" record, just ignore it
				if (strcmp(s, "NEW") == 0)
				{
					continue;
				}
				else if (strcmp(s, "C") == 0)
				{
					theType		= 	AccountDetailAWCredit;
				}
				else if (strcmp(s, "D") == 0)
				{
					theType		=	AccountDetailAWDebit;
				}
				else if (strcmp(s, "M") == 0)
				{
					theType		= AccountDetailAWMemo;
				}
				else
				{
					fprintf(stderr, "Bad Type in <%s>!\n",
							  buf2);
					badRecord	= true;
				}
			}
			else
			{
				fprintf(stderr, "Can\'t find type in <%s>!!!\n",
						  buf2);
				badRecord	= true;
			}


			// We assume the input records are sorted by time,
			// and we know the account records are.
			for (;
				 vADi != vAccountDetail.end();
				 vADi++)
			{
				// If we got it, we're gold!
				if ((*vADi)->mTime == theTime)
					break;

				if ((*vADi)->mTime > theTime)
					break;
			}

			//	Ok, if the times are equal, we found the 
			//	item, Just count it and be done!
			if ((*vADi)->mTime == theTime)
			{
				userRecFoundCount++;
				continue;
			}

			//	Otherwise, we must not have found it, and
			//	have to make a new one.
			userRecMissingCount++;
			pTheTimeAsTM	= localtime(&theTime);
			strftime(cTheTime, sizeof(cTheTime),
					 "%m/%d/%y %H:%M:%S",
					 pTheTimeAsTM);

			printf("%s %s missing %s\n",
				   pUser->GetUserId(),
				   cTheTime,
				   buf2);



			// Now, the amount
			if (theType != AccountDetailAWMemo)
			{
				if (!badRecord)
				{
					if ((s = split(NULL,"\t")) && *s)
					{
						theAmount	= atof(s);
					}
					else
					{
						fprintf(stderr, "Can\'t find amount in <%s>!!!\n",
								  buf2);
						badRecord	= true;
					}
				}
			}
			else
				theAmount	= 0;

			if (theAmount > 99999999.99 ||
				 theAmount < -99999999.99)
			{
				fprintf(stderr, "Amount %f too large in <%s>. Skipped!\n",
						  theAmount, buf);
				badRecord	= true;
			}

			// Finally...zee comment!
			if (!badRecord)
			{
				if ((s = split(NULL, "\t")) && *s)
				{
					memoLen	= strlen(s);
					if (*(s + memoLen - 1) == '\n')
              	    *(s + memoLen - 1) = '\0';				
					if (strlen(s) > MEMO_SIZE)
					{
						fprintf(stderr, "Memo <%s> too big in <%s>!\n", s, buf2);
						badRecord	= true;
					}
					else if (strlen(s) > 0)
					{
						strcpy(theMemo, s);
					}
					else
					{
						fprintf(stderr, "Zero Len Comment!\n");
						strcpy(s, " ");
					}
				}
				else
				{
					fprintf(stderr, "No Memo!\n");
					strcpy(theMemo, " ");
				}
			}
			// All done! If we don't have a "bad" record,
			// then we should advance our pointers
			if (badRecord)
			{
				rejectCount++;
				userRejectCount++;
				printf("Record Skipped!!!\n");
				continue;
			}
			else
			{
				acceptCount++;
				userAcceptCount++;
			}

			// Let's see if we can figure out anything else about
			// this record
			if (strncmp(theMemo, "Insertion fee item ", 19) == 0)
			{
				theType	= AccountDetailFeeInsertion;
				pItemId	= theMemo + 19;
				if (strlen(pItemId) <= ITEM_SIZE)
					strcpy(theItem, pItemId);
				else
					theItem[0]	= '\0';

				// No need for a memo.
				theMemo[0]		= '\0';
			}
			else if (strncmp(theMemo, "Item ", 5) == 0) 
			{
				pItemId	= theMemo + 5;
				pComma	= strchr(pItemId, ',');
				if (pComma != NULL)
				{
					if (((pComma - pItemId) <= ITEM_SIZE) &&
						strncmp(pComma, ", final value $", 15) == 0)
					{
						// SOMETIMEs the amount is -ve, which means this
						// is _really_ a listing fee credit!
						if (theAmount < 0)
						{
							theType	= AccountDetailCreditNoSale;
							theAmount	= -theAmount;
						}
						else
						{
							theType	= AccountDetailFeeFinalValue;
						}
						memset(theItem, 0x00, sizeof(theItem));
						memcpy(theItem, pItemId, pComma - pItemId);

						// Memo is only final value now
						sprintf(theMemo, "F%s", pComma + 3);
					}
				}
			}
			else if (strncmp(theMemo,
							 "courtesy credit ", 16) == 0)
			{
				theType	= AccountDetailCreditCourtesy;
			}
			else if (strncmp(theMemo,
							 "credit card payment -- thank you!",
							 33) == 0)
			{
				theType	= AccountDetailPaymentCC;
				theMemo[0]	= '\0';
			}
			else if (strncmp(theMemo,
							 "payment -- thank you!",
							 20) == 0)
			{
				theType	= AccountDetailPaymentCheck;
				theMemo[0]	= '\0';
			}
			else if (strncmp(theMemo,
							 "finance charge",
							 14) == 0)
			{
				theType	= AccountDetailFinanceCharge;
				theMemo[0]	= '\0';
			}

			// For some reason, some old records come in with -ve signs in the
			// amounts, some don't. Let's make sure it's consistent : All debits
			// are negative.
			if (theType == AccountDetailFeeInsertion		||
				theType == AccountDetailFeeBold				||
				theType == AccountDetailFeeFeatured			||
				theType == AccountDetailFeeCategoryFeatured ||
				theType == AccountDetailFeeFinalValue		||
				theType == AccountDetailFinanceCharge		||
				theType == AccountDetailAWDebit					)
			{
				if (theAmount > 0)
					theAmount	= -theAmount;
			}
			else
			{
				if (theAmount < 0)
					theAmount	= -theAmount;
			}

			// One more check
			 if (theAmount > 99999999.99 ||
				 theAmount < -99999999.99)
			 {
				fprintf(stderr, "Amount %f too large in <%s>. Skipped!\n",
						theAmount, buf);
				badRecord   = true;
					continue;
			 }

			// Ok, let's start filling in the array
			id[0]						=	pUser->GetId();

			pTheTime	= localtime(&theTime);
			sprintf(date[0], 
					"%02d-%02d-%02d %02d:%02d:%02d",
					pTheTime->tm_year + 1900,
					pTheTime->tm_mon + 1,
					pTheTime->tm_mday,
					pTheTime->tm_hour,
					pTheTime->tm_min,
					pTheTime->tm_sec);

			action[0]					=	theType;
			amount[0]					=	theAmount;
			transactionId[0]			=	0;
			strcpy(memo[0], theMemo);
			migrationBatchId[0]	=	BatchId;

			itemTransactionId[0] =
				gApp->GetDatabase()->XAddRawAccountDetail(1,
														  &id[0],
														  (char *)&date[0],
														  &action[0],
														  &amount[0],
														  (char *)&memo[0],
														  &transactionId[0],
														  &migrationBatchId[0]);


			// Now, construct an account detail record
			if (theType == AccountDetailFeeInsertion ||
				theType == AccountDetailFeeFinalValue)
			{
				strcpy(item[iItem], theItem);
				if (item[iItem][0] == '\0')
					strcpy(item[0], "Unknown");

				gApp->GetDatabase()->XAddAccountAWItemXref(iItem,
														  &itemTransactionId[0],
														  (char *)&item[0]);


			}


		} while (1==1);

		delete	pUser;
		pUser			= NULL;

		// Dump them out
		// Now we'll be looping around here...
		
	} while (!done);

	printf("Done! %d Users, %d Records, %d accepted, %d rejected\n",
			userCount, recCount, acceptCount, rejectCount);

	// Clean up
	fclose(pFile);

	return;
}

static clsAuditAccountingApp *pTestApp = NULL;

int main(int argc, char* argv[])
{
	int	batchId;

	batchId	= atoi(argv[1]);

	if (batchId == 0)
	{
		printf("Bad Batch id <%s>\n", argv[1]);
	}
	else
	{
		printf("**** Begin Batch %d ****\n", batchId);
	}

	if (!pTestApp)
	{
		pTestApp	= new clsAuditAccountingApp(0);
	}

	pTestApp->InitShell();
	pTestApp->Run(batchId);

	return 0;
}