protected void DropYear_SelectedIndexChanged(object sender, EventArgs e) { int year = Int32.Parse(this.DropYear.SelectedValue); if (year < 2000) { return; } FinancialAccounts balanceAccounts = FinancialAccounts.ForOrganization(CurrentOrganization, FinancialAccountType.Balance); FinancialAccounts resultAccounts = FinancialAccounts.ForOrganization(CurrentOrganization, FinancialAccountType.Result); FinancialAccount ownCapital = CurrentOrganization.FinancialAccounts.DebtsEquity; FinancialAccount resultAsNoted = CurrentOrganization.FinancialAccounts.CostsYearlyResult; FinancialAccounts balancesWithoutCapital = FinancialAccounts.ForOrganization(CurrentOrganization, FinancialAccountType.Balance); balancesWithoutCapital.Remove(ownCapital); FinancialAccounts resultAccountsWithoutNotedResult = FinancialAccounts.ForOrganization(CurrentOrganization, FinancialAccountType.Result); resultAccountsWithoutNotedResult.Remove(CurrentOrganization.FinancialAccounts.CostsYearlyResult); Currency currency = CurrentOrganization.DefaultCountry.Currency; this.LabelResultsAll.Text = String.Format("{0} {1:N2}", currency.Code, resultAccounts.GetDeltaCents(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)) / 100.0); this.LabelResultsNoted.Text = String.Format("{0} {1:N2}", currency.Code, resultAsNoted.GetDeltaCents(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)) / 100.0); this.LabelEoyBalance.Text = String.Format("{0} {1:N2}", currency.Code, balanceAccounts.GetDeltaCents(new DateTime(1900, 1, 1), new DateTime(year + 1, 1, 1)) / 100.0); Int64 endOfLastYearCapital = ownCapital.GetDeltaCents(new DateTime(1900, 1, 1), new DateTime(year, 1, 1)); Int64 endOfSelectedYearCapital = ownCapital.GetDeltaCents(new DateTime(1900, 1, 1), new DateTime(year + 1, 1, 1)); this.LabelEolyOwnCapital.Text = String.Format("{0} {1:N2}", currency.Code, endOfLastYearCapital / 100.0); this.LabelEocyOwnCapital.Text = String.Format("{0} {1:N2}", currency.Code, endOfSelectedYearCapital / 100.0); this.LabelOwnCapitalDiff.Text = String.Format("{0} {1:N2}", currency.Code, (endOfSelectedYearCapital - endOfLastYearCapital) / 100.0); this.LabelOwnCapitalDelta.Text = String.Format("{0} {1:N2}", currency.Code, ownCapital.GetDeltaCents(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)) / 100.0); }
private BudgetData GetBudgetData(int budgetId, int year) { FinancialAccount account = FinancialAccount.FromIdentity(budgetId); double budget = -account.GetBudget(year); Int64 actualCents = account.GetDeltaCents(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)); double actual = actualCents / 100.0; // Get suballocated FinancialAccounts accountTree = account.GetTree(); double budgetTotal = -accountTree.GetBudgetSum(year); Int64 deltaTotalCents = accountTree.GetDeltaCents(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1)); double deltaTotal = deltaTotalCents / 100.0; BudgetData result = new BudgetData(); result.AccountName = account.Name; if (budgetTotal > 0.0) { result.PercentActual = actual / budgetTotal * 100.0; result.PercentSuballocated = (budgetTotal - budget) / budgetTotal * 100.0; result.PercentSuballocatedUsed = (deltaTotal - actual) / budgetTotal * 100.0; } return(result); }
private static void ProcessUploadThread(object args) { string guid = ((ProcessThreadArguments)args).Guid; Documents documents = Documents.RecentFromDescription(guid); if (documents.Count != 1) { return; // abort } Document uploadedDoc = documents[0]; FinancialAccount account = ((ProcessThreadArguments)args).Account; ExternalBankData externalData = new ExternalBankData(); externalData.Profile = ExternalBankDataProfile.FromIdentity(ExternalBankDataProfile.SESebId); // TODO: HACK HACK HACK HACK LOAD using ( StreamReader reader = new StreamReader(StorageRoot + uploadedDoc.ServerFileName, Encoding.GetEncoding(1252))) { externalData.LoadData(reader, ((ProcessThreadArguments)args).Organization, account.ForeignCurrency); } _staticDataLookup[guid + "FirstTx"] = externalData.Records[0].DateTime.ToLongDateString(); _staticDataLookup[guid + "LastTx"] = externalData.Records[externalData.Records.Length - 1].DateTime.ToLongDateString(); _staticDataLookup[guid + "TxCount"] = externalData.Records.Length.ToString("N0"); _staticDataLookup[guid + "Profile"] = externalData.Profile; _staticDataLookup[guid + "PercentRead"] = 1; DateTime timeWalker = externalData.Records[0].DateTime; int currentRecordIndex = 0; // Walk past the first identical time records, and verify that we have at least one balance record that matches // our own for this timestamp. There may be several transactions in the master file, but at least one should have // a post-transaction balance that matches our records, or something is much more broken. long swarmopsCentsStart = account.GetDeltaCents(Constants.DateTimeLow, timeWalker.AddSeconds(1)); // At least one of the transactions for this timestamp should match. bool foundMatch = false; while (externalData.Records[currentRecordIndex].DateTime == timeWalker) { if (externalData.Records[currentRecordIndex].AccountBalanceCents == swarmopsCentsStart) { foundMatch = true; // continue loop until on first record past initial conditions } currentRecordIndex++; // no test for array overrun in while } if (!foundMatch) { throw new InvalidOperationException("Unable to locate stable initial conditions for resynchronization"); } // From here on out, every new timestamp should have the exact same delta in Swarmops as it has in the master file. List <ExternalBankMismatchingDateTime> mismatchList = new List <ExternalBankMismatchingDateTime>(); while (currentRecordIndex < externalData.Records.Length) { DateTime lastTimestamp = timeWalker; timeWalker = externalData.Records[currentRecordIndex].DateTime; long swarmopsDeltaCents = account.GetDeltaCents(lastTimestamp.AddSeconds(1), timeWalker.AddSeconds(1)); // "AddSeconds" because DeltaCents operates on ">= lowbound, < highbound" int timestampStartIndex = currentRecordIndex; long masterDeltaCents = externalData.Records[currentRecordIndex++].TransactionNetCents; int masterTransactionCount = 1; while (currentRecordIndex < externalData.Records.Length && externalData.Records[currentRecordIndex].DateTime == timeWalker) { masterDeltaCents += externalData.Records[currentRecordIndex++].TransactionNetCents; masterTransactionCount++; } if (masterDeltaCents != swarmopsDeltaCents) { // We have a mismatch. Add it to the list. ExternalBankMismatchingDateTime newMismatch = new ExternalBankMismatchingDateTime(); newMismatch.DateTime = timeWalker; newMismatch.MasterDeltaCents = masterDeltaCents; newMismatch.MasterTransactionCount = masterTransactionCount; newMismatch.SwarmopsDeltaCents = swarmopsDeltaCents; newMismatch.SwarmopsTransactionCount = 0; // TODO // Load transactions from both sources. First, create the interim construction object. ExternalBankMismatchConstruction mismatchConstruction = new ExternalBankMismatchConstruction(); // Load from Master for (int innerRecordIndex = timestampStartIndex; innerRecordIndex < currentRecordIndex; innerRecordIndex++) { string description = externalData.Records[innerRecordIndex].Description.Replace(" ", " "); if (!mismatchConstruction.Master.ContainsKey(description)) { mismatchConstruction.Master[description] = new ExternalBankMismatchingRecordConstruction(); } mismatchConstruction.Master[description].Cents.Add( externalData.Records[innerRecordIndex].TransactionNetCents); mismatchConstruction.Master[description].Transactions.Add(null); // no dependencies on the master side, only on swarmops side } // Load from Swarmops FinancialAccountRows swarmopsTransactionRows = account.GetRowsFar(lastTimestamp, timeWalker); // the "select far" is a boundary < x <= boundary selector. Default is boundary <= x < boundary. Dictionary <int, FinancialTransaction> lookupTransactions = new Dictionary <int, FinancialTransaction>(); // note all transaction IDs, then sum up per transaction foreach (FinancialAccountRow swarmopsTransactionRow in swarmopsTransactionRows) { lookupTransactions[swarmopsTransactionRow.FinancialTransactionId] = swarmopsTransactionRow.Transaction; } foreach (FinancialTransaction transaction in lookupTransactions.Values) { string description = transaction.Description.Replace(" ", " "); // for legacy compatibility with new importer if (!mismatchConstruction.Swarmops.ContainsKey(description)) { mismatchConstruction.Swarmops[description] = new ExternalBankMismatchingRecordConstruction(); } long cents = transaction[account]; if (cents != 0) // only add nonzero records { mismatchConstruction.Swarmops[description].Cents.Add(transaction[account]); mismatchConstruction.Swarmops[description].Transactions.Add(transaction); } } // Then, parse the intermediate construction object to the presentation-and-action object. Dictionary <string, ExternalBankMismatchingRecordDescription> mismatchingRecordList = new Dictionary <string, ExternalBankMismatchingRecordDescription>(); foreach (string masterKey in mismatchConstruction.Master.Keys) { Dictionary <int, bool> checkMasterIndex = new Dictionary <int, bool>(); Dictionary <int, bool> checkSwarmopsIndex = new Dictionary <int, bool>(); // For each key and entry for each key; // 1) locate an exact corresponding amount in swarmops records and log; failing that, // 2) if exactly one record left in master and swarmops records, log; failing that, // 3) log the rest of the master OR rest of swarmops records with no corresponding // equivalent with counterpart. (May produce bad results if 2 consistent mismatches // for every description.) ExternalBankMismatchingRecordDescription newRecord = new ExternalBankMismatchingRecordDescription(); newRecord.Description = masterKey; List <long> masterCentsList = new List <long>(); List <long> swarmopsCentsList = new List <long>(); List <object> dependenciesList = new List <object>(); List <FinancialTransaction> transactionsList = new List <FinancialTransaction>(); List <ExternalBankMismatchResyncAction> actionsList = new List <ExternalBankMismatchResyncAction>(); // STEP 1 - locate all identical matches if (mismatchConstruction.Swarmops.ContainsKey(masterKey)) { for (int masterIndex = 0; masterIndex < mismatchConstruction.Master[masterKey].Cents.Count; masterIndex++) { // no "continue" necessary on first run-through; nothing has been checked off yet long findMasterCents = mismatchConstruction.Master[masterKey].Cents[masterIndex]; for (int swarmopsIndex = 0; swarmopsIndex < mismatchConstruction.Swarmops[masterKey].Cents.Count; swarmopsIndex++) { if (checkSwarmopsIndex.ContainsKey(swarmopsIndex)) { continue; // may have been checked off already in the rare case of twin identical amounts } if (findMasterCents == mismatchConstruction.Swarmops[masterKey].Cents[swarmopsIndex]) { // There is a match as per case 1. Record both, mark both as used, continue. masterCentsList.Add(findMasterCents); swarmopsCentsList.Add( mismatchConstruction.Swarmops[masterKey].Cents[swarmopsIndex]); // should be equal, we're defensive here transactionsList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex]); dependenciesList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex] .Dependency); actionsList.Add(ExternalBankMismatchResyncAction.NoAction); checkMasterIndex[masterIndex] = true; checkSwarmopsIndex[swarmopsIndex] = true; break; } } } } // STEP 2 - if exactly one record left on both sides, connect and log as mismatching record // TODO: improve logic to handle same number of records left on both sides if (mismatchConstruction.Swarmops.ContainsKey(masterKey) && mismatchConstruction.Master[masterKey].Cents.Count - checkMasterIndex.Keys.Count == 1 && mismatchConstruction.Swarmops[masterKey].Cents.Count - checkSwarmopsIndex.Keys.Count == 1) { for (int masterIndex = 0; masterIndex < mismatchConstruction.Master[masterKey].Cents.Count; masterIndex++) { if (checkMasterIndex.ContainsKey(masterIndex)) { continue; // This will fire for all but one indexes } long findMasterCents = mismatchConstruction.Master[masterKey].Cents[masterIndex]; for (int swarmopsIndex = 0; swarmopsIndex < mismatchConstruction.Swarmops[masterKey].Cents.Count; swarmopsIndex++) { if (checkSwarmopsIndex.ContainsKey(swarmopsIndex)) { continue; } masterCentsList.Add(findMasterCents); swarmopsCentsList.Add(mismatchConstruction.Swarmops[masterKey].Cents[swarmopsIndex]); dependenciesList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex].Dependency); transactionsList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex]); actionsList.Add(ExternalBankMismatchResyncAction.RewriteSwarmops); checkMasterIndex[masterIndex] = true; checkSwarmopsIndex[swarmopsIndex] = true; } } } // STEP 3 - log remaining records on both sides as missing counterparts. Only one of these should fire. // STEP 3a - log remaining on Master side if (mismatchConstruction.Master[masterKey].Cents.Count > checkMasterIndex.Keys.Count) { for (int masterIndex = 0; masterIndex < mismatchConstruction.Master[masterKey].Cents.Count; masterIndex++) { if (checkMasterIndex.ContainsKey(masterIndex)) { continue; } masterCentsList.Add(mismatchConstruction.Master[masterKey].Cents[masterIndex]); swarmopsCentsList.Add(0); // null equivalent; invalid value dependenciesList.Add(null); transactionsList.Add(null); actionsList.Add(ExternalBankMismatchResyncAction.CreateSwarmops); checkMasterIndex[masterIndex] = true; } } // STEP 3b - log remaining on Swarmops side if (mismatchConstruction.Swarmops.ContainsKey(masterKey) && mismatchConstruction.Swarmops[masterKey].Cents.Count > checkSwarmopsIndex.Keys.Count) { for (int swarmopsIndex = 0; swarmopsIndex < mismatchConstruction.Swarmops[masterKey].Cents.Count; swarmopsIndex++) { if (checkSwarmopsIndex.ContainsKey(swarmopsIndex)) { continue; } masterCentsList.Add(0); // null equivalent; invalid value swarmopsCentsList.Add(mismatchConstruction.Swarmops[masterKey].Cents[swarmopsIndex]); transactionsList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex]); if (mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex].Dependency != null) { dependenciesList.Add( mismatchConstruction.Swarmops[masterKey].Transactions[swarmopsIndex].Dependency); actionsList.Add(ExternalBankMismatchResyncAction.ManualAction); // can't auto } else { dependenciesList.Add(null); actionsList.Add(ExternalBankMismatchResyncAction.DeleteSwarmops); } checkMasterIndex[swarmopsIndex] = true; } } newRecord.MasterCents = masterCentsList.ToArray(); newRecord.SwarmopsCents = swarmopsCentsList.ToArray(); newRecord.ResyncActions = actionsList.ToArray(); // newRecord.TransactionDependencies = dependenciesList.ToArray(); newRecord.Transactions = transactionsList.ToArray(); mismatchingRecordList[masterKey] = newRecord; } // Finally, add the transactions that were (described) in Swarmops but not in Master foreach (string swarmopsKey in mismatchConstruction.Swarmops.Keys) { if (!mismatchingRecordList.ContainsKey(swarmopsKey)) { mismatchingRecordList[swarmopsKey] = new ExternalBankMismatchingRecordDescription(); mismatchingRecordList[swarmopsKey].Description = swarmopsKey; mismatchingRecordList[swarmopsKey].SwarmopsCents = mismatchConstruction.Swarmops[swarmopsKey].Cents.ToArray(); mismatchingRecordList[swarmopsKey].Transactions = mismatchConstruction.Swarmops[swarmopsKey].Transactions.ToArray(); mismatchingRecordList[swarmopsKey].MasterCents = new long[mismatchConstruction.Swarmops[swarmopsKey].Cents.Count]; // inits to zero mismatchingRecordList[swarmopsKey].ResyncActions = new ExternalBankMismatchResyncAction[ mismatchConstruction.Swarmops[swarmopsKey].Cents.Count]; for (int index = 0; index < mismatchingRecordList[swarmopsKey].ResyncActions.Length; index++) { mismatchingRecordList[swarmopsKey].ResyncActions[index] = ExternalBankMismatchResyncAction.DeleteSwarmops; } } } newMismatch.MismatchingRecords = mismatchingRecordList.Values.ToArray(); mismatchList.Add(newMismatch); } int percentProcessed = (int)(currentRecordIndex * 100L / externalData.Records.Length); lock (_staticDataLookup) { if (percentProcessed > 1) { _staticDataLookup[guid + "PercentRead"] = percentProcessed; // for the progress bar to update async } if (percentProcessed > 99) { // Placed inside loop to have a contiguous lock block, even though it decreases performance. // Should normally be placed just outside. _staticDataLookup[guid + "MismatchArray"] = mismatchList.ToArray(); _staticDataLookup[guid + "Account"] = account; } } } }
protected void Page_Load(object sender, EventArgs e) { Response.ContentType = "application/json"; string yearString = Request.QueryString["Year"]; string monthString = Request.QueryString["Month"]; int year = Int32.Parse(yearString); int month = Int32.Parse(monthString); if (!CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Read))) { throw new UnauthorizedAccessException("Access denied because security tokens say so"); } DateTime periodStart, periodEnd; bool zeroStart = false; bool zeroEnd = false; bool displayDescription = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.BookkeepingDetails, AccessType.Read)); // HACK HACK HACK if (CurrentOrganization.Identity == 8 && CurrentOrganization.Name.StartsWith("Rick Falkvinge")) { // TODO displayDescription = true; // FIX THIS WITH A DAMN SETTING, YOU MORON, DON'T HARDCODE IT } bool canSeeAudit = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Auditing, AccessType.Read)); if (month > 0 && month <= 12) { periodStart = new DateTime(year, month, 1); periodEnd = periodStart.AddMonths(1); } else if (month > 20 && month < 25) // quarters 1..4 are coded as months 21..24 { periodStart = new DateTime(year, (month - 21) * 3 + 1, 1); periodEnd = periodStart.AddMonths(3); } else if (month == 31) // "whole year" is coded as month 31 { periodStart = new DateTime(year, 1, 1); periodEnd = new DateTime(year + 1, 1, 1); } else { throw new ArgumentException("Invalid month supplied: " + month.ToString(CultureInfo.InvariantCulture)); } DateTime profitLossStart = new DateTime(periodStart.Year, 1, 1); DateTime balanceStart = Constants.DateTimeLow; FinancialAccounts accounts = FinancialAccounts.ForOrganization(CurrentOrganization); FinancialAccountRows rows = accounts.GetRows(periodStart, periodEnd); StringBuilder result = new StringBuilder(16384); Dictionary <int, Int64> runningBalanceLookup = new Dictionary <int, long>(); Dictionary <int, FinancialAccount> accountLookup = new Dictionary <int, FinancialAccount>(); foreach (FinancialAccount accountLoop in accounts) { accountLookup[accountLoop.Identity] = accountLoop; } int currentTransactionId = 0; int rowCount = 0; foreach (FinancialAccountRow row in rows) { string creditString = string.Empty; string debitString = string.Empty; FinancialAccount account = accountLookup[row.FinancialAccountId]; if (!runningBalanceLookup.ContainsKey(row.FinancialAccountId)) { if (account.AccountType == FinancialAccountType.Asset || account.AccountType == FinancialAccountType.Debt) { runningBalanceLookup[account.Identity] = account.GetDeltaCents(balanceStart, periodStart); } else { runningBalanceLookup[account.Identity] = account.GetDeltaCents(profitLossStart, periodStart); } } if (row.FinancialTransactionId != currentTransactionId) { // We're starting a new transaction here // If it's not the first one, close the previous one first if (currentTransactionId != 0) { result.Append("]},"); } FinancialTransaction transaction = row.Transaction; string description = transaction.Description; string hasDoxString = "<img src='/Images/Icons/iconshock-search-256px.png' onmouseover=\"this.src='/Images/Icons/iconshock-search-hot-256px.png';\" onmouseout=\"this.src='/Images/Icons/iconshock-search-256px.png';\" data-txid='{0}' class='LocalViewDox' style='cursor:pointer' height='20' width='20' />" + "<img src='/Images/Icons/iconshock-download-240px.png' onmouseover=\"this.src='/Images/Icons/iconshock-download-hot-240px.png';\" onmouseout=\"this.src='/Images/Icons/iconshock-download-240px.png';\" data-docid='{1}' data-docname=\"{2}\" class='LocalDownloadDox' style='cursor:pointer' height='18' width='18' />"; string actionHtml = string.Empty; Documents documents = Documents.ForObject(transaction.Dependency ?? transaction); if (documents.Count > 0) { foreach (Document doc in documents) { actionHtml += String.Format("<a href='/Pages/v5/Support/StreamUpload.aspx?DocId={0}&hq=1' data-caption=\"{1}\" class='FancyBox_Gallery' data-fancybox='{2}'></a>", doc.Identity, doc.ClientFileName.Replace("\"", "'"), transaction.Identity); } actionHtml = String.Format(hasDoxString, row.FinancialTransactionId.ToString(CultureInfo.InvariantCulture), documents[0].Identity, CurrentOrganization.Name + " - " + Resources.Global.Financial_GeneralLedger + " " + transaction.DateTime.ToShortDateString() + " - " + Resources.Global.Financial_TransactionIdShort + transaction.OrganizationSequenceId.ToString("N0")) + "<span class='hiddenDocLinks'>" + actionHtml + "</span>"; } result.Append("{" + String.Format( "\"id\":\"{0:N0}\",\"idDisplay\":\"<span class='weight-more-emphasis'>{0:N0}</span>\",\"datetime\":\"<span class='weight-more-emphasis'>{1:MMM-dd HH:mm}</span>\",\"txDescription\":\"<span class='weight-more-emphasis tx-description'>{2}</span>\",\"action\":\"{3}\"," + "\"state\":\"open\",\"children\":[", row.Transaction.OrganizationSequenceId, row.TransactionDateTime, JsonSanitize(description), JsonSanitize(actionHtml))); if (transaction.Dependency != null) { IHasIdentity dependency = transaction.Dependency; string info = string.Empty; Documents docs = null; if (dependency is VatReport) { VatReport report = (VatReport)dependency; if (report.OpenTransactionId == transaction.Identity) { info = String.Format(Resources.Pages.Ledgers.InspectLedgers_TxInfo_OpenVatReport, report.DescriptionShort); } else if (report.CloseTransactionId == transaction.Identity) { info = String.Format(Resources.Pages.Ledgers.InspectLedgers_TxInfo_CloseVatReport, report.DescriptionShort); } } // TODO: Continue here with adding Info row under transactions where it's helpful // TODO: Remember that the Info row needs cell merging, colspan=5 or =6 } /* * result.Append("{" + String.Format( * "\"id\":\"{0:N0}\",\"datetime\":\"{1:MMM-dd HH:mm}\",\"description\":\"{2}\"," + * "\"action\":\"{6}\",\"state\":\"open\",\"children\":[", * row.Transaction.OrganizationSequenceId, * row.TransactionDateTime, * JsonSanitize(description), * debitString, * creditString, * runningBalanceLookup[row.FinancialAccountId]/100.0, * JsonSanitize(actionHtml))); */ currentTransactionId = row.FinancialTransactionId; rowCount = 1; } else { // still same transaction result.Append(","); rowCount++; } if (row.AmountCents < 0) { creditString = String.Format("{0:N2}", row.AmountCents / 100.0); } else if (row.AmountCents > 0) { debitString = String.Format("{0:N2}", row.AmountCents / 100.0); } runningBalanceLookup[row.FinancialAccountId] += row.AmountCents; /* * if (canSeeAudit) * { * actionHtml += * String.Format ( * " <img src=\"/Images/Icons/iconshock-flag-white-16px.png\" class=\"LocalIconFlag\" txId=\"{0}\" />", * row.FinancialTransactionId.ToString (CultureInfo.InvariantCulture)); * }*/ //result.Append("{\"description\":\"child\"}"); string accountClass; switch (accountLookup[row.FinancialAccountId].AccountType) { case FinancialAccountType.Asset: accountClass = Resources.Global.Financial_Asset; break; case FinancialAccountType.Debt: accountClass = Resources.Global.Financial_Debt; break; case FinancialAccountType.Income: accountClass = Resources.Global.Financial_Income; break; case FinancialAccountType.Cost: accountClass = Resources.Global.Financial_Cost; break; default: throw new NotImplementedException(); } string accountName = account.Name; if (account.ParentIdentity != 0) { if (!accountLookup.ContainsKey(account.ParentIdentity)) { accountLookup[account.ParentIdentity] = account.Parent; } accountName = accountLookup[account.ParentIdentity].Name + " » " + accountName; } result.Append("{" + String.Format( "\"id\":\"{0:N0}-{6:N0}\",\"idDisplay\":\"{0:N0}:{6:N0}\",\"datetime\":\"{1}\",\"txDescription\":\"{2}\"," + "\"deltaPos\":\"{3}\",\"deltaNeg\":\"{4}\",\"balance\":\"{5:N2}\"", row.Transaction.OrganizationSequenceId, JsonSanitize(accountClass), JsonSanitize(accountName), debitString, creditString, runningBalanceLookup[row.FinancialAccountId] / 100.0, rowCount) + "}"); } if (rows.Count == 0) { // If there are no transactions in this time period, say so result.Append("{\"description\":\"" + JsonSanitize(Resources.Pages.Ledgers.InspectLedgers_NoTransactions) + "\"},"); } Response.Output.WriteLine("[" + result.ToString().TrimEnd(',') + "]}]"); Response.End(); }
protected void Page_Load(object sender, EventArgs e) { Response.ContentType = "application/json"; string accountIdString = Request.QueryString["AccountId"]; string yearString = Request.QueryString["Year"]; string monthString = Request.QueryString["Month"]; string emptyResponse = "[{\"id\":\"-\",\"description\":\"" + JsonSanitize(Resources.Pages.Ledgers.InspectLedgers_PleaseSelectAccount) + "\"}]"; if (string.IsNullOrEmpty(accountIdString) || string.IsNullOrEmpty(yearString) || string.IsNullOrEmpty(monthString) || accountIdString == "undefined") { Response.Output.WriteLine(emptyResponse); Response.End(); } int accountId = Int32.Parse(accountIdString); int year = Int32.Parse(yearString); int month = Int32.Parse(monthString); DateTime dawnOfMankind = new DateTime(1901, 1, 1); // no org will ever import bookkeeping from before this date if (accountId <= 0) { Response.Output.WriteLine(emptyResponse); Response.End(); } FinancialAccount account = FinancialAccount.FromIdentity(accountId); if (account.OrganizationId != CurrentOrganization.Identity) { throw new UnauthorizedAccessException("All the nopes in the world"); } if (!CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Read))) { throw new UnauthorizedAccessException("Access denied because security tokens say so"); } DateTime periodStart, periodEnd; DateTime balanceStart = dawnOfMankind; bool zeroStart = false; bool zeroEnd = false; bool displayDescription = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.BookkeepingDetails, AccessType.Read)); bool canSeeAudit = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Auditing, AccessType.Read)); if (month > 0 && month <= 12) { periodStart = new DateTime(year, month, 1); periodEnd = periodStart.AddMonths(1); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { balanceStart = new DateTime(year, 1, 1); if (month == 1) { zeroStart = true; } if (month == 12) { zeroEnd = true; } } } else if (month > 20 && month < 25) // quarters 1..4 are coded as months 21..24 { periodStart = new DateTime(year, (month - 21) * 3 + 1, 1); periodEnd = periodStart.AddMonths(3); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { balanceStart = new DateTime(year, 1, 1); if (month == 21) { zeroStart = true; } if (month == 24) { zeroEnd = true; } } } else if (month == 31) // "whole year" is coded as month 31 { periodStart = new DateTime(year, 1, 1); periodEnd = new DateTime(year + 1, 1, 1); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { zeroStart = true; zeroEnd = true; } } else { throw new ArgumentException("Invalid month supplied: " + month.ToString(CultureInfo.InvariantCulture)); } FinancialAccountRows rows = account.GetRows(periodStart, periodEnd); StringBuilder result = new StringBuilder(16384); Int64 runningBalance = 0L; string startString = Resources.Pages.Ledgers.InspectLedgers_InboundBalanceZero; string endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalanceZero; if (!zeroStart) { runningBalance = account.GetDeltaCents(balanceStart, periodStart); startString = Resources.Pages.Ledgers.InspectLedgers_InboundBalance; } if (!zeroEnd) { endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalance; } else if (periodEnd > DateTime.Now) { // account is zeroed at end of this period, but we're not yet at end of period, so add a "to date" disclaimer endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalanceZeroToDate; } result.Append("{" + String.Format("\"description\":\"{0}\",\"balance\":\"{1:N0}\"", JsonSanitize(startString), runningBalance / 100.0) + "},"); foreach (FinancialAccountRow row in rows) { string creditString = string.Empty; string debitString = string.Empty; string description = row.Description; if (!displayDescription) { description = Resources.Pages.Ledgers.InspectLedgers_TxDetail_DescriptionWithheld; } if (row.AmountCents < 0) { creditString = String.Format("{0:N0}", row.AmountCents / 100.0); } else if (row.AmountCents > 0) { debitString = String.Format("{0:N0}", row.AmountCents / 100.0); } runningBalance += row.AmountCents; string actionHtml = String.Format( "<img src=\"/Images/Icons/iconshock-magnifyingglass-16px.png\" class=\"LocalIconInspect\" txId=\"{0}\" />", row.FinancialTransactionId.ToString(CultureInfo.InvariantCulture)); if (canSeeAudit) { actionHtml += String.Format( " <img src=\"/Images/Icons/iconshock-flag-white-16px.png\" class=\"LocalIconFlag\" txId=\"{0}\" />", row.FinancialTransactionId.ToString(CultureInfo.InvariantCulture)); } result.Append("{" + String.Format( "\"id\":\"{0:N0}\",\"datetime\":\"{1:MMM-dd HH:mm}\",\"description\":\"{2}\"," + "\"deltaPos\":\"{3}\",\"deltaNeg\":\"{4}\",\"balance\":\"{5:N0}\",\"action\":\"{6}\"", row.FinancialTransactionId, row.TransactionDateTime, JsonSanitize(description), debitString, creditString, runningBalance / 100.0, JsonSanitize(actionHtml)) + "},"); } if (rows.Count == 0) { // If there are no transactions in this time period, say so result.Append("{\"description\":\"" + JsonSanitize(Resources.Pages.Ledgers.InspectLedgers_NoTransactions) + "\"},"); } result.Append("{" + String.Format("\"description\":\"{0}\",\"balance\":\"{1:N0}\"", JsonSanitize(endString), runningBalance / 100.0) + "},"); Response.Output.WriteLine("[" + result.ToString().TrimEnd(',') + "]"); Response.End(); }
public static ChangeAccountDataResult SetAccountInitialBalance(int accountId, string newInitialBalanceString) { try { AuthenticationData authData = GetAuthenticationDataAndCulture(); FinancialAccount account = FinancialAccount.FromIdentity(accountId); if (!PrepareAccountChange(account, authData, false) || authData.CurrentOrganization.Parameters.FiscalBooksClosedUntilYear >= authData.CurrentOrganization.FirstFiscalYear) { return(new ChangeAccountDataResult { Result = ChangeAccountDataOperationsResult.NoPermission }); } Int64 desiredInitialBalanceCents = (Int64) (Double.Parse(newInitialBalanceString, NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.CurrentCulture) * 100.0); Int64 currentInitialBalanceCents = account.GetDeltaCents(new DateTime(1900, 1, 1), new DateTime(authData.CurrentOrganization.FirstFiscalYear, 1, 1)); Int64 deltaCents = desiredInitialBalanceCents - currentInitialBalanceCents; // Find or create "Initial Balances" transaction FinancialAccountRows testRows = FinancialAccountRows.ForOrganization(authData.CurrentOrganization, new DateTime(1900, 1, 1), new DateTime(authData.CurrentOrganization.FirstFiscalYear, 1, 1)); FinancialTransaction initialBalancesTransaction = null; foreach (FinancialAccountRow row in testRows) { if (row.Transaction.Description == "Initial Balances") { initialBalancesTransaction = row.Transaction; break; } } if (initialBalancesTransaction == null) { // create transaction initialBalancesTransaction = FinancialTransaction.Create(authData.CurrentOrganization.Identity, new DateTime(authData.CurrentOrganization.FirstFiscalYear - 1, 12, 31), "Initial Balances"); } Dictionary <int, Int64> recalcBase = initialBalancesTransaction.GetRecalculationBase(); int equityAccountId = authData.CurrentOrganization.FinancialAccounts.DebtsEquity.Identity; if (!recalcBase.ContainsKey(accountId)) { recalcBase[accountId] = 0; } if (!recalcBase.ContainsKey(equityAccountId)) { recalcBase[equityAccountId] = 0; } recalcBase[accountId] += deltaCents; recalcBase[equityAccountId] -= deltaCents; initialBalancesTransaction.RecalculateTransaction(recalcBase, authData.CurrentUser); return(new ChangeAccountDataResult { Result = ChangeAccountDataOperationsResult.Changed, NewData = (desiredInitialBalanceCents / 100.0).ToString("N2", CultureInfo.CurrentCulture) }); } catch (Exception weirdException) { SwarmDb.GetDatabaseForWriting() .CreateExceptionLogEntry(DateTime.UtcNow, "AccountPlan-SetInitBalance", weirdException); throw; } }
protected void Page_Load(object sender, EventArgs e) { Response.ContentType = "application/json"; string yearString = Request.QueryString["Year"]; string monthString = Request.QueryString["Month"]; string emptyResponse = "[{\"id\":\"-\",\"description\":\"" + JsonSanitize(Resources.Pages.Ledgers.InspectLedgers_PleaseSelectAccount) + "\"}]"; if (string.IsNullOrEmpty(yearString) || string.IsNullOrEmpty(monthString)) { Response.Output.WriteLine(emptyResponse); Response.End(); } int year = Int32.Parse(yearString); int month = Int32.Parse(monthString); FinancialAccount account = CurrentOrganization.FinancialAccounts.AssetsBitcoinHot; if (account == null || account.OrganizationId != CurrentOrganization.Identity) { throw new UnauthorizedAccessException("All the nopes in the world"); } if (!CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Read)) && CurrentOrganization.HasOpenLedgers) { throw new UnauthorizedAccessException("Access denied because security tokens say so"); } DateTime periodStart, periodEnd; DateTime balanceStart = Constants.DateTimeLow; bool zeroStart = false; bool zeroEnd = false; bool displayDescription = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.BookkeepingDetails, AccessType.Read)); // HACK HACK HACK if (CurrentOrganization.Identity == 8 && CurrentOrganization.Name.StartsWith("Rick Falkvinge")) { // TODO displayDescription = true; // FIX THIS WITH A DAMN SETTING, YOU MORON, DON'T HARDCODE IT } bool canSeeAudit = CurrentAuthority.HasAccess(new Access(CurrentOrganization, AccessAspect.Auditing, AccessType.Read)); if (month > 0 && month <= 12) { periodStart = new DateTime(year, month, 1); periodEnd = periodStart.AddMonths(1); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { balanceStart = new DateTime(year, 1, 1); if (month == 1) { zeroStart = true; } if (month == 12) { zeroEnd = true; } } } else if (month > 20 && month < 25) // quarters 1..4 are coded as months 21..24 { periodStart = new DateTime(year, (month - 21) * 3 + 1, 1); periodEnd = periodStart.AddMonths(3); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { balanceStart = new DateTime(year, 1, 1); if (month == 21) { zeroStart = true; } if (month == 24) { zeroEnd = true; } } } else if (month == 31) // "whole year" is coded as month 31 { periodStart = new DateTime(year, 1, 1); periodEnd = new DateTime(year + 1, 1, 1); if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost) { zeroStart = true; zeroEnd = true; } } else { throw new ArgumentException("Invalid month supplied: " + month.ToString(CultureInfo.InvariantCulture)); } FinancialAccountRows rows = account.GetRows(periodStart, periodEnd); StringBuilder result = new StringBuilder(16384); Int64 runningBalance = 0L; Int64 runningBitcoinBalance = 0; string startString = Resources.Pages.Ledgers.InspectLedgers_InboundBalanceZero; string endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalanceZero; if (!zeroStart) { runningBitcoinBalance = account.GetForeignCurrencyBalanceDeltaCents(balanceStart, periodStart).Cents; runningBalance = account.GetDeltaCents(balanceStart, periodStart); startString = Resources.Pages.Ledgers.InspectLedgers_InboundBalance; } if (!zeroEnd) { endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalance; } else if (periodEnd > DateTime.Now) { // account is zeroed at end of this period, but we're not yet at end of period, so add a "to date" disclaimer endString = Resources.Pages.Ledgers.InspectLedgers_OutboundBalanceZeroToDate; } result.Append("{" + String.Format("\"description\":\"{0}\",\"balanceBitcoin\":\"{1:N2}\"", JsonSanitize(startString), runningBitcoinBalance / 100.0) + "},"); foreach (FinancialAccountRow row in rows) { string description = row.Description; if (!displayDescription) { description = Resources.Pages.Ledgers.InspectLedgers_TxDetail_DescriptionWithheld; } Int64 deltaSatoshis = row.TransactionRow.AmountForeignCents.Cents; runningBitcoinBalance += deltaSatoshis; string hasDoxString = "<img src='/Images/Icons/iconshock-search-256px.png' onmouseover=\"this.src='/Images/Icons/iconshock-search-hot-256px.png';\" onmouseout=\"this.src='/Images/Icons/iconshock-search-256px.png';\" data-txid='{0}' class='LocalIconInspect' style='cursor:pointer' height='20' width='20' />"; string actionHtml = String.Format(hasDoxString, row.FinancialTransactionId.ToString(CultureInfo.InvariantCulture)); /* * if (canSeeAudit) * { * actionHtml += * String.Format( * " <img src=\"/Images/Icons/iconshock-flag-white-16px.png\" class=\"LocalIconFlag\" txId=\"{0}\" />", * row.FinancialTransactionId.ToString(CultureInfo.InvariantCulture)); * }*/ result.Append("{" + String.Format( "\"id\":\"{0:N0}\",\"datetime\":\"{1:MMM-dd HH:mm}\",\"description\":\"{2}\"," + "\"deltaPresentation\":\"{3}\",\"deltaBitcoin\":\"{4}\",\"balanceBitcoin\":\"{5:N2}\",\"action\":\"{6}\"", row.Transaction.OrganizationSequenceId, row.TransactionDateTime, JsonSanitize(description), row.AmountCents != 0? (row.AmountCents / 100.0).ToString("N2") : string.Empty, deltaSatoshis != 0? (deltaSatoshis / 100.0).ToString("N2"): string.Empty, runningBitcoinBalance / 100.0, JsonSanitize(actionHtml)) + "},"); } if (rows.Count == 0) { // If there are no transactions in this time period, say so result.Append("{\"description\":\"" + JsonSanitize(Resources.Pages.Ledgers.InspectLedgers_NoTransactions) + "\"},"); } result.Append("{" + String.Format("\"description\":\"{0}\",\"balanceBitcoin\":\"{1:N2}\"", JsonSanitize(endString), runningBitcoinBalance / 100.0) + "},"); Response.Output.WriteLine("[" + result.ToString().TrimEnd(',') + "]"); Response.End(); }
protected void Page_Load(object sender, EventArgs e) { this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Financials, AccessType.Read); _authenticationData = GetAuthenticationDataAndCulture(); OutstandingAccountType accountType = OutstandingAccountType.ExpenseClaims; DateTime targetDateTime = DateTime.Today.AddDays(1); bool renderPresentTime = true; FinancialAccount balanceAccount = null; Int64 ledgerExpectedCents; OutstandingAccounts outstandingAccounts = null; bool reverseLedgerSign = false; if (Request.QueryString ["Year"] != null) { targetDateTime = _authenticationData.CurrentOrganization.GetEndOfFiscalYear(Int32.Parse((string)Request.QueryString["Year"], CultureInfo.InvariantCulture)); renderPresentTime = false; } if (Request.QueryString ["AccountType"] != null) { accountType = (OutstandingAccountType)Enum.Parse(typeof(OutstandingAccountType), Request.QueryString["AccountType"]); // Will throw on invalid input. Sucks to be hacker-wannabe } switch (accountType) { case OutstandingAccountType.ExpenseClaims: balanceAccount = _authenticationData.CurrentOrganization.FinancialAccounts.DebtsExpenseClaims; outstandingAccounts = GetOutstandingExpenseClaims(renderPresentTime, targetDateTime); reverseLedgerSign = true; // Expenses is debt and negative in ledger break; case OutstandingAccountType.CashAdvances: outstandingAccounts = GetOutstandingCashAdvances(renderPresentTime, targetDateTime); balanceAccount = _authenticationData.CurrentOrganization.FinancialAccounts.AssetsOutstandingCashAdvances; break; default: throw new NotImplementedException("Unimplemented Outstanding Account Type"); } if (renderPresentTime) { ledgerExpectedCents = balanceAccount.GetDeltaCents(DateTime.MinValue, DateTime.MaxValue); // get ALL transactions } else { ledgerExpectedCents = balanceAccount.GetDeltaCents(DateTime.MinValue, targetDateTime); } if (reverseLedgerSign) { ledgerExpectedCents = -ledgerExpectedCents; } Response.ContentType = "application/json"; Response.Output.WriteLine(FormatJson(outstandingAccounts, ledgerExpectedCents)); Response.End(); }
private void PopulateBudgetData() { Ledger.Accounts = FinancialAccounts.FromSingle(_account); int year = Int32.Parse(this.DropYears.SelectedValue); Ledger.DateStart = new DateTime(year, 1, 1); Ledger.DateEnd = new DateTime(year, 12, 31); Ledger.MaxAmount = 1.0e12m; double budget = _account.GetBudget(year); Ledger.Populate(); // Populate predictions int monthIterator = 1; Int64[] monthlyPredicts = _account.GetBudgetMonthly(year); while (monthIterator <= 12) { TextBox box = (TextBox) this.Master.FindControl("BodyContent").FindControl("TextPredictMonth" + monthIterator.ToString()); if (monthlyPredicts.Length == 12) { box.Text = (monthlyPredicts[monthIterator - 1] / 100).ToString("N0", new CultureInfo("sv-SE")); } else { box.Text = string.Empty; } monthIterator++; } // Populate actuals monthIterator = 1; while (monthIterator <= 12 && new DateTime(year, monthIterator, 1).AddMonths(1) < DateTime.Today) { DateTime start = new DateTime(year, monthIterator, 1); Int64 deltaCents = _account.GetDeltaCents(start, start.AddMonths(1)); decimal deltaDecimal = -deltaCents / 100.0m; TextBox actual = (TextBox)this.Master.FindControl("BodyContent").FindControl("TextActualsMonth" + monthIterator.ToString()); actual.Text = deltaDecimal.ToString("N0", new CultureInfo(_account.Organization.DefaultCountry.Culture)); monthIterator++; } // Clear the non-set actuals fields while (monthIterator <= 12) { TextBox actual = (TextBox)this.Master.FindControl("BodyContent").FindControl("TextActualsMonth" + monthIterator.ToString()); actual.Text = string.Empty; monthIterator++; } InitSuballocation(true); ScriptManager.RegisterStartupScript(this, Page.GetType(), "set_budget_iframe", "var currWidth = document.getElementById('DivIframeContainer').offsetWidth; document.getElementById('DivIframeContainer').innerHTML='<iframe src=\"BudgetUsageBar.aspx?AccountId=" + _account.Identity.ToString() + "&Year=" + year.ToString() + "&Width=' + currWidth + '\" width=\"100%\" scrolling=\"no\" height=\"100\" frameBorder=\"0\" />';", true); }
public static ImportExternalTransactionDataResults ImportExternalTransactionData(ExternalBankData import, ImportExternalTransactionDataArgs args) { FinancialAccount assetAccount = args.Account; FinancialAccount autoDepositAccount = args.Organization.FinancialAccounts.IncomeDonations; int autoDepositLimit = 0; // Disabled; TODO: this.CurrentOrganization.Parameters.AutoDonationLimit; bool autosetInitialBalance = false; ImportExternalTransactionDataResults result = new ImportExternalTransactionDataResults(); int count = 0; int progressUpdateInterval = import.Records.Length / 40; Int64 importedCentsTotal = 0; if (progressUpdateInterval > 100) { progressUpdateInterval = 100; } ProgressBarBackend progressDisplay = new ProgressBarBackend(args.Guid); Currency organizationCurrency = assetAccount.Organization.Currency; Currency accountCurrency = assetAccount.ForeignCurrency; if (accountCurrency == null) { accountCurrency = organizationCurrency; } FinancialAccountRows existingRows = assetAccount.GetRows(Constants.DateTimeLow, Constants.DateTimeHigh); // gets all if (existingRows.Count == 0) { autosetInitialBalance = true; } foreach (ExternalBankDataRecord row in import.Records) { // Update progress. count++; if (progressUpdateInterval < 2 || count % progressUpdateInterval == 0) { int percent = (count * 99) / import.Records.Length; progressDisplay.Set(percent); } // Update high- and low-water marks. if (row.DateTime < result.EarliestTransaction) { result.EarliestTransaction = row.DateTime; } if (row.DateTime > result.LatestTransaction) { result.LatestTransaction = row.DateTime; } string importKey = row.ImportHash; Int64 amountCents = row.TransactionNetCents; if (amountCents == 0) // defensive programming - these _should_ be duplicated in the interpreter if no "fee" field { amountCents = row.TransactionGrossCents; } Int64 foreignCents = amountCents; importedCentsTotal += amountCents; if (accountCurrency.Identity != organizationCurrency.Identity) { amountCents = new Money(amountCents, accountCurrency, row.DateTime).ToCurrency(organizationCurrency).Cents; } FinancialTransaction transaction = FinancialTransaction.ImportWithStub(args.Organization.Identity, row.DateTime, assetAccount.Identity, amountCents, row.Description, importKey, Sha256.Compute(row.RawData), args.CurrentUser.Identity); if (transaction != null) { // The transaction was created. result.TransactionsImported++; // If non-presentation currency, log the account currency amount as well. if (accountCurrency.Identity != organizationCurrency.Identity) { transaction.Rows[0].AmountForeignCents = new Money(foreignCents, accountCurrency); } if (row.Description.ToLowerInvariant().StartsWith(args.Organization.IncomingPaymentTag)) { // Check for previously imported payment group // TODO: MAKE FLEXIBLE - CALL PAYMENTREADERINTERFACE! // HACK HACK HACK HACK PaymentGroup group = PaymentGroup.FromTag(args.Organization, "SEBGM" + DateTime.Today.Year + // TODO: Get tags from org row.Description.Substring(args.Organization.IncomingPaymentTag.Length).Trim()); if (group != null && group.Open) { // There was a previously imported and not yet closed payment group matching this transaction // Close the payment group and match the transaction against accounts receivable transaction.Dependency = group; group.Open = false; transaction.AddRow(args.Organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents, args.CurrentUser); } } else if (amountCents < 0) { // Autowithdrawal mechanisms removed, condition kept because of downstream else-if conditions } else if (amountCents > 0) { if (row.FeeCents < 0) { // This is always an autodeposit, if there is a fee (which is never > 0.0) transaction.AddRow(args.Organization.FinancialAccounts.CostsBankFees, -row.FeeCents, args.CurrentUser); transaction.AddRow(autoDepositAccount, -row.TransactionGrossCents, args.CurrentUser); } else if (amountCents < autoDepositLimit * 100) { // Book against autoDeposit account. transaction.AddRow(autoDepositAccount, -amountCents, args.CurrentUser); } } } else { // Transaction was not imported; assume duplicate result.DuplicateTransactions++; } } // Import complete. Return true if the bookkeeping account matches the bank data. Int64 databaseAccountBalanceCents; if (accountCurrency.Identity == organizationCurrency.Identity) { databaseAccountBalanceCents = assetAccount.BalanceTotalCents; } else { // foreign-currency account databaseAccountBalanceCents = assetAccount.ForeignCurrencyBalance.Cents; } // Subtract any transactions made after the most recent imported transaction. // This is necessary in case of Paypal and others which continuously feed the // bookkeeping account with new transactions; it will already have fed transactions // beyond the end-of-file. Int64 beyondEofCents = assetAccount.GetDeltaCents(result.LatestTransaction.AddSeconds(1), DateTime.Now.AddDays(2)); // Caution: the "AddSeconds(1)" is not foolproof, there may be other new txs on the same second. if (databaseAccountBalanceCents - beyondEofCents == import.LatestAccountBalanceCents) { Payouts.AutomatchAgainstUnbalancedTransactions(args.Organization); OutboundInvoices.AutomatchAgainstUnbalancedTransactions(args.Organization, args.CurrentUser); result.AccountBalanceMatchesBank = true; result.BalanceMismatchCents = 0; } else { result.AccountBalanceMatchesBank = false; result.BalanceMismatchCents = (databaseAccountBalanceCents - beyondEofCents) - import.LatestAccountBalanceCents; if (autosetInitialBalance) { Int64 newInitialBalanceCents = -result.BalanceMismatchCents; Money initialBalance = new Money(newInitialBalanceCents, accountCurrency); assetAccount.InitialBalance = initialBalance; result.InitialBalanceCents = newInitialBalanceCents; result.InitialBalanceCurrencyCode = accountCurrency.Code; // make an approximation of conversion rate set for initial balance in presentation to tell user initialBalance.ValuationDateTime = new DateTime(assetAccount.Organization.FirstFiscalYear, 1, 1); result.BalanceMismatchCents = initialBalance.ToCurrency(assetAccount.Organization.Currency).Cents; } } result.CurrencyCode = args.Organization.Currency.Code; GuidCache.Set(args.Guid + "-Results", result); return(result); }