public ReconciliationResult MonthEndReconciliation(
            LedgerBook ledgerBook,
            DateTime reconciliationDate,
            IBudgetCurrencyContext budgetContext,
            StatementModel statement,
            bool ignoreWarnings,
            params BankBalance[] currentBankBalances)
        {
            if (ledgerBook == null)
            {
                throw new ArgumentNullException(nameof(ledgerBook));
            }

            if (currentBankBalances == null)
            {
                throw new ArgumentNullException(nameof(currentBankBalances));
            }

            if (budgetContext == null)
            {
                throw new ArgumentNullException(nameof(budgetContext));
            }

            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement));
            }

            if (!budgetContext.BudgetActive)
            {
                throw new ArgumentException("Reconciling against an inactive budget is invalid.");
            }

            var stopWatch = Stopwatch.StartNew();

            this.logger.LogInfo(l => l.Format("Starting Ledger Book reconciliation {0}", DateTime.Now));

            if (!ignoreWarnings)
            {
                this.validationMessages = new Collection <string>();
            }
            try
            {
                PreReconciliationValidation(ledgerBook, reconciliationDate, statement);
            }
            catch (ValidationWarningException ex)
            {
                if (ShouldValidationExceptionBeRethrown(ignoreWarnings, ex))
                {
                    throw;
                }
            }

            ReconciliationResult recon;

            using (this.reconciliationConsistency.EnsureConsistency(ledgerBook))
            {
                recon = ledgerBook.Reconcile(reconciliationDate, budgetContext.Model, statement, currentBankBalances);
            }

            // Create new single use matching rules - if needed to ensure transfers are assigned a bucket easily without user intervention.
            foreach (var task in recon.Tasks)
            {
                this.logger.LogInfo(
                    l => l.Format("TASK: {0} SystemGenerated:{1}", task.Description, task.SystemGenerated));
                var transferTask = task as TransferTask;
                if (transferTask != null && transferTask.SystemGenerated && transferTask.Reference.IsSomething())
                {
                    this.logger.LogInfo(
                        l =>
                        l.Format(
                            "TRANSFER-TASK detected- creating new single use rule. SystemGenerated:{1} Reference:{2}",
                            task.Description, task.SystemGenerated, transferTask.Reference));
                    this.transactionRuleService.CreateNewSingleUseRule(transferTask.BucketCode, null,
                                                                       new[] { transferTask.Reference }, null, null, true);
                }
            }

            stopWatch.Stop();
            this.logger.LogInfo(l => l.Format("Finished Ledger Book reconciliation {0}. It took {1:F0}ms", DateTime.Now, stopWatch.ElapsedMilliseconds));
            this.validationMessages = null;
            return(recon);
        }
 private ReconciliationResult Act(LedgerBook book, BudgetModel budget)
 {
     return(book.Reconcile(NextReconcileDate, budget, this.testDataStatement, NextReconcileBankBalance));
 }