// PlugIn Script: Billing - Pharos account and online solution account switching //--------------------------------------------------------------------------------------- // Designed for: Pharos 8.1.5293 // Where: Pharos Systems International. // Updated: 11-Jan-2011 // // Description: // Balance: Check both the balance of the user's Pharos account and the // balance of the user's online solution account. // Debit: Debit from the user's Pharos account (if it exists) until empty // and then debit the user's online solution account. // Credit: Not supported. // // Update: // 11-Jan-2011 - Updated to handle online gateway responses with a single balance // - Gets path for Pharos bin folder from registry (used to run billing extender). // // Requirements: // * Offline billing must be enabled. // * For users with a Pharos account, the offline limit must be set for each account. // * The billing plugin must be installed to the same drive/folder on each print server. // //--------------------------------------------------------------------------------------- import "DB"; import "IO"; import "List"; import "String"; import "User"; import "Win32"; //--------------------------------------------------------------------------------------- // Script Info //--------------------------------------------------------------------------------------- // Script name and version. new sScriptName = "Billing - Pharos Account + Online Account"; new sLogPrefix = "[" + sScriptName + "] -> "; IO.PrintLine("Solution: " + sScriptName); IO.PrintLine("Version: 2"); IO.PrintLine("Copyright 2010 Pharos Systems International"); //--------------------------------------------------------------------------------------- // Variables //--------------------------------------------------------------------------------------- // Error messages. new eErrorEventTypeNotSupported = "Event type '" + PlugIn.EventType + "' not supported."; new eErrorPharosUserNotExist = "No account was not found for '" + PlugIn.BillName + "'."; new eErrorOnlineUserNotExist = "No online account was not found for '" + PlugIn.BillName + "'. Please contact the card office to determine why your online account is not available."; new eErrorUnknownBillingResponse = "Unknown command returned from Billing plug-in."; new eErrorBillingFileEmpty = "The Billing plug-in returned an empty file."; new eErrorBillingTransitionedToOffline = "The billing system is no longer available. You must log off and on again to continue printing while the system is offline."; // Account info new bIncludeNegativePharosBalancesInDebit = false; // If true and the user's has a negative balance on an advance account, // we will include that negative amount in the debit amount // I.e. we will attempt to reset their balance to zero. new bPharosAccountRequired = true; // If required, user cannot use system without a Pharos account. new sPharosPurse1Name = "Technology Grant"; new sPharosPurse2Name = "Tuition Fees"; new sPharosPurse3Name = "User Pays"; new sDefaultOnlineBalanceName = "Flex Account"; // Default online account balance name (used // if the online solution does not return account names). new sOfflineBalanceName = "Pending charges"; // Billing Plug-in filenames and other details. new sPharosPath = Win32.RegQueryValue("SOFTWARE\\Pharos\\Installed Components", "Path"); new sCmdBillingPlugInFileNameBalance = "\"" + sPharosPath + "\\Bin\\IPBilExt.exe\""; new sCmdBillingPlugInFileNameDebit = "\"" + sPharosPath + "\\Bin\\IPBilExt.exe\""; new iCmdTimeout = 30000; //--------------------------------------------------------------------------------------- // Constants - DO NOT ALTER //--------------------------------------------------------------------------------------- // Character constants. new sCharCR = "\n"; new sCharLF = "\r"; new sCharSpace = " "; new sCharQuote = "\""; // Billing constants. new sEventTypeBalance = "Balance"; new sEventTypeDebit = "Debit"; new sBillingOptionAdvance = "Advance"; new sBillingOptionArrears = "Arrears"; new sPharosNowTimestamp = "NOW"; new sPharosOffline = "PHAROS_OFFLINE"; new sCmdPharosSuccess = "OK"; new sCmdPharosFailure = "FAIL"; //--------------------------------------------------------------------------------------- // PlugIn code //--------------------------------------------------------------------------------------- // Set the default Pharos and online solution balances. new sPharosUser = ""; new sPharosCardId = PlugIn.BillName; new bIsPharosAccountAvailable = false; new bIsCardIdAvailable = false; new fPharosBalance = 0.00; new sPharosBillingOption = sBillingOptionAdvance; new fPharosPurse1 = 0.00; new fPharosPurse2 = 0.00; new fPharosPurse3 = 0.00; new sPharosAccounts = ""; new iPharosAccountCount = 0; new fPharosOfflineLimit = 0.00; new fPharosOfflineAmount = 0.00; new bIsOnlineAccountAvailable = false; new bIsOnlineSystemOffline = false; new fOnlineBalance = 0.00; new sOnlineBillingOption = sBillingOptionAdvance; new sOnlineAccounts = ""; new iOnlineAccountCount = 0; PlugIn.Result = false; IO.PrintLine(sLogPrefix + "Event Type: " + PlugIn.EventType); IO.PrintLine(sLogPrefix + "Bill Name: " + PlugIn.BillName); bIsPharosAccountAvailable = false; try { IO.PrintLine(sLogPrefix + "Get user by logon id."); User.GetUserByLogon(PlugIn.BillName); bIsPharosAccountAvailable = true; } catch { IO.PrintLine(sLogPrefix + "Failed to get user by logon id. Get user by card id."); try { User.GetUserByCardID(PlugIn.BillName); bIsPharosAccountAvailable = true; } catch { IO.PrintLine(sLogPrefix + "Failed to get user by logon id or card id."); } } if (bIsPharosAccountAvailable) { IO.PrintLine(sLogPrefix + "Get user's Pharos balance."); sPharosUser = User.GetProperty("user"); sPharosCardId = User.GetProperty("card_id"); if (not String.IsEmpty(sPharosCardId)) { bIsCardIdAvailable = true; } fPharosBalance = 0.00 + User.GetBalance(0); // Get total balance sPharosBillingOption = User.GetProperty("billing_option"); fPharosOfflineLimit = 0.00 + User.GetProperty("offline_limit"); fPharosOfflineAmount = 0.00 + User.GetProperty("offline_amount"); // Get purse information // Get Pharos purse 1 - specialized purse (default advanced) fPharosPurse1 = User.GetBalance(1); if (fPharosPurse1 > 0.00) { iPharosAccountCount += 1; sPharosAccounts += sPharosPurse1Name + sCharCR + sCharSpace + // Purse name fPharosPurse1 + sCharSpace + // Purse balance sBillingOptionAdvance + sCharCR; // Purse billing option } // Get Pharos purse 2 - specialized purse (default advanced) fPharosPurse2 = User.GetBalance(2); if (fPharosPurse2 > 0.00) { iPharosAccountCount += 1; sPharosAccounts += sPharosPurse2Name + sCharCR + sCharSpace + // Purse name fPharosPurse2 + sCharSpace + // Purse balance sBillingOptionAdvance + sCharCR; // Purse billing option } // Get Pharos purse 3 - user pays iPharosAccountCount += 1; fPharosPurse3 = User.GetBalance(3); sPharosAccounts += sPharosPurse3Name + sCharCR + sCharSpace + // Purse name fPharosPurse3 + sCharSpace + // Purse balance sPharosBillingOption + sCharCR; // Purse billing option IO.PrintLine(sLogPrefix + "User: " + sPharosUser); IO.PrintLine(sLogPrefix + "Card Id: " + sPharosCardId); IO.PrintLine(sLogPrefix + "Is Card Id Available? " + bIsCardIdAvailable); IO.PrintLine(sLogPrefix + "Pharos Balance: " + fPharosBalance); IO.PrintLine(sLogPrefix + "Pharos Billing Option: " + sPharosBillingOption); IO.PrintLine(sLogPrefix + "Pharos Accounts: " + sPharosAccounts); IO.PrintLine(sLogPrefix + "Offline Limit: " + fPharosOfflineLimit); IO.PrintLine(sLogPrefix + "Offline Amount: " + fPharosOfflineAmount); } else if (bPharosAccountRequired) { IO.PrintLine(sLogPrefix + "Error: " + eErrorPharosUserNotExist); PlugIn.Result = false; PlugIn.Error = eErrorPharosUserNotExist; exit; } // Get the user's online balance (even for a debit) if (bIsCardIdAvailable) { IO.PrintLine(sLogPrefix + "Get user's online balance."); // Call the billing plug-in. new sCmdBillingPlugInOutputFileName = Win32.GetTempFileName(); new sCmdBillingPlugIn = sCmdBillingPlugInFileNameBalance + sCharSpace + sCmdBillingPlugInOutputFileName + sCharSpace + sEventTypeBalance + sCharSpace + sCharQuote + sPharosCardId + sCharQuote + sCharSpace + sCharQuote + PlugIn.ClientDesc + sCharQuote; IO.PrintLine(sLogPrefix + "Billing PlugIn: " + sCmdBillingPlugIn); Win32.ExecProcess(sCmdBillingPlugIn, iCmdTimeout); new sCmdResults = ""; IO.LoadFile(sCmdResults, sCmdBillingPlugInOutputFileName); IO.DeleteFile(sCmdBillingPlugInOutputFileName); new listCmdResults = []; // Get the billing plug-in results into a list. new iPos = String.Find(sCmdResults, sCharCR); while (iPos <> -1) { new sCmdResultLine = sCmdResults; String.Left(sCmdResultLine, iPos); String.Delete(sCmdResults, 0, iPos + 1); String.TrimLeft(sCmdResultLine); String.TrimRight(sCmdResultLine); listCmdResults += sCmdResultLine; iPos = String.Find(sCmdResults, sCharCR); } if (not String.IsEmpty(sCmdResults)) { listCmdResults += sCmdResults; } // Output the results. IO.PrintLine(sLogPrefix + "PlugIn Results: " + listCmdResults); if (List.Length(listCmdResults) < 1) { IO.PrintLine(sLogPrefix + "Receive an empty file for the online solution command."); } else if (String.Find(listCmdResults[0], sCmdPharosSuccess) <> -1) { // If the billing plug-in is successful, add the online solution balance to the balance results. bIsOnlineAccountAvailable = true; fOnlineBalance = 0.00 + listCmdResults[1]; sOnlineBillingOption = listCmdResults[2]; // Check if any accounts were returned. if (List.Length(listCmdResults) > 3) { new iBalanceCount = 0 + listCmdResults[3]; // If there is only one purse, no purse balance info is included. if (iBalanceCount == 0) { sOnlineAccounts += sDefaultOnlineBalanceName + sCharCR + sCharSpace + listCmdResults[1] + sCharSpace + listCmdResults[2] + sCharCR; iOnlineAccountCount += 1; } else { // Get all the account details. new iCount = 0; while (iCount < iBalanceCount) { if (List.Length(listCmdResults) >= 3 + ((iCount * 3) + 4)) { new sAccountName = listCmdResults[4 + (iCount * 3) + 0]; new sAccountBalance = listCmdResults[4 + (iCount * 3) + 1]; new sAccountBillingOption = listCmdResults[4 + (iCount * 3) + 2]; iOnlineAccountCount += 1; sOnlineAccounts += sAccountName + sCharCR + sCharSpace + sAccountBalance + sCharSpace + sAccountBillingOption + sCharCR; } iCount += 1; } } } else { // Otherwise, add the online solution balance and use the // standard online solution account name. iOnlineAccountCount = 1; sOnlineAccounts = sDefaultOnlineBalanceName + sCharCR + sCharSpace + fOnlineBalance + sCharSpace + sOnlineBillingOption + sCharCR; } } else if (listCmdResults[1] == sPharosOffline) { bIsOnlineSystemOffline = true; } else { if (not bIsPharosAccountAvailable) { IO.PrintLine(sLogPrefix + "Error: " + listCmdResults[1]); PlugIn.Result = false; PlugIn.Error = listCmdResults[1]; exit; } } } // If the billing system is offline and the user's Pharos account has a billing option of "Advance", // we must force the entire transaction offline (since we cannot split transactions between partial online/offline) if (bIsOnlineSystemOffline and sPharosBillingOption == sBillingOptionAdvance) { IO.PrintLine(sLogPrefix + "Online billing system is offline. Returning PHAROS_OFFLINE."); PlugIn.Result = false; PlugIn.Error = sPharosOffline; exit; } if (PlugIn.EventType == sEventTypeBalance) { IO.PrintLine(sLogPrefix + "Handle balance request."); if (bIsOnlineAccountAvailable or bIsPharosAccountAvailable) { // Set the standard balance and billing option. PlugIn.Balance = 0.00 + fOnlineBalance; PlugIn.BillingOption = sOnlineBillingOption; PlugIn.BalanceCount = iOnlineAccountCount; PlugIn.Accounts = sOnlineAccounts; if (bIsPharosAccountAvailable) { // Add the Pharos balance. PlugIn.Balance += fPharosBalance; // Calculate the billing option. if ((sPharosBillingOption == sBillingOptionAdvance) and (sOnlineBillingOption == sBillingOptionAdvance)) { PlugIn.BillingOption = sBillingOptionAdvance; } else { PlugIn.BillingOption = sBillingOptionArrears; } // Add the Pharos balance as an account. PlugIn.BalanceCount += iPharosAccountCount; PlugIn.Accounts = sPharosAccounts + PlugIn.Accounts; } if (fPharosOfflineAmount> 0) { PlugIn.Balance -= fPharosOfflineAmount; PlugIn.BalanceCount += 1; PlugIn.Accounts = sOfflineBalanceName + sCharCR + sCharSpace + (-fPharosOfflineAmount) + sCharSpace + sBillingOptionAdvance + sCharCR + PlugIn.Accounts; } PlugIn.Result = true; IO.PrintLine(sLogPrefix + "Accounts: " + sCharCR + PlugIn.Accounts); } else { IO.PrintLine(sLogPrefix + "Neither a Pharos or an Online account are available."); } } else if (PlugIn.EventType == sEventTypeDebit) { // Handle a debit request. IO.PrintLine(sLogPrefix + "Handle debit request."); IO.PrintLine(sLogPrefix + "Date/Time: " + PlugIn.DateTime); IO.PrintLine(sLogPrefix + "Amount: " + (-PlugIn.Amount)); new fPurseDebitAmount = 0.00; if ((fPharosBalance - PlugIn.Amount) > -0.00001 or (User.GetProperty("billing_option") == sBillingOptionArrears)) { IO.PrintLine(sLogPrefix + "Debit the amount entirely from the Pharos account."); try { if (fPharosPurse1 > 0.00) { fPurseDebitAmount = PlugIn.Amount; if (PlugIn.Amount > fPharosPurse1) { fPurseDebitAmount = fPharosPurse1; } PlugIn.Amount -= fPurseDebitAmount; User.Debit(fPurseDebitAmount, "edicashier", 1, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) } if (fPharosPurse2 > 0.00) { fPurseDebitAmount = PlugIn.Amount; if (PlugIn.Amount > fPharosPurse2) { fPurseDebitAmount = fPharosPurse2; } PlugIn.Amount -= fPurseDebitAmount; User.Debit(fPurseDebitAmount, "edicashier", 2, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) } User.Debit(PlugIn.Amount, "edicashier", 3, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) PlugIn.Result = true; } catch { IO.PrintLine(sLogPrefix + "Error: " + Script.GetErrorMessage()); PlugIn.Result = false; PlugIn.Error = Script.GetErrorMessage(); } } else { IO.PrintLine(sLogPrefix + "Debit from online solution and Pharos account (if available)."); new fAvailablePharosFunds = fPharosBalance; if ((fAvailablePharosFunds < 0) and (sPharosBillingOption == sBillingOptionAdvance)) { if (not bIncludeNegativePharosBalancesInDebit) { IO.PrintLine(sLogPrefix + "Pharos account balance is negative, but will be ignored."); fAvailablePharosFunds = 0; } else if (fAvailablePharosFunds < 0) { IO.PrintLine(sLogPrefix + "Including the negative Pharos account balance in debit request. Negative balance: " + fAvailablePharosFunds); } } IO.PrintLine("+++++++++DEBUG AMOUNT: " + PlugIn.Amount); IO.PrintLine("+++++++++DEBUG fpharosbalance: " + fPharosBalance); // Debit an amount from the online solution. new sCmdBillingPlugInOutputFileName = Win32.GetTempFileName(); new sCmdBillingPlugIn = sCmdBillingPlugInFileNameDebit + sCharSpace + sCmdBillingPlugInOutputFileName + sCharSpace + sEventTypeDebit + sCharSpace + sCharQuote + sPharosCardId + sCharQuote + sCharSpace + (PlugIn.Amount - fAvailablePharosFunds) + sCharSpace + PlugIn.Usage1 + sCharSpace + sCharQuote + PlugIn.Resource + sCharQuote + sCharSpace + sCharQuote + PlugIn.Client + sCharQuote + sCharSpace + sCharQuote + PlugIn.ClientDesc + sCharQuote + sCharSpace + PlugIn.Usage2 + sCharSpace + sCharQuote + PlugIn.DateTime + sCharQuote; IO.PrintLine(sLogPrefix + "Billing PlugIn: " + sCmdBillingPlugIn); Win32.ExecProcess(sCmdBillingPlugIn, iCmdTimeout); new sCmdResults = ""; IO.LoadFile(sCmdResults, sCmdBillingPlugInOutputFileName); IO.DeleteFile(sCmdBillingPlugInOutputFileName); new listCmdResults = []; // Get the billing plug-in results into a list. new iPos = String.Find(sCmdResults, sCharCR); while (iPos <> -1) { new sCmdResultLine = sCmdResults; String.Left(sCmdResultLine, iPos); String.Delete(sCmdResults, 0, iPos + 1); String.TrimLeft(sCmdResultLine); String.TrimRight(sCmdResultLine); listCmdResults += sCmdResultLine; iPos = String.Find(sCmdResults, sCharCR); } if (not String.IsEmpty(sCmdResults)) { listCmdResults += sCmdResults; } // Output the results. IO.PrintLine(sLogPrefix + "PlugIn Results: " + listCmdResults); if (List.Length(listCmdResults) < 1) { IO.PrintLine(sLogPrefix + "Receive an empty file for the online solution command."); PlugIn.Result = false; PlugIn.Error = eErrorBillingFileEmpty; } else if (String.Find(listCmdResults[0], sCmdPharosFailure) <> -1) { PlugIn.Result = false; if (List.Length(listCmdResults) > 1) { PlugIn.Error = listCmdResults[1]; if (PlugIn.Error == sPharosOffline and PlugIn.DateTime == sPharosNowTimestamp) { PlugIn.Error = eErrorBillingTransitionedToOffline; } } } else if (String.Find(listCmdResults[0], sCmdPharosSuccess) <> -1) { if (fAvailablePharosFunds > 0 or (bIncludeNegativePharosBalancesInDebit and (fAvailablePharosFunds < 0) and (sPharosBillingOption == sBillingOptionAdvance))) { IO.PrintLine(sLogPrefix + "Debit the remaining amount from the Pharos account."); IO.PrintLine(sLogPrefix + "Debiting: " + (-fAvailablePharosFunds)); try { if (fAvailablePharosFunds < 0) { IO.PrintLine(sLogPrefix + "Debit is positive as the user's negative balance on the Pharos account has been paid."); User.Credit(fAvailablePharosFunds, "edicashier", 3, 1); // Perform a debit using the built-in edicashier (no debit transaction is logged) } else { if (fPharosPurse1 > 0.00) { fPurseDebitAmount = fAvailablePharosFunds; if (fAvailablePharosFunds > fPharosPurse1) { fPurseDebitAmount = fPharosPurse1; } fAvailablePharosFunds -= fPurseDebitAmount; User.Debit(fPurseDebitAmount, "edicashier", 1, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) } if (fPharosPurse2 > 0.00) { fPurseDebitAmount = fAvailablePharosFunds; if (fAvailablePharosFunds > fPharosPurse2) { fPurseDebitAmount = fPharosPurse2; } fAvailablePharosFunds -= fPurseDebitAmount; User.Debit(fPurseDebitAmount, "edicashier", 2, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) } User.Debit(fAvailablePharosFunds, "edicashier", 3, 0); // Perform a debit using the built-in edicashier (no debit transaction is logged) } PlugIn.Result = true; } catch { IO.PrintLine(sLogPrefix + "Error: " + Script.GetErrorMessage()); PlugIn.Result = false; PlugIn.Error = Script.GetErrorMessage(); } } else { PlugIn.Result = true; } } else { IO.PrintLine(sLogPrefix + "Error: " + eErrorUnknownBillingResponse); PlugIn.Error = eErrorUnknownBillingResponse; PlugIn.Result = false; } } } else { IO.PrintLine(sLogPrefix + "Error: " + eErrorEventTypeNotSupported); PlugIn.Result = false; PlugIn.Error = eErrorEventTypeNotSupported; }