public async Task <IActionResult> Edit(int id, [Bind("ParentId,Class,CurrencyId,Id,UserId,LogRecordId,Status,ModDateTime,ModByUserId,Comment")] PaymentGroup paymentGroup) { if (id != paymentGroup.Id) { return(NotFound()); } if (ModelState.IsValid) { try { _context.Update(paymentGroup); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!PaymentGroupExists(paymentGroup.Id)) { return(NotFound()); } else { throw; } } return(RedirectToAction(nameof(Index))); } ViewData["CurrencyId"] = new SelectList(_context.Currencies, "Id", "Id", paymentGroup.CurrencyId); ViewData["ParentId"] = new SelectList(_context.PersonGroups, "Id", "Id", paymentGroup.ParentId); return(View(_mapper.Map <PaymentGroupViewModel>(paymentGroup))); }
public static PaymentGroup ToPaymentGroup(PaymentsGroupViewModel vm, bool showCategorized) { var visiblePayments = vm.GetVisiblePayments(showCategorized); var paymentGroup = new PaymentGroup { IsExpanded = vm.IsExpanded, Amount = visiblePayments.Select(v => v.Amount).Sum(), Ccy = visiblePayments[0].Ccy, Id = vm.Id.ToUUID(), Kind = visiblePayments[0].Kind, PaymentCount = visiblePayments.Count, What = visiblePayments[0].What, When = visiblePayments[0].When.ToTimestamp(), CategoryId = visiblePayments[0].CategoryId.ToUUID(), ColumnId = visiblePayments[0].ColumnId.ToUUID(), DebtId = visiblePayments[0].DebtId.ToUUID() }; if (vm.IsExpanded) { foreach (var v in visiblePayments) { paymentGroup.Payments.Add(new PaymentView { Payment = ToPaymentView(v) }); } } return(paymentGroup); }
public async Task <IActionResult> Create([Bind("ParentId,Class,CurrencyId,Id,UserId,LogRecordId,Status,ModDateTime,ModByUserId,Comment")] PaymentGroup paymentGroup) { if (ModelState.IsValid) { _context.Add(paymentGroup); await _context.SaveChangesAsync(); return(RedirectToAction(nameof(Index))); } ViewData["CurrencyId"] = new SelectList(_context.Currencies, "Id", "Id", paymentGroup.CurrencyId); ViewData["ParentId"] = new SelectList(_context.PersonGroups, "Id", "Id", paymentGroup.ParentId); return(View(_mapper.Map <PaymentGroupViewModel>(paymentGroup))); }
// GET: PaymentGroup/Delete/5 public ActionResult Delete(Nullable <byte> id) { if (id == null) { return(new HttpStatusCodeResult(HttpStatusCode.BadRequest)); } Db db = new Db(DbServices.ConnectionString); PaymentGroup paymentGroup = PaymentGroupServices.Get(id.Value, db); if (paymentGroup == null) { return(HttpNotFound()); } return(View(paymentGroup)); }
protected void GridPaymentGroups_ItemCreated(object sender, GridItemEventArgs e) { if (e.Item is GridDataItem) { PaymentGroup group = (PaymentGroup)e.Item.DataItem; if (group == null) { return; } HyperLink editLink = (HyperLink)e.Item.FindControl("ManageLink"); editLink.Attributes["href"] = "#"; editLink.Attributes["onclick"] = String.Format("return ShowPaymentGroupForm('{0}','{1}');", group.Identity, e.Item.ItemIndex); } }
public ActionResult Create([Bind(Include = "Id, Name")] PaymentGroup paymentGroup) { Db db = new Db(DbServices.ConnectionString); if (ModelState.IsValid) { try { PaymentGroupServices.Insert(CurrentUser.Id, paymentGroup, db); TempData["Success"] = ResourceServices.GetString(Cf.Data.Resources.ResourceBase.Culture, "UI", "InsertConfirmed"); return(RedirectToAction("Index")); } catch (CfException cfex) { TempData["Failure"] = cfex.ErrorDefinition.LocalizedMessage; } catch (Exception ex) { TempData["Failure"] = ex.Message; } } return(View(paymentGroup)); }
static public object FromBasic(IHasIdentity basic) { string argumentType = basic.GetType().ToString(); switch (argumentType) { // ----- Is there any way to make self-writing code here through replication or similar, so // ----- that every case doesn't need to be listed? // ------------ COMMUNICATION CLASSES ------------ case "Swarmops.Basic.Types.BasicCommunicationTurnaround": return(CommunicationTurnaround.FromBasic((BasicCommunicationTurnaround)basic)); case "Swarmops.Basic.Types.Communications.BasicOutboundComm": return(OutboundComm.FromBasic((BasicOutboundComm)basic)); case "Swarmops.Basic.Types.Communications.BasicOutboundCommRecipient": return(OutboundCommRecipient.FromBasic((BasicOutboundCommRecipient)basic)); // ----------- FINANCIAL CLASSES ---------- case "Swarmops.Basic.Types.BasicExpenseClaim": return(ExpenseClaim.FromBasic((BasicExpenseClaim)basic)); case "Swarmops.Basic.Types.Financial.BasicCashAdvance": return(CashAdvance.FromBasic((BasicCashAdvance)basic)); case "Swarmops.Basic.Types.BasicInboundInvoice": return(InboundInvoice.FromBasic((BasicInboundInvoice)basic)); case "Swarmops.Basic.Types.Financial.BasicFinancialAccount": return(FinancialAccount.FromBasic((BasicFinancialAccount)basic)); case "Swarmops.Basic.Types.Financial.BasicFinancialTransaction": return(FinancialTransaction.FromBasic((BasicFinancialTransaction)basic)); case "Swarmops.Basic.Types.BasicFinancialValidation": return(FinancialValidation.FromBasic((BasicFinancialValidation)basic)); case "Swarmops.Basic.Types.BasicOutboundInvoice": return(OutboundInvoice.FromBasic((BasicOutboundInvoice)basic)); case "Swarmops.Basic.Types.BasicOutboundInvoiceItem": return(OutboundInvoiceItem.FromBasic((BasicOutboundInvoiceItem)basic)); case "Swarmops.Basic.Types.BasicPayment": return(Payment.FromBasic((BasicPayment)basic)); case "Swarmops.Basic.Types.BasicPaymentGroup": return(PaymentGroup.FromBasic((BasicPaymentGroup)basic)); case "Swarmops.Basic.Types.BasicPayout": return(Payout.FromBasic((BasicPayout)basic)); case "Swarmops.Basic.Types.BasicPayrollAdjustment": return(PayrollAdjustment.FromBasic((BasicPayrollAdjustment)basic)); case "Swarmops.Basic.Types.BasicPayrollItem": return(PayrollItem.FromBasic((BasicPayrollItem)basic)); case "Swarmops.Basic.Types.BasicSalary": return(Salary.FromBasic((BasicSalary)basic)); case "Swarmops.Basic.Types.Financial.BasicFinancialTransactionTagSet": return(FinancialTransactionTagSet.FromBasic((BasicFinancialTransactionTagSet)basic)); case "Swarmops.Basic.Types.Financial.BasicFinancialTransactionTagType": return(FinancialTransactionTagType.FromBasic((BasicFinancialTransactionTagType)basic)); // ------------ GOVERNANCE CLASSES ------------ case "Swarmops.Basic.Types.BasicBallot": return(Ballot.FromBasic((BasicBallot)basic)); case "Swarmops.Basic.Types.BasicMeetingElectionCandidate": return(MeetingElectionCandidate.FromBasic((BasicInternalPollCandidate)basic)); case "Swarmops.Basic.Types.BasicMeetingElection": return(MeetingElection.FromBasic((BasicInternalPoll)basic)); case "Swarmops.Basic.Types.BasicMeetingElectionVote": return(MeetingElectionVote.FromBasic((BasicInternalPollVote)basic)); case "Swarmops.Basic.Types.Governance.BasicMotion": return(Motion.FromBasic((BasicMotion)basic)); case "Swarmops.Basic.Types.Governance.BasicMotionAmendment": return(MotionAmendment.FromBasic((BasicMotionAmendment)basic)); // ------------ PARLEY/ACTIVISM CLASSES ------------ case "Swarmops.Basic.Types.BasicExternalActivity": return(ExternalActivity.FromBasic((BasicExternalActivity)basic)); case "Swarmops.Basic.Types.BasicParley": return(Parley.FromBasic((BasicParley)basic)); case "Swarmops.Basic.Types.BasicParleyAttendee": return(ParleyAttendee.FromBasic((BasicParleyAttendee)basic)); case "Swarmops.Basic.Types.BasicParleyOption": return(ParleyOption.FromBasic((BasicParleyOption)basic)); case "Swarmops.Basic.Types.BasicPerson": return(Person.FromBasic((BasicPerson)basic)); // ------------------ FAIL ---------------- default: throw new NotImplementedException("Unimplemented argument type: " + argumentType); } }
protected void ImportBgmaxText(string text) { string[] lines = text.Split("\r\n".ToCharArray()); DateTime timestamp = DateTime.MinValue; int bgMaxVersion = 0; PaymentGroup curPaymentGroup = null; Payment curPayment = null; Currency currency = null; Int64 curPaymentGroupAmountCents = 0; foreach (string line in lines) { if (line.Length < 2) { continue; // CR/LF split causes every other line to be empty } switch (line.Substring(0, 2)) { case "01": // BGMAX intro string bgmaxmarker = line.Substring(2, 20).Trim(); if (bgmaxmarker != "BGMAX") { throw new Exception("bad format -- not bgmax"); } bgMaxVersion = Int32.Parse(line.Substring(22, 2)); timestamp = DateTime.ParseExact(line.Substring(24, 20), "yyyyMMddHHmmssffffff", CultureInfo.InvariantCulture); break; case "05": // Begin payment group if (bgMaxVersion < 1) { throw new InvalidOperationException("BGMax record must precede first payment group"); } currency = Currency.FromCode(line.Substring(22, 3)); curPaymentGroup = PaymentGroup.Create(Organization.PPSE, timestamp, currency, _currentUser); curPaymentGroupAmountCents = 0; break; case "20": // Begin payment if (curPaymentGroup == null) { throw new InvalidOperationException("Payment group start must precede first payment"); } string fromAccount = line.Substring(2, 10); string reference = line.Substring(12, 25).Trim(); // left space padded in BgMax format Int64 amountCents = Int64.Parse(line.Substring(37, 18), CultureInfo.InvariantCulture); string key = "SEBGM" + DateTime.Today.Year.ToString() + line.Substring(57, 12); bool hasImage = (line[69] == '1' ? true : false); // TODO: Check if existed already -- must do -- IMPORTANT curPayment = curPaymentGroup.CreatePayment(amountCents, reference, fromAccount, key, hasImage); curPaymentGroupAmountCents += amountCents; break; case "25": // Payment info: Freeform if (curPayment == null) { throw new InvalidOperationException("Payment start must precede payment information"); } curPayment.AddInformation(PaymentInformationType.Freeform, line.Substring(2, 50).Trim()); break; case "26": // Payment info: Name if (curPayment == null) { throw new InvalidOperationException("Payment start must precede payment information"); } curPayment.AddInformation(PaymentInformationType.Name, line.Substring(2, 35).Trim()); break; case "27": // Payment info: Street, postal code if (curPayment == null) { throw new InvalidOperationException("Payment start must precede payment information"); } curPayment.AddInformation(PaymentInformationType.Street, line.Substring(2, 35).Trim()); curPayment.AddInformation(PaymentInformationType.PostalCode, line.Substring(37, 9).Replace(" ", "")); // also removes inspace break; case "28": // Payment info: City, Country if (curPayment == null) { throw new InvalidOperationException("Payment start must precede payment information"); } curPayment.AddInformation(PaymentInformationType.City, line.Substring(2, 35).Trim()); curPayment.AddInformation(PaymentInformationType.Country, line.Substring(37, 35).Trim()); curPayment.AddInformation(PaymentInformationType.CountryCode, line.Substring(72, 2).Trim()); break; case "29": // Payment info: City, Country if (curPayment == null) { throw new InvalidOperationException("Payment start must precede payment information"); } curPayment.AddInformation(PaymentInformationType.OrgNumber, line.Substring(2, 12).Trim()); break; case "15": // End payment group if (curPaymentGroup == null) { throw new InvalidOperationException("Payment group start must precede payment group end"); } string tag = DateTime.Today.Year.ToString() + line.Substring(45, 5); Int64 reportedAmountCents = Int64.Parse(line.Substring(50, 18), CultureInfo.InvariantCulture); curPaymentGroup.AmountCents = curPaymentGroupAmountCents; curPaymentGroup.Tag = tag; curPaymentGroup.Open = true; // flags payment group as ready curPaymentGroup.MapTransaction(); curPaymentGroup = null; break; case "70": // BGMAX termination break; // don't care default: break; // don't care about other fields } } ScriptManager.RegisterStartupScript(this, Page.GetType(), "alldone", "alert ('Timestamp: " + timestamp.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "');", true); }
protected void ProcessImportedData(ImportResult import) { FinancialAccount assetAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAssetAccount.SelectedValue)); FinancialAccount autoDepositAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoDeposits.SelectedValue)); FinancialAccount autoWithdrawalAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoWithdrawals.SelectedValue)); int autoDepositLimit = Int32.Parse(this.TextDepositLimit.Text); int autoWithdrawalLimit = Int32.Parse(this.TextWithdrawalLimit.Text); int organizationId = Int32.Parse(this.DropOrganizations.SelectedValue); Organization organization = Organization.FromIdentity(organizationId); int importedTransactionCount = 0; foreach (ImportedRow row in import.Rows) { // Each row is at least a stub, probably more. // If too old, ignore. if (row.DateTime < new DateTime(2008, 12, 4)) { continue; } string importKey = row.SuppliedTransactionId; // If importKey is empty, construct a hash from the data fields. if (string.IsNullOrEmpty(importKey)) { string hashKey = row.HashBase + row.Comment + (row.AmountCentsNet / 100.0).ToString(CultureInfo.InvariantCulture) + row.CurrentBalance.ToString(CultureInfo.InvariantCulture) + row.DateTime.ToString("yyyy-MM-dd-hh-mm-ss"); importKey = SHA1.Hash(hashKey).Replace(" ", ""); } if (importKey.Length > 30) { importKey = importKey.Substring(0, 30); } Int64 amountCents = row.AmountCentsNet; if (amountCents == 0) { amountCents = row.AmountCentsGross; } FinancialTransaction transaction = FinancialTransaction.ImportWithStub(organizationId, row.DateTime, assetAccount.Identity, amountCents, row.Comment, importKey, _currentUser.Identity); if (transaction != null) { // The transaction was created. Examine if the autobook criteria are true. importedTransactionCount++; FinancialAccounts accounts = FinancialAccounts.FromBankTransactionTag(row.Comment); if (accounts.Count == 1) { // This is a labelled local donation. Geography geography = accounts[0].AssignedGeography; FinancialAccount localAccount = accounts[0]; transaction.AddRow(organization.FinancialAccounts.IncomeDonations, -amountCents, _currentUser); transaction.AddRow(organization.FinancialAccounts.CostsLocalDonationTransfers, amountCents, _currentUser); transaction.AddRow(localAccount, -amountCents, _currentUser); Activizr.Logic.Support.PWEvents.CreateEvent(EventSource.PirateWeb, EventType.LocalDonationReceived, _currentUser.Identity, organizationId, geography.Identity, 0, transaction.Identity, localAccount.Identity.ToString()); } else if (row.Comment.ToLowerInvariant().StartsWith("bg 451-0061 ")) // TODO: Organization.Parameters.FinancialTrackedTransactionPrefix { // Check for previously imported payment group PaymentGroup group = PaymentGroup.FromTag(organization, "SEBGM" + DateTime.Today.Year.ToString() + // TODO: Get tagging from org row.Comment.Substring(11)); if (group != null) { // 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(organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents, _currentUser); } } else if (amountCents < 0) { if ((-amountCents) < autoWithdrawalLimit * 100) { // Book against autoWithdrawal account. transaction.AddRow(autoWithdrawalAccount, -amountCents, _currentUser); } } else if (amountCents > 0) { if (row.Fee < 0) { // This is always an autodeposit, if there is a fee (which is never > 0.0) transaction.AddRow(organization.FinancialAccounts.CostsBankFees, -row.Fee, _currentUser); transaction.AddRow(autoDepositAccount, -row.AmountCentsGross, _currentUser); } else if (amountCents < autoDepositLimit * 100) { // Book against autoDeposit account. transaction.AddRow(autoDepositAccount, -amountCents, _currentUser); } } } } // Import complete. Examine if we expect more transactions -- if the imported balance differs from // the database balance: double databaseAccountBalance = assetAccount.BalanceTotal; bool mismatch = false; if (databaseAccountBalance != import.CurrentBalance) { mismatch = true; } string message = importedTransactionCount.ToString() + " transactions were imported."; if (importedTransactionCount == 0) { message = "No transactions were imported. "; } else if (importedTransactionCount == 1) { message = "One transaction was imported. "; } if (import.CurrentBalance > 0) { if (mismatch) { message += " Transactions are missing from the database. Import more transactions."; } else { message += " The account balance is up to date. No further import is necessary."; ScriptManager.RegisterStartupScript(this, Page.GetType(), "alldone", "alert ('The account balance is up to date. No further import is necessary.');", true); // Auto-match open payouts against new transactions Payouts.AutomatchAgainstUnbalancedTransactions(organization); } } this.LabelImportResultText.Text = message; }
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); }