private void AddDocuments(VatReport report) { VatReportItems items = report.Items; foreach (VatReportItem item in items) { FinancialTransaction tx = item.Transaction; Documents documents = Documents.ForObject(tx.Dependency ?? tx); // tx.Dependency if not null, else tx int pageCounter = 0; int pagesTotal = documents.Count; foreach (Document doc in documents) { pageCounter++; VatReportDocuments.Add(new RepeatedDocument { BaseId = item.FinancialTransactionId.ToString(CultureInfo.InvariantCulture), DocId = doc.Identity, Title = tx.Description + " " + Document.GetLocalizedPageCounter(pageCounter, pagesTotal) }); } } }
public static void MatchTransactionOpenVatReport(int transactionId, int vatReportId) { if (transactionId == 0 || vatReportId == 0) { return; } AuthenticationData authData = GetAuthenticationDataAndCulture(); if ( !authData.Authority.HasAccess(new Access(authData.CurrentOrganization, AccessAspect.BookkeepingDetails))) { throw new UnauthorizedAccessException(); } FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId); VatReport vatReport = VatReport.FromIdentity(vatReportId); if (authData.CurrentOrganization.Identity != transaction.OrganizationId || authData.CurrentOrganization.Identity != vatReport.OrganizationId) { throw new InvalidOperationException("Organization mismatch"); } Int64 diffCents = vatReport.VatInboundCents - vatReport.VatOutboundCents; if (transaction.Rows.AmountCentsTotal != diffCents) { throw new InvalidOperationException("Amount mismatch"); } // Go ahead and close vatReport.CloseTransaction = transaction; // throws if already closed vatReport.Open = false; if (diffCents > 0) { // Positive amount - asset account transaction.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsVatInboundReported, -diffCents, null); } else { // Negative amount - liability account transaction.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsVatOutboundReported, -diffCents, null); } }
public static AjaxCallResult CreateVatReport(string itemId) { AuthenticationData authData = GetAuthenticationDataAndCulture(); if (!authData.Authority.HasAccess(new Access(authData.CurrentOrganization, AccessAspect.BookkeepingDetails))) { throw new UnauthorizedAccessException(); } VatReport.CreateNext(authData.CurrentOrganization); return(new AjaxCallResult { Success = true }); }
protected void Page_Load(object sender, EventArgs e) { // Get auth data this._authenticationData = GetAuthenticationDataAndCulture(); string reportIdParameter = Request.QueryString["ReportId"]; string reportKeyParameter = Request.QueryString["ReportKey"]; if (!string.IsNullOrEmpty(reportIdParameter)) { this._reportId = Int32.Parse(reportIdParameter); // will throw if non-numeric - don't matter for app } else if (!string.IsNullOrEmpty(reportKeyParameter)) { this._reportId = SwarmDb.GetDatabaseForReading().GetVatReportIdFromGuid(reportKeyParameter.ToLowerInvariant().Trim()); } else { throw new ArgumentException("No Report Requested"); } VatReport report = VatReport.FromIdentity(_reportId); // TODO: AUTH CHECK Response.ContentType = "application/json"; Int64 turnoverCentsTotal = 0; Int64 inboundCentsTotal = 0; Int64 outboundCentsTotal = 0; StringBuilder response = new StringBuilder(16384); VatReportItems items = report.Items; items.Sort(VatItemSorterByDate); List <string> lines = new List <string>(); string hasDoxString = "<img data-txid='{0}' class='LocalViewDox action-icon' />" + "<img data-docid='{1}' data-docname=\\\"{2}\\\" class='LocalDownloadDox action-icon' />"; foreach (VatReportItem item in items) { FinancialTransaction transaction = item.Transaction; bool include = false; string element = string.Format("\"id\":\"{0}\",\"txid\":\"{1}\",\"datetime\":\"{2:MMM dd}\",\"description\":\"{3}\"", transaction.Identity, transaction.OrganizationSequenceId, transaction.DateTime, JsonSanitize(transaction.Description)); if (item.TurnoverCents != 0) { element += String.Format(",\"turnover\":\"{0:N2}\"", item.TurnoverCents / 100.0); turnoverCentsTotal += item.TurnoverCents; include = true; } if (item.VatInboundCents != 0) { element += String.Format(",\"inbound\":\"{0:N2}\"", item.VatInboundCents / 100.0); inboundCentsTotal += item.VatInboundCents; include = true; } if (item.VatOutboundCents != 0) { element += String.Format(",\"outbound\":\"{0:N2}\"", item.VatOutboundCents / 100.0); outboundCentsTotal += item.VatOutboundCents; include = true; } Documents documents = Documents.ForObject(transaction.Dependency ?? transaction); if (documents.Count > 0) { element += String.Format(",\"dox\":\"" + hasDoxString + "\"", transaction.Identity, documents[0].Identity, CurrentOrganization.Name + " - " + report.Description + " - " + Resources.Global.Financial_TransactionIdShort + transaction.OrganizationSequenceId.ToString("N0")); } if (include) { lines.Add("{" + element + "}"); } } if (lines.Count == 0) // add empty report line { lines.Add("{" + String.Format("\"id\":\"0\",\"description\":\"{0}\"", JsonSanitize(Resources.Pages.Ledgers.ViewVatReports_EmptyReport)) + "}"); } Response.Write("{\"rows\":[" + String.Join(",", lines.ToArray()) + "]"); Response.Write(",\"footer\":[{"); // needs to be on separate line to not trigger a String.Format warning Response.Write(String.Format("\"id\":\"0\",\"description\":\"{0}\",\"turnover\":\"{1:N2}\",\"inbound\":\"{2:N2}\",\"outbound\":\"{3:N2}\"", JsonSanitize(Resources.Pages.Ledgers.ViewVatReports_Footer_Total.ToUpperInvariant()), turnoverCentsTotal / 100.0, inboundCentsTotal / 100.0, outboundCentsTotal / 100.0)); Response.Write("}]}"); // needs to be separate to not trigger String.Format warning Response.End(); }
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) { if (!CurrentOrganization.IsEconomyEnabled) { Response.Redirect("/Pages/v5/Financial/EconomyNotEnabled.aspx", true); return; } PageTitle = Resources.Pages.Ledgers.ViewVatReports_PageTitle; InfoBoxLiteral = Resources.Pages.Ledgers.ViewVatReports_Info; VatReportDocuments = new List <RepeatedDocument>(); // Security: If the org has open ledgers, or key was given, then anyone may read. Otherwise, Financials.Read. int specificReportId = 0; VatReport initialReport = null; VatReport specificReport = null; string reportKey = Request.QueryString["ReportKey"]; if (!string.IsNullOrEmpty(reportKey)) { specificReport = VatReport.FromGuid(reportKey); specificReportId = specificReport.Identity; this.VatReportKey = reportKey; } if (CurrentOrganization.HasOpenLedgers || specificReportId > 0) { PageAccessRequired = new Access(AccessAspect.Null, AccessType.Read); } else { PageAccessRequired = new Access(CurrentOrganization, AccessAspect.Financials, AccessType.Read); } if (!Page.IsPostBack) { Localize(); // Populate VAT report dropdown if (specificReportId > 0 && !CurrentOrganization.HasOpenLedgers) { // Show one single report this.LabelContentHeader.Text = specificReport.Description; this.DropReports.Visible = false; this.InitialReportId = specificReport.Identity; AddDocuments(specificReport); } else { // Populate dropdown and documents list VatReports reports = VatReports.ForOrganization(CurrentOrganization, true); if (reports.Count > 0) { reports.Sort(VatReports.VatReportSorterByDate); foreach (VatReport report in reports) { this.DropReports.Items.Add(new ListItem(report.Description, report.Identity.ToString(CultureInfo.InvariantCulture))); AddDocuments(report); } initialReport = reports.Last(); this.InitialReportId = initialReport.Identity; this.DropReports.SelectedValue = this.InitialReportId.ToString(CultureInfo.InvariantCulture); } else { // There are no VAT reports for this organization (yet?) so display an error instead this.PanelShowVatReports.Visible = false; this.PanelShowNoVatReports.Visible = true; } } } RegisterControl(EasyUIControl.DataGrid | EasyUIControl.Tree); this.RepeaterLightboxItems.DataSource = this.VatReportDocuments; this.RepeaterLightboxItems.DataBind(); }
protected void Page_Load(object sender, EventArgs e) { this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.BookkeepingDetails); this.PageTitle = this.Title = this.LabelHeader.Text = String.Format(Resources.Pages.Ledgers.EndOfMonth_Title, DateTime.UtcNow.AddMonths(-1)); this.InfoBoxLiteral = Resources.Pages.Ledgers.EndOfMonth_Info; // Check which reports are required ItemGroups = new List <EomItemGroup>(); // Group I: External data & accounts EomItemGroup group1 = new EomItemGroup(); group1.Header = Resources.Pages.Ledgers.EndOfMonth_Header_ExternalData; group1.Id = "ExternalData"; string lastUploadItemId = string.Empty; // Iterate over all Balance accounts and check for automation; // if so, add it to an upload sequence FinancialAccounts assetAccounts = FinancialAccounts.ForOrganization(this.CurrentOrganization, FinancialAccountType.Asset); DateTime lastMonthEndDate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1).AddDays(-1); int lastMonth = lastMonthEndDate.Year * 100 + lastMonthEndDate.Month; bool skippable = true; foreach (FinancialAccount assetAccount in assetAccounts) { if (assetAccount.AutomationProfileId != 0) { // This account has automation // If automation has Bank Account Statement enabled (assume true for now): FinancialAccountDocument lastBankStatement = assetAccount.GetMostRecentDocument(FinancialAccountDocumentType.BankStatement); int lastStatementMonth = (this.CurrentOrganization.FirstFiscalYear - 1) * 100 + 12; // December of the year before if (lastBankStatement != null) { lastStatementMonth = lastBankStatement.ConcernsPeriodStart.Year * 100 + lastBankStatement.ConcernsPeriodStart.Month; skippable = false; } string lastId = string.Empty; int monthIterator = lastStatementMonth; while (monthIterator < lastMonth) { monthIterator++; if (monthIterator % 100 == 13) { monthIterator += 88; } EomItem bankStatement = new EomItem(); bankStatement.DependsOn = lastId; // empty for first record bankStatement.Id = lastId = "BankStatement-" + assetAccount.Identity.ToString(CultureInfo.InvariantCulture) + '-' + monthIterator; bankStatement.Name = string.Format(Resources.Pages.Ledgers.EndOfMonth_UploadBankStatementFor, assetAccount.Name, "PDF", new DateTime(monthIterator / 100, monthIterator % 100, 15).ToString("MMMM yyyy")); bankStatement.Completed = false; // TODO bankStatement.Icon = "upload"; bankStatement.Skippable = skippable; group1.Items.Add(bankStatement); } // Add data upload item // TODO: Check if the last data upload was into the current month EomItem dataUploadItem = new EomItem(); dataUploadItem.Id = "BankTransactionData-" + assetAccount.Identity.ToString(CultureInfo.InvariantCulture); dataUploadItem.Icon = "upload"; dataUploadItem.Completed = false; // todo dataUploadItem.Name = String.Format(Resources.Pages.Ledgers.EndOfMonth_UploadTransactionDataFor, assetAccount.Name, "CSV"); dataUploadItem.Skippable = false; group1.Items.Add(dataUploadItem); // Check if we need to add a resync of the hotwallet's native ledger if (CurrentOrganization.FinancialAccounts.AssetsBitcoinHot != null) // has hotwallet { Int64 cashSatoshisInLedger = CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.GetForeignCurrencyBalanceDeltaCents( Constants.DateTimeLow, Constants.DateTimeHigh).Cents; Int64 cashSatoshisInHotwallet = HotBitcoinAddresses.GetSatoshisInHotwallet(CurrentOrganization)[BitcoinChain.Cash]; if (cashSatoshisInHotwallet != cashSatoshisInLedger) { // Resync required EomItem resyncSatoshiCountItem = new EomItem(); resyncSatoshiCountItem.Id = "ResyncSatoshisInLedger"; resyncSatoshiCountItem.Icon = "approve"; resyncSatoshiCountItem.Completed = false; resyncSatoshiCountItem.Skippable = false; resyncSatoshiCountItem.Callback = "ResyncSatoshisInLedger"; resyncSatoshiCountItem.Name = Resources.Pages.Ledgers.EndOfMonth_CheckLedgerAgainstHotWallet; group1.Items.Add(resyncSatoshiCountItem); } } } } // If there are any items in Group 1, OR if an account matching is necessary, then // add that as an action item if (group1.Items.Count() > 0 || FinancialTransactions.GetUnbalanced(this.CurrentOrganization).Count() > 0) { EomItem matchAccounts = new EomItem(); matchAccounts.DependsOn = lastUploadItemId; // may be empty if there's nothing to upload and that's ok matchAccounts.Id = "MatchAccounts"; matchAccounts.Completed = false; // we already know there's at least one unbalanced matchAccounts.Icon = "wrench"; matchAccounts.Skippable = false; matchAccounts.Name = Resources.Pages.Ledgers.EndOfMonth_MatchAccounts; group1.Items.Add(matchAccounts); } if (group1.Items.Count > 0) { ItemGroups.Add(group1); } // Group: Payroll and Taxes EomItemGroup group2 = new EomItemGroup(); group2.Header = Resources.Pages.Ledgers.EndOfMonth_Header_PayrollTaxes; group2.Id = "PayrollTaxes"; ReportRequirement vatRequired = VatReport.IsRequired(this.CurrentOrganization); if (vatRequired == ReportRequirement.Required || vatRequired == ReportRequirement.Completed) { EomItem vatReport = new EomItem(); vatReport.Id = "VatReport"; vatReport.Callback = "CreateVatReport"; vatReport.Completed = (vatRequired == ReportRequirement.Completed ? true : false); vatReport.Name = String.Format(Resources.Pages.Ledgers.EndOfMonth_CreateVatReport, (vatReport.Completed ? VatReport.LastReportDescription(this.CurrentOrganization) : VatReport.NextReportDescription(this.CurrentOrganization))); vatReport.Icon = "document"; group2.Items.Add(vatReport); } Payroll payroll = Payroll.ForOrganization(this.CurrentOrganization); if (payroll.Any()) { // There is active payroll // TODO: Taxes for last month and processing for this month } else { EomItem payrollInactive = new EomItem(); payrollInactive.Id = "PayrollInactive"; payrollInactive.Completed = true; payrollInactive.Name = Resources.Pages.Ledgers.EndOfMonth_PayrollInactive; payrollInactive.Icon = "document"; group2.Items.Add(payrollInactive); } if (group2.Items.Count > 0) { ItemGroups.Add(group2); } // Group: Closure of Ledgers and Annual Reports int lastClosedYear = CurrentOrganization.Parameters.FiscalBooksClosedUntilYear; if (lastClosedYear - 1 < DateTime.UtcNow.Year) { EomItemGroup groupReports = new EomItemGroup(); groupReports.Header = Resources.Pages.Ledgers.EndOfMonth_Header_AnnualReports; EomItem itemCloseYear = new EomItem(); itemCloseYear.Id = "CloseLedgers"; itemCloseYear.Callback = "CloseLedgers"; itemCloseYear.Icon = "document"; itemCloseYear.Name = String.Format(Resources.Pages.Ledgers.EndOfMonth_CloseLedgersFor, lastClosedYear + 1); groupReports.Items.Add(itemCloseYear); ItemGroups.Add(groupReports); } }