Beispiel #1
0
        /// <summary>
        /// init the exchange rate, to avoid messages "Cannot find exchange rate for EUR USD"
        /// </summary>
        public static void InitExchangeRate()
        {
            TAccountPeriodInfo AccountingPeriodInfo =
                new TAccountPeriodInfo(FLedgerNumber, 1);
            ADailyExchangeRateTable dailyrates = new ADailyExchangeRateTable();
            ADailyExchangeRateRow   row        = dailyrates.NewRowTyped(true);

            row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
            row.TimeEffectiveFrom = 100;
            row.FromCurrencyCode  = "USD";
            row.ToCurrencyCode    = "EUR";
            row.RateOfExchange    = 1.34m;
            dailyrates.Rows.Add(row);
            row = dailyrates.NewRowTyped(true);
            row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
            row.TimeEffectiveFrom = 100;
            row.FromCurrencyCode  = "USD";
            row.ToCurrencyCode    = "GBP";
            row.RateOfExchange    = 1.57m;
            dailyrates.Rows.Add(row);

            if (!ADailyExchangeRateAccess.Exists(row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom, row.TimeEffectiveFrom, null))
            {
                ADailyExchangeRateAccess.SubmitChanges(dailyrates, null);
            }
        }
Beispiel #2
0
        /// <summary>
        /// init the exchange rate, to avoid messages "Cannot find exchange rate for EUR USD"
        /// </summary>
        public static void InitExchangeRate()
        {
            TAccountPeriodInfo AccountingPeriodInfo =
                new TAccountPeriodInfo(FLedgerNumber, 1);
            ADailyExchangeRateTable dailyrates = new ADailyExchangeRateTable();
            ADailyExchangeRateRow   row        = dailyrates.NewRowTyped(true);

            row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
            row.TimeEffectiveFrom = 100;
            row.FromCurrencyCode  = "USD";
            row.ToCurrencyCode    = "EUR";
            row.RateOfExchange    = 1.34m;
            dailyrates.Rows.Add(row);
            row = dailyrates.NewRowTyped(true);
            row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
            row.TimeEffectiveFrom = 100;
            row.FromCurrencyCode  = "USD";
            row.ToCurrencyCode    = "GBP";
            row.RateOfExchange    = 1.57m;
            dailyrates.Rows.Add(row);

            if (!ADailyExchangeRateAccess.Exists(row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom, row.TimeEffectiveFrom, null))
            {
                ADailyExchangeRateAccess.SubmitChanges(dailyrates, null);
            }

            ALedgerTable Ledger = ALedgerAccess.LoadByPrimaryKey(FLedgerNumber, null);

            for (int periodCounter = 1; periodCounter <= Ledger[0].NumberOfAccountingPeriods + Ledger[0].NumberFwdPostingPeriods; periodCounter++)
            {
                AccountingPeriodInfo = new TAccountPeriodInfo(FLedgerNumber, periodCounter);

                ACorporateExchangeRateTable corprates = new ACorporateExchangeRateTable();
                ACorporateExchangeRateRow   corprow   = corprates.NewRowTyped(true);
                corprow.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
                corprow.TimeEffectiveFrom = 100;
                corprow.FromCurrencyCode  = "USD";
                corprow.ToCurrencyCode    = "EUR";
                corprow.RateOfExchange    = 1.34m;
                corprates.Rows.Add(corprow);
                corprow = corprates.NewRowTyped(true);
                corprow.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
                corprow.TimeEffectiveFrom = 100;
                corprow.FromCurrencyCode  = "USD";
                corprow.ToCurrencyCode    = "GBP";
                corprow.RateOfExchange    = 1.57m;
                corprates.Rows.Add(corprow);

                if (!ACorporateExchangeRateAccess.Exists(corprow.FromCurrencyCode, corprow.ToCurrencyCode, corprow.DateEffectiveFrom, null))
                {
                    ACorporateExchangeRateAccess.SubmitChanges(corprates, null);
                }
            }
        }
Beispiel #3
0
            public static void AddARow(String FromCurrency, String ToCurrency, DateTime EffectiveDate, decimal Rate)
            {
                ADailyExchangeRateRow newRow = FMainDS.ADailyExchangeRate.NewRowTyped();

                newRow.FromCurrencyCode  = FromCurrency;
                newRow.ToCurrencyCode    = ToCurrency;
                newRow.DateEffectiveFrom = EffectiveDate;
                newRow.TimeEffectiveFrom = 7200;
                newRow.RateOfExchange    = Rate;
                FMainDS.ADailyExchangeRate.Rows.Add(newRow);
            }
Beispiel #4
0
        public void LoadModalEmptyTable()
        {
            // Initialise data - create an empty table and our test ledger
            FMainDS.LoadAll();
            FMainDS.DeleteAllRows();
            FMainDS.SaveChanges();
            FLedgerDS.CreateTestLedger();

            // variables to hold the dialog result output
            decimal  selectedRate;
            DateTime selectedDate;
            int      selectedTime;

            // Open the screen modally on our test ledger using a 'from' currency of GBP
            TFrmSetupDailyExchangeRate mainScreen = new TFrmSetupDailyExchangeRate(null);

            DialogBoxHandler = delegate(string name, IntPtr hWnd)
            {
                LoadModalEmptyTableHandler();
            };

            DialogResult dlgResult = mainScreen.ShowDialog(STANDARD_TEST_LEDGER_NUMBER,
                                                           FStandardEffectiveDate,
                                                           "GBP",
                                                           1.0m,
                                                           out selectedRate,
                                                           out selectedDate,
                                                           out selectedTime);

            // Check the result for any assertions
            if (dlgResult == DialogResult.Abort)
            {
                Assert.Fail(FModalAssertResult);
            }

            // Check we returned the correct data to the caller
            Assert.AreEqual(DialogResult.OK, dlgResult);
            Assert.AreEqual(STANDARD_RATE_OF_EXCHANGE, selectedRate);
            Assert.AreEqual(FStandardEffectiveDate, selectedDate);
            Assert.AreEqual(7200, selectedTime);

            // Check we did also save the result
            FMainDS.LoadAll();
            ADailyExchangeRateRow row =
                (ADailyExchangeRateRow)FMainDS.ADailyExchangeRate.Rows.Find(new object[] { "GBP", STANDARD_TEST_CURRENCY, FStandardEffectiveDate,
                                                                                           7200 });

            Assert.IsNotNull(row, "The selected exchange rate was not saved");
            Assert.AreEqual(STANDARD_RATE_OF_EXCHANGE, row.RateOfExchange);
        }
        private void ValidateDataDetailsManual(ADailyExchangeRateRow ARow)
        {
            if ((ARow.RowState == DataRowState.Detached) || FSkipValidation)
            {
                return;
            }

            TVerificationResultCollection VerificationResultCollection = FPetraUtilsObject.VerificationResultCollection;

            TSharedFinanceValidation_GLSetup.ValidateDailyExchangeRate(this, ARow, ref VerificationResultCollection,
                FPetraUtilsObject.ValidationControlsDict, minModalEffectiveDate, maxModalEffectiveDate, blnIsInModalMode, FMainDS.ALedgerInfo,
                FEarliestAccountingPeriodStartDate, FLatestAccountingPeriodEndDate);

            // Now make an additional manual check that the rate is sensible
            TScreenVerificationResult verificationResultSensible = null;
            TScreenVerificationResult verificationResultDuplicate = null;

            if ((ARow.RowState == DataRowState.Added) || (ARow.RowState == DataRowState.Modified))
            {
                // We are going to check if the rate of exchange is sensible.  We need our own view because we don't know how the grid is currently sorted
                // If we are in modal mode we may not be seeing the 'neighbouring' rate.  But this filter will include it in the test.
                string filter =
                    ADailyExchangeRateTable.GetFromCurrencyCodeDBName() + " = '" + ARow.FromCurrencyCode + "' and " +
                    ADailyExchangeRateTable.GetToCurrencyCodeDBName() + " = '" + ARow.ToCurrencyCode + "'";
                DataView myView = new DataView(FMainDS.ADailyExchangeRate, filter, SortByDateDescending, DataViewRowState.CurrentRows);

                // Find our current row
                int nThis = FindRowInDataView(myView, ARow.FromCurrencyCode, ARow.ToCurrencyCode, ARow.DateEffectiveFrom, ARow.TimeEffectiveFrom);
                ADailyExchangeRateRow drThis = null;
                ADailyExchangeRateRow drPrev = null;
                ADailyExchangeRateRow drNext = null;
                decimal ratio = 1.0m;

                if ((nThis >= 0) && (ARow.RateOfExchange != 0.0m))
                {
                    drThis = (ADailyExchangeRateRow)(myView[nThis]).Row;

                    if (nThis >= 1)
                    {
                        drPrev = (ADailyExchangeRateRow)(myView[nThis - 1]).Row;
                    }

                    if (nThis < myView.Count - 1)
                    {
                        drNext = (ADailyExchangeRateRow)(myView[nThis + 1]).Row;
                    }

                    if ((drPrev != null) && (drPrev.RateOfExchange > 0.0m))
                    {
                        ratio = drThis.RateOfExchange / drPrev.RateOfExchange;

                        if (ratio < 1.0m)
                        {
                            ratio = drPrev.RateOfExchange / drThis.RateOfExchange;
                        }
                    }

                    if ((drNext != null) && (drNext.RateOfExchange > 0.0m))
                    {
                        decimal tryRatio = drThis.RateOfExchange / drNext.RateOfExchange;

                        if (tryRatio < 1.0m)
                        {
                            tryRatio = drNext.RateOfExchange / drThis.RateOfExchange;
                        }

                        if (tryRatio > ratio)
                        {
                            ratio = tryRatio;
                        }
                    }

                    if (ratio > EXCHANGE_RATE_WARNING_RATIO)
                    {
                        string validationMessage = String.Format(
                            ErrorCodes.GetErrorInfo(PetraErrorCodes.ERR_EXCH_RATE_MAY_BE_INCORRECT).ErrorMessageText,
                            ARow.RateOfExchange,
                            ARow.FromCurrencyCode,
                            ARow.ToCurrencyCode,
                            dtpDetailDateEffectiveFrom.Text,
                            txtDetailTimeEffectiveFrom.Text,
                            ratio - 1.0m);

                        // So we have a new warning to raise on a row that has been added/edited
                        verificationResultSensible = new TScreenVerificationResult(
                            this,
                            ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId],
                            validationMessage,
                            Catalog.GetString("Exchange Rate Alert"),
                            PetraErrorCodes.ERR_EXCH_RATE_MAY_BE_INCORRECT,
                            txtDetailRateOfExchange,
                            TResultSeverity.Resv_Noncritical);
                    }
                }

                // Now check to see if it is a duplicate rate
                filter += String.Format(" and {0}={1} and {2}=#{3}#",
                    ADailyExchangeRateTable.GetRateOfExchangeDBName(),
                    ARow.RateOfExchange.ToString(CultureInfo.InvariantCulture),
                    ADailyExchangeRateTable.GetDateEffectiveFromDBName(),
                    ARow.DateEffectiveFrom.ToString("d", CultureInfo.InvariantCulture));
                myView = new DataView(FMainDS.ADailyExchangeRate, filter, String.Empty, DataViewRowState.CurrentRows);

                if (myView.Count > 1)
                {
                    verificationResultDuplicate = new TScreenVerificationResult(
                        this,
                        ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId],
                        Catalog.GetString("A row with this date and exchange rate already exists"),
                        Catalog.GetString("Exchange Rate Alert"),
                        PetraErrorCodes.ERR_EXCH_RATE_IS_DUPLICATE,
                        txtDetailRateOfExchange,
                        TResultSeverity.Resv_Noncritical);
                }
            }

            VerificationResultCollection.Auto_Add_Or_AddOrRemove(this, verificationResultSensible,
                ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId]);
            VerificationResultCollection.Auto_Add_Or_AddOrRemove(this, verificationResultDuplicate,
                ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId]);
        }
Beispiel #6
0
        /// <summary>
        /// Validates the Daily Exchange Rates screen data.
        /// </summary>
        /// <param name="AContext">Context that describes where the data validation failed.</param>
        /// <param name="ARow">The <see cref="DataRow" /> which holds the the data against which the validation is run.</param>
        /// <param name="AVerificationResultCollection">Will be filled with any <see cref="TVerificationResult" /> items if
        /// data validation errors occur.</param>
        /// <param name="AValidationControlsDict">A <see cref="TValidationControlsDict" /> containing the Controls that
        /// display data that is about to be validated.</param>
        /// <param name="AMinDateTime">The earliest allowable date.</param>
        /// <param name="AMaxDateTime">The latest allowable date.</param>
        public static void ValidateDailyExchangeRate(object AContext,
                                                     ADailyExchangeRateRow ARow,
                                                     ref TVerificationResultCollection AVerificationResultCollection,
                                                     TValidationControlsDict AValidationControlsDict,
                                                     DateTime AMinDateTime,
                                                     DateTime AMaxDateTime)
        {
            DataColumn ValidationColumn;
            TValidationControlsData ValidationControlsData;
            TVerificationResult     VerificationResult;

            // Don't validate deleted DataRows
            if (ARow.RowState == DataRowState.Deleted)
            {
                return;
            }

            // RateOfExchange must be positive (definitely not zero because we can invert it)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TNumericalChecks.IsPositiveDecimal(ARow.RateOfExchange,
                                                                        ValidationControlsData.ValidationControlLabel,
                                                                        AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition/removal to/from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }

            // Date must not be empty and must be in range
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnDateEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TSharedValidationControlHelper.IsNotInvalidDate(ARow.DateEffectiveFrom,
                                                                                     ValidationControlsData.ValidationControlLabel, AVerificationResultCollection, true,
                                                                                     AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);

                if (VerificationResult == null)
                {
                    if ((AMinDateTime > DateTime.MinValue) && (AMaxDateTime < DateTime.MaxValue))
                    {
                        // Check that the date is in range
                        VerificationResult = TDateChecks.IsDateBetweenDates(ARow.DateEffectiveFrom,
                                                                            AMinDateTime,
                                                                            AMaxDateTime,
                                                                            ValidationControlsData.ValidationControlLabel,
                                                                            TDateBetweenDatesCheckType.dbdctUnspecific,
                                                                            TDateBetweenDatesCheckType.dbdctUnspecific,
                                                                            AContext,
                                                                            ValidationColumn,
                                                                            ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                    else if (AMaxDateTime < DateTime.MaxValue)
                    {
                        VerificationResult = TDateChecks.FirstLesserOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMaxDateTime,
                                                                                          ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMaxDateTime),
                                                                                          AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                    else if (AMinDateTime > DateTime.MinValue)
                    {
                        VerificationResult = TDateChecks.FirstGreaterOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMinDateTime,
                                                                                           ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMinDateTime),
                                                                                           AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                }
            }

            // Time must not be negative (indicating an error)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnTimeEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TTimeChecks.IsValidIntegerTime(ARow.TimeEffectiveFrom,
                                                                    ValidationControlsData.ValidationControlLabel,
                                                                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }
        }
        /// <summary>
        /// get daily exchange rate for the given currencies and date;
        /// TODO: might even collect the latest exchange rate from the web
        /// </summary>
        /// <param name="ACurrencyFrom"></param>
        /// <param name="ACurrencyTo"></param>
        /// <param name="ADateEffective"></param>
        /// <returns>Zero if no exchange rate found</returns>
        public static decimal GetDailyExchangeRate(string ACurrencyFrom, string ACurrencyTo, DateTime ADateEffective)
        {
            if (ACurrencyFrom == ACurrencyTo)
            {
                return(1.0M);
            }

            bool           NewTransaction;
            TDBTransaction Transaction = DBAccess.GDBAccessObj.GetNewOrExistingTransaction
                                             (IsolationLevel.ReadCommitted, TEnforceIsolationLevel.eilMinimum, out NewTransaction);

            ADailyExchangeRateRow fittingRate = null;

            // TODO: get the most recent exchange rate for the given date and currencies
            bool oppositeRate             = false;
            ADailyExchangeRateTable rates = ADailyExchangeRateAccess.LoadByPrimaryKey(ACurrencyFrom, ACurrencyTo, ADateEffective, 0, Transaction);

            if (rates.Count == 0)
            {
                // try other way round
                rates        = ADailyExchangeRateAccess.LoadByPrimaryKey(ACurrencyTo, ACurrencyFrom, ADateEffective, 0, Transaction);
                oppositeRate = true;
            }

            if (rates.Count == 1)
            {
                fittingRate = rates[0];
            }
            else if (rates.Count == 0)
            {
                // TODO: collect exchange rate from the web; save to db
                // see tracker http://sourceforge.net/apps/mantisbt/openpetraorg/view.php?id=87

                // Or look for most recent exchange rate???
                ADailyExchangeRateTable tempTable   = new ADailyExchangeRateTable();
                ADailyExchangeRateRow   templateRow = tempTable.NewRowTyped(false);
                templateRow.FromCurrencyCode = ACurrencyFrom;
                templateRow.ToCurrencyCode   = ACurrencyTo;
                oppositeRate = false;
                rates        = ADailyExchangeRateAccess.LoadUsingTemplate(templateRow, Transaction);

                if (rates.Count == 0)
                {
                    templateRow.FromCurrencyCode = ACurrencyTo;
                    templateRow.ToCurrencyCode   = ACurrencyFrom;
                    oppositeRate = true;
                    rates        = ADailyExchangeRateAccess.LoadUsingTemplate(templateRow, Transaction);
                }

                if (rates.Count > 0)
                {
                    // sort rates by date, look for rate just before the date we are looking for
                    rates.DefaultView.Sort      = ADailyExchangeRateTable.GetDateEffectiveFromDBName();
                    rates.DefaultView.RowFilter = ADailyExchangeRateTable.GetDateEffectiveFromDBName() + "<= #" +
                                                  ADateEffective.ToString("yyyy-MM-dd") + "#";

                    if (rates.DefaultView.Count > 0)
                    {
                        fittingRate = (ADailyExchangeRateRow)rates.DefaultView[rates.DefaultView.Count - 1].Row;
                    }
                }
            }

            if (NewTransaction)
            {
                DBAccess.GDBAccessObj.RollbackTransaction();
            }

            if (fittingRate != null)
            {
                if (oppositeRate)
                {
                    return(1.0M / fittingRate.RateOfExchange);
                }

                return(fittingRate.RateOfExchange);
            }

            TLogging.Log("Cannot find daily exchange rate for " + ACurrencyFrom + " " + ACurrencyTo);

            //return 1.0M;
            //Instead, cause a validation error to force the user to select an exchange rate
            return(0M);
        }
Beispiel #8
0
        /// <summary>
        /// Gets the best (latest) exchange rate for a pair of currencies from a table of rates.
        /// </summary>
        /// <param name="ADailyRateTable">A table of rates that has been obtained by calling LoadExchangeRateData for a period of interest.</param>
        /// <param name="ACurrencyFrom">The 'from' currency</param>
        /// <param name="ACurrencyTo">The 'to' currency</param>
        /// <param name="AEnforceUniqueRate">If true the method will only return true if there is a unique rate for the specified currencies on the 'latest' date.
        /// If false the method returns the rate for the latest 'time' on the latest date.</param>
        /// <param name="ARateOfExchange">The returned rate</param>
        /// <param name="AEffectiveDate">The date associated with the returned rate.</param>
        /// <returns>True if a matching rate was found.  The rate and date are in the out parameters.</returns>
        public static bool GetBestExchangeRate(ExchangeRateTDSADailyExchangeRateTable ADailyRateTable,
                                               string ACurrencyFrom,
                                               string ACurrencyTo,
                                               Boolean AEnforceUniqueRate,
                                               out decimal ARateOfExchange,
                                               out DateTime AEffectiveDate)
        {
            ARateOfExchange = 0.0m;
            AEffectiveDate  = DateTime.MinValue;

            ADailyExchangeRateRow uniqueFittingRow = null;
            bool oppositeRate = false;

            // sort rates by date, look for rate just before the date we are looking for
            string rowFilter = String.Format("{0}='{1}' AND {2}='{3}'",
                                             ADailyExchangeRateTable.GetFromCurrencyCodeDBName(),
                                             ACurrencyFrom,
                                             ADailyExchangeRateTable.GetToCurrencyCodeDBName(),
                                             ACurrencyTo);

            ADailyRateTable.DefaultView.RowFilter = rowFilter;
            ADailyRateTable.DefaultView.Sort      = String.Format("{0} DESC, {1} DESC",
                                                                  ADailyExchangeRateTable.GetDateEffectiveFromDBName(),
                                                                  ADailyExchangeRateTable.GetTimeEffectiveFromDBName());

            // If we are after a unique row we need to remember the one from this view, if it exists
            if (AEnforceUniqueRate && (ADailyRateTable.DefaultView.Count == 1))
            {
                uniqueFittingRow = (ADailyExchangeRateRow)ADailyRateTable.DefaultView[0].Row;
            }

            if ((ADailyRateTable.DefaultView.Count == 0) || AEnforceUniqueRate)
            {
                // try other way round
                rowFilter = String.Format("{0}='{1}' AND {2}='{3}'",
                                          ADailyExchangeRateTable.GetToCurrencyCodeDBName(),
                                          ACurrencyFrom,
                                          ADailyExchangeRateTable.GetFromCurrencyCodeDBName(),
                                          ACurrencyTo);

                ADailyRateTable.DefaultView.RowFilter = rowFilter;
                oppositeRate = true;

                if (AEnforceUniqueRate)
                {
                    if (ADailyRateTable.DefaultView.Count > 1)
                    {
                        // This way round does not have a unique rate
                        uniqueFittingRow = null;
                    }
                    else if ((ADailyRateTable.DefaultView.Count == 1) && (uniqueFittingRow == null))
                    {
                        // we will use this as our unique rate
                        uniqueFittingRow = (ADailyExchangeRateRow)ADailyRateTable.DefaultView[0].Row;
                    }
                    else
                    {
                        // put this variable back as it was
                        oppositeRate = false;
                    }
                }
            }

            ADailyExchangeRateRow fittingRate = null;

            if (AEnforceUniqueRate)
            {
                // Did we get a unique rate?
                if (uniqueFittingRow != null)
                {
                    fittingRate = uniqueFittingRow;
                }
            }
            else if (ADailyRateTable.DefaultView.Count > 0)
            {
                // Just return the first in the list
                fittingRate = (ADailyExchangeRateRow)ADailyRateTable.DefaultView[0].Row;
            }

            if (fittingRate != null)
            {
                if (oppositeRate)
                {
                    ARateOfExchange = GLRoutines.Divide(1.0m, fittingRate.RateOfExchange);
                }
                else
                {
                    ARateOfExchange = fittingRate.RateOfExchange;
                }

                AEffectiveDate = fittingRate.DateEffectiveFrom;
            }

            //Returning 0 causes a validation error to force the user to select an exchange rate:
            return(ARateOfExchange != 0.0m);
        }
        /// <summary>
        /// Validates the Daily Exchange Rates screen data.
        /// </summary>
        /// <param name="AContext">Context that describes where the data validation failed.</param>
        /// <param name="ARow">The <see cref="DataRow" /> which holds the the data against which the validation is run.</param>
        /// <param name="AVerificationResultCollection">Will be filled with any <see cref="TVerificationResult" /> items if
        /// data validation errors occur.</param>
        /// <param name="AValidationControlsDict">A <see cref="TValidationControlsDict" /> containing the Controls that
        /// display data that is about to be validated.</param>
        /// <param name="AMinDateTime">The earliest allowable date.</param>
        /// <param name="AMaxDateTime">The latest allowable date.</param>
        public static void ValidateDailyExchangeRate(object AContext,
            ADailyExchangeRateRow ARow,
            ref TVerificationResultCollection AVerificationResultCollection,
            TValidationControlsDict AValidationControlsDict,
            DateTime AMinDateTime,
            DateTime AMaxDateTime)
        {
            DataColumn ValidationColumn;
            TValidationControlsData ValidationControlsData;
            TVerificationResult VerificationResult;

            // Don't validate deleted DataRows
            if (ARow.RowState == DataRowState.Deleted)
            {
                return;
            }

            // RateOfExchange must be positive (definitely not zero because we can invert it)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TNumericalChecks.IsPositiveDecimal(ARow.RateOfExchange,
                    ValidationControlsData.ValidationControlLabel,
                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition/removal to/from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }

            // Date must not be empty and must be in range
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnDateEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TSharedValidationControlHelper.IsNotInvalidDate(ARow.DateEffectiveFrom,
                    ValidationControlsData.ValidationControlLabel, AVerificationResultCollection, true,
                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);

                if (VerificationResult == null)
                {
                    if ((AMinDateTime > DateTime.MinValue) && (AMaxDateTime < DateTime.MaxValue))
                    {
                        // Check that the date is in range
                        VerificationResult = TDateChecks.IsDateBetweenDates(ARow.DateEffectiveFrom,
                            AMinDateTime,
                            AMaxDateTime,
                            ValidationControlsData.ValidationControlLabel,
                            TDateBetweenDatesCheckType.dbdctUnspecific,
                            TDateBetweenDatesCheckType.dbdctUnspecific,
                            AContext,
                            ValidationColumn,
                            ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                    else if (AMaxDateTime < DateTime.MaxValue)
                    {
                        VerificationResult = TDateChecks.FirstLesserOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMaxDateTime,
                            ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMaxDateTime),
                            AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                    else if (AMinDateTime > DateTime.MinValue)
                    {
                        VerificationResult = TDateChecks.FirstGreaterOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMinDateTime,
                            ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMinDateTime),
                            AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                    }
                }
            }

            // Time must not be negative (indicating an error)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnTimeEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TTimeChecks.IsValidIntegerTime(ARow.TimeEffectiveFrom,
                    ValidationControlsData.ValidationControlLabel,
                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Imports currency exchange rates, daily and corporate,
        /// from a one-of-two-styles formatted CSV file
        /// </summary>
        /// <param name="AExchangeRDT">Daily or Corporate exchange rate table</param>
        /// <param name="ADataFilename">The .CSV file to process</param>
        /// <param name="ACSVSeparator"></param>
        /// <param name="ANumberFormat"></param>
        /// <param name="ADateFormat"></param>
        /// <param name="AImportMode">Daily or Corporate</param>
        /// <param name="AResultCollection">A validation collection to which errors will be added</param>
        /// <returns>The number of rows that were actually imported.  Rows that duplicate existing rows do not count.
        /// This is usually because this is an attempt to import again after a failed previous attempt.</returns>
        private static int ImportCurrencyExRatesFromCSV(TTypedDataTable AExchangeRDT,
                                                        string ADataFilename,
                                                        string ACSVSeparator,
                                                        string ANumberFormat,
                                                        string ADateFormat,
                                                        string AImportMode,
                                                        TVerificationResultCollection AResultCollection)
        {
            // Keep a list of errors/warnings and severity
            List <Tuple <string, TResultSeverity> > InvalidRows = new List <Tuple <string, TResultSeverity> >();

            // keep a variable that becomes true if any row has an invalid column count, so we can show a helpful message
            bool InvalidColumnCount = false;

            // Check our method parameters
            if ((AImportMode != "Corporate") && (AImportMode != "Daily"))
            {
                throw new ArgumentException("Invalid value '" + AImportMode + "' for mode argument: Valid values are Corporate and Daily");
            }
            else if ((AImportMode == "Corporate") && !(AExchangeRDT is ACorporateExchangeRateTable))
            {
                throw new ArgumentException("Invalid type of exchangeRateDT argument for mode: 'Corporate'. Needs to be: ACorporateExchangeRateTable");
            }
            else if ((AImportMode == "Daily") && !(AExchangeRDT is ADailyExchangeRateTable))
            {
                throw new ArgumentException("Invalid type of exchangeRateDT argument for mode: 'Daily'. Needs to be: ADailyExchangeRateTable");
            }

            bool IsShortFileFormat;
            int  x, y;

            // To store the From and To currencies
            // Use an array to store these to make for easy
            //   inverting of the two currencies when calculating
            //   the inverse value.
            string[] Currencies = new string[2];

            Type DataTableType;
            int  RowsImported = 0;

            // This table will tell us the base currencies used by the current set of ledgers
            ALedgerTable ledgers = TRemote.MFinance.Setup.WebConnectors.GetAvailableLedgers();

            List <string> allBaseCurrencies           = new List <string>();
            DateTime      maxRecommendedEffectiveDate = DateTime.MaxValue;
            DateTime      minRecommendedEffectiveDate = DateTime.MinValue;
            int           preferredPeriodStartDay     = 0;

            // Use the ledger table rows to get a list of base currencies and current period end dates
            for (int i = 0; i < ledgers.Rows.Count; i++)
            {
                ALedgerRow ledger = (ALedgerRow)ledgers.Rows[i];
                string     code   = ledger.BaseCurrency;

                if (ledger.LedgerStatus == true)
                {
                    if (allBaseCurrencies.Contains(code) == false)
                    {
                        allBaseCurrencies.Add(code);
                    }

                    DataTable AccountingPeriods = TDataCache.TMFinance.GetCacheableFinanceTable(TCacheableFinanceTablesEnum.AccountingPeriodList,
                                                                                                ledger.LedgerNumber);
                    AAccountingPeriodRow currentPeriodRow = (AAccountingPeriodRow)AccountingPeriods.Rows.Find(new object[] { ledger.LedgerNumber,
                                                                                                                             ledger.CurrentPeriod });
                    AAccountingPeriodRow forwardPeriodRow = (AAccountingPeriodRow)AccountingPeriods.Rows.Find(
                        new object[] { ledger.LedgerNumber,
                                       ledger.CurrentPeriod +
                                       ledger.NumberFwdPostingPeriods });

                    if ((forwardPeriodRow != null) &&
                        ((maxRecommendedEffectiveDate == DateTime.MaxValue) || (forwardPeriodRow.PeriodEndDate > maxRecommendedEffectiveDate)))
                    {
                        maxRecommendedEffectiveDate = forwardPeriodRow.PeriodEndDate;
                    }

                    if ((currentPeriodRow != null) &&
                        ((minRecommendedEffectiveDate == DateTime.MinValue) || (currentPeriodRow.PeriodStartDate > minRecommendedEffectiveDate)))
                    {
                        minRecommendedEffectiveDate = currentPeriodRow.PeriodStartDate;
                    }

                    if ((currentPeriodRow != null) && (preferredPeriodStartDay == 0))
                    {
                        preferredPeriodStartDay = currentPeriodRow.PeriodStartDate.Day;
                    }
                    else if ((currentPeriodRow != null) && (currentPeriodRow.PeriodStartDate.Day != preferredPeriodStartDay))
                    {
                        preferredPeriodStartDay = -1;
                    }
                }
            }

            // This will tell us the complete list of all available currencies
            ACurrencyTable allCurrencies = new ACurrencyTable();
            DataTable      CacheDT       = TDataCache.GetCacheableDataTableFromCache("CurrencyCodeList", String.Empty, null, out DataTableType);

            allCurrencies.Merge(CacheDT);
            allCurrencies.CaseSensitive = true;

            // Start reading the file
            using (StreamReader DataFile = new StreamReader(ADataFilename, System.Text.Encoding.Default))
            {
                string ThousandsSeparator = (ANumberFormat == TDlgSelectCSVSeparator.NUMBERFORMAT_AMERICAN ? "," : ".");
                string DecimalSeparator   = (ANumberFormat == TDlgSelectCSVSeparator.NUMBERFORMAT_AMERICAN ? "." : ",");

                CultureInfo MyCultureInfoDate = new CultureInfo("en-GB");
                MyCultureInfoDate.DateTimeFormat.ShortDatePattern = ADateFormat;

                // TODO: disconnect the grid from the datasource to avoid flickering?

                string FileNameWithoutExtension = Path.GetFileNameWithoutExtension(ADataFilename).ToUpper();

                if ((FileNameWithoutExtension.IndexOf("_") == 3) &&
                    (FileNameWithoutExtension.LastIndexOf("_") == 3) &&
                    (FileNameWithoutExtension.Length == 7))
                {
                    // File name format assumed to be like this: USD_HKD.csv
                    IsShortFileFormat = true;
                    Currencies        = FileNameWithoutExtension.Split(new char[] { '_' });
                }
                else
                {
                    IsShortFileFormat = false;
                }

                int LineNumber = 0;

                while (!DataFile.EndOfStream)
                {
                    string Line = DataFile.ReadLine();
                    LineNumber++;

                    // See if the first line is a special case??
                    if (LineNumber == 1)
                    {
                        // see if the first line is a text header - look for digits
                        // A valid header would look like:  From,To,Date,Rate
                        bool bFoundDigit = false;

                        for (int i = 0; i < Line.Length; i++)
                        {
                            char c = Line[i];

                            if ((c >= '0') && (c <= '9'))
                            {
                                bFoundDigit = true;
                                break;
                            }
                        }

                        if (!bFoundDigit)
                        {
                            // No digits so we will assume the line is a header
                            continue;
                        }
                    }

                    //Convert separator to a char
                    char Sep = ACSVSeparator[0];
                    //Turn current line into string array of column values
                    string[] CsvColumns = Line.Split(Sep);

                    int NumCols = CsvColumns.Length;

                    // Do we have the correct number of columns?
                    int minColCount = IsShortFileFormat ? 2 : 4;
                    int maxColCount = (AImportMode == "Daily") ? minColCount + 1 : minColCount;

                    if ((NumCols < minColCount) || (NumCols > maxColCount))
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetPluralString(
                                                              "Line {0}: contains 1 column", "Line {0}: contains {1} columns", NumCols, true),
                                                          LineNumber, NumCols.ToString());

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        InvalidColumnCount = true;
                        continue;
                    }

                    if (!IsShortFileFormat)
                    {
                        //Read the values for the current line
                        //From currency
                        Currencies[0] = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).ToString().ToUpper();
                        //To currency
                        Currencies[1] = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).ToString().ToUpper();
                    }

                    // Perform validation on the From and To currencies at this point!!
                    if ((allCurrencies.Rows.Find(Currencies[0]) == null) && (allCurrencies.Rows.Find(Currencies[1]) == null))
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: invalid currency codes ({1} and {2})"), LineNumber.ToString(), Currencies[0], Currencies[1]);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        continue;
                    }
                    else if (allCurrencies.Rows.Find(Currencies[0]) == null)
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: invalid currency code ({1})"), LineNumber.ToString(), Currencies[0]);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        continue;
                    }
                    else if (allCurrencies.Rows.Find(Currencies[1]) == null)
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: invalid currency code ({1})"), LineNumber.ToString(), Currencies[1]);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        continue;
                    }

                    if ((allBaseCurrencies.Contains(Currencies[0]) == false) && (allBaseCurrencies.Contains(Currencies[1]) == false))
                    {
                        //raise a non-critical error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: Warning:  One of '{1}' and '{2}' should be a base currency in one of the active ledgers."),
                                                          LineNumber.ToString(), Currencies[0], Currencies[1]);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Noncritical));
                    }

                    // Date parsing as in Petra 2.x instead of using XML date format!!!
                    string   DateEffectiveStr = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).Replace("\"", String.Empty);
                    DateTime DateEffective;

                    if (!DateTime.TryParse(DateEffectiveStr, MyCultureInfoDate, DateTimeStyles.None, out DateEffective))
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: invalid date ({1})"), LineNumber.ToString(), DateEffectiveStr);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        continue;
                    }

                    if (DateEffective > maxRecommendedEffectiveDate)
                    {
                        // raise a warning
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: Warning: The date '{1}' is after the latest forwarding period of any active ledger"),
                                                          LineNumber.ToString(), DateEffectiveStr);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Noncritical));
                    }
                    else if (DateEffective < minRecommendedEffectiveDate)
                    {
                        // raise a warning
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: Warning: The date '{1}' is before the current accounting period of any active ledger"),
                                                          LineNumber.ToString(), DateEffectiveStr);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Noncritical));
                    }

                    if (AImportMode == "Corporate")
                    {
                        if ((preferredPeriodStartDay >= 1) && (DateEffective.Day != preferredPeriodStartDay))
                        {
                            // raise a warning
                            string resultText = String.Format(Catalog.GetString(
                                                                  "Line {0}: Warning: The date '{1}' should be the first day of an accounting period used by all the active ledgers."),
                                                              LineNumber.ToString(), DateEffectiveStr);

                            InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Noncritical));
                        }
                    }

                    decimal ExchangeRate = 0.0m;
                    try
                    {
                        string ExchangeRateString =
                            StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).Replace(ThousandsSeparator, "").Replace(
                                DecimalSeparator, ".").Replace("\"", String.Empty);

                        ExchangeRate = Convert.ToDecimal(ExchangeRateString, System.Globalization.CultureInfo.InvariantCulture);

                        if (ExchangeRate == 0)
                        {
                            throw new Exception();
                        }
                    }
                    catch (Exception)
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Line {0}: invalid rate of exchange ({1})"), LineNumber.ToString(), ExchangeRate);

                        InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                        continue;
                    }

                    int TimeEffective = 7200;

                    if (AImportMode == "Daily")
                    {
                        // Daily rate imports can have an optional final column which is the time
                        // Otherwise we assume the time is a default of 7200 (02:00am)
                        if ((IsShortFileFormat && (NumCols == 3)) ||
                            (!IsShortFileFormat && (NumCols == 5)))
                        {
                            string timeEffectiveStr = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true);
                            int    t = (int)new Ict.Common.TypeConverter.TShortTimeConverter().ConvertTo(timeEffectiveStr, typeof(int));

                            if (t < 0)
                            {
                                // it wasn't in the format 02:00
                                if (!Int32.TryParse(timeEffectiveStr, out t))
                                {
                                    // Not a regular Int32 either
                                    t = -1;
                                }
                            }

                            if ((t >= 0) && (t < 86400))
                            {
                                TimeEffective = t;
                            }
                            else
                            {
                                // raise an error
                                string resultText = String.Format(Catalog.GetString(
                                                                      "Line {0}: invalid effective time ({1})"), LineNumber.ToString(), t);

                                InvalidRows.Add(new Tuple <string, TResultSeverity>(resultText, TResultSeverity.Resv_Critical));
                                continue;
                            }
                        }
                    }

                    if ((AImportMode == "Corporate") && AExchangeRDT is ACorporateExchangeRateTable)
                    {
                        ACorporateExchangeRateTable ExchangeRateDT = (ACorporateExchangeRateTable)AExchangeRDT;

                        // run this code in the loop twice to get ExchangeRate value and its inverse
                        for (int i = 0; i <= 1; i++)
                        {
                            //this will cause x and y to go from 0 to 1 and 1 to 0 respectively
                            x = i;
                            y = Math.Abs(i - 1);

                            ACorporateExchangeRateRow ExchangeRow = (ACorporateExchangeRateRow)ExchangeRateDT.Rows.
                                                                    Find(new object[] { Currencies[x], Currencies[y], DateEffective });

                            if (ExchangeRow == null)                                                                                    // remove 0 for Corporate
                            {
                                ExchangeRow = (ACorporateExchangeRateRow)ExchangeRateDT.NewRowTyped();
                                ExchangeRow.FromCurrencyCode  = Currencies[x];
                                ExchangeRow.ToCurrencyCode    = Currencies[y];
                                ExchangeRow.DateEffectiveFrom = DateEffective;
                                ExchangeRateDT.Rows.Add(ExchangeRow);
                                RowsImported++;
                            }

                            if (i == 0)
                            {
                                ExchangeRow.RateOfExchange = ExchangeRate;
                            }
                            else
                            {
                                ExchangeRow.RateOfExchange = Math.Round(1 / ExchangeRate, 10);
                            }
                        }
                    }
                    else if ((AImportMode == "Daily") && AExchangeRDT is ADailyExchangeRateTable)
                    {
                        ADailyExchangeRateTable ExchangeRateDT = (ADailyExchangeRateTable)AExchangeRDT;

                        // run this code in the loop twice to get ExchangeRate value and its inverse
                        for (int i = 0; i <= 1; i++)
                        {
                            //this will cause x and y to go from 0 to 1 and 1 to 0 respectively
                            x = i;
                            y = Math.Abs(i - 1);

                            ADailyExchangeRateRow ExchangeRow = (ADailyExchangeRateRow)ExchangeRateDT.Rows.
                                                                Find(new object[] { Currencies[x], Currencies[y], DateEffective, TimeEffective });

                            if (ExchangeRow == null)                                                                                    // remove 0 for Corporate
                            {
                                ExchangeRow = (ADailyExchangeRateRow)ExchangeRateDT.NewRowTyped();
                                ExchangeRow.FromCurrencyCode  = Currencies[x];
                                ExchangeRow.ToCurrencyCode    = Currencies[y];
                                ExchangeRow.DateEffectiveFrom = DateEffective;
                                ExchangeRow.TimeEffectiveFrom = TimeEffective;
                                ExchangeRateDT.Rows.Add(ExchangeRow);
                                RowsImported++;
                            }

                            if (i == 0)
                            {
                                ExchangeRow.RateOfExchange = ExchangeRate;
                            }
                            else
                            {
                                ExchangeRow.RateOfExchange = Math.Round(1 / ExchangeRate, 10);
                            }
                        }
                    }
                }

                // if there are rows that could not be imported
                if ((InvalidRows != null) && (InvalidRows.Count > 0))
                {
                    int errorCount   = 0;
                    int warningCount = 0;

                    // Go through once just to count the errors and warnings
                    foreach (Tuple <string, TResultSeverity> Row in InvalidRows)
                    {
                        if (Row.Item2 == TResultSeverity.Resv_Noncritical)
                        {
                            warningCount++;
                        }
                        else
                        {
                            errorCount++;
                        }
                    }

                    string resultText        = String.Empty;
                    bool   messageListIsFull = false;
                    int    counter           = 0;

                    if (errorCount > 0)
                    {
                        resultText = string.Format(Catalog.GetPluralString("1 row was not imported due to invalid data:",
                                                                           "{0} rows were not imported due to invalid data:", errorCount, true), errorCount) +
                                     Environment.NewLine;
                    }

                    if (warningCount > 0)
                    {
                        resultText = string.Format(Catalog.GetPluralString("There was 1 warning associated with the imported rows:",
                                                                           "There were {0} warnings associated with the imported rows:", warningCount, true), warningCount) +
                                     Environment.NewLine;
                    }

                    // Now go through again itemising each one
                    foreach (Tuple <string, TResultSeverity> Row in InvalidRows)
                    {
                        counter++;

                        if (counter <= MAX_MESSAGE_COUNT)
                        {
                            resultText += Environment.NewLine + Row.Item1;
                        }
                        else if (!messageListIsFull)
                        {
                            resultText += String.Format(Catalog.GetString(
                                                            "{0}{0}{1} errors/warnings were reported in total.  This message contains the first {2}."),
                                                        Environment.NewLine, InvalidRows.Count, MAX_MESSAGE_COUNT);
                            messageListIsFull = true;
                        }
                    }

                    // additional message if one or more rows has an invalid number of columns
                    if (InvalidColumnCount && IsShortFileFormat)
                    {
                        resultText += String.Format("{0}{0}" + Catalog.GetString("Each row should contain 2 or 3 columns as follows:") + "{0}" +
                                                    Catalog.GetString(
                                                        "  1. Effective Date{0}  2. Exchange Rate{0}  3. Effective time in seconds (Optional for Daily Rate only)"),
                                                    Environment.NewLine);
                    }
                    else if (InvalidColumnCount && !IsShortFileFormat)
                    {
                        resultText += String.Format("{0}{0}" + Catalog.GetString("Each row should contain 4 or 5 columns as follows:") + "{0}" +
                                                    Catalog.GetString(
                                                        "    1. From Currency{0}    2. To Currency{0}    3. Effective Date{0}    4. Exchange Rate{0}    5. Effective time in seconds (Optional for Daily Rate only)"),
                                                    Environment.NewLine);
                    }

                    TVerificationResult result = new TVerificationResult(AImportMode,
                                                                         resultText,
                                                                         CommonErrorCodes.ERR_INCONGRUOUSSTRINGS,
                                                                         (errorCount > 0) ? TResultSeverity.Resv_Critical : TResultSeverity.Resv_Noncritical);
                    AResultCollection.Add(result);
                }

                DataFile.Close();

                return(RowsImported);
            }
        }
Beispiel #11
0
        /// <summary>
        /// Validates the Daily Exchange Rates screen data.
        /// </summary>
        /// <param name="AContext">Context that describes where the data validation failed.</param>
        /// <param name="ARow">The <see cref="DataRow" /> which holds the the data against which the validation is run.</param>
        /// <param name="AVerificationResultCollection">Will be filled with any <see cref="TVerificationResult" /> items if
        /// data validation errors occur.</param>
        /// <param name="AMinDateTime">The earliest allowable date.</param>
        /// <param name="AMaxDateTime">The latest allowable date.</param>
        /// <param name="AIgnoreZeroRateCheck">If true a zero rate will be allowed.  This will be the case when the Daily Exchange Rate screen is modal.</param>
        /// <param name="ALedgerTableRef">A ledger table containg the available ledgers and their base currencies</param>
        /// <param name="AEarliestAccountingPeriodStartDate">The earliest accounting period start date in all the active ledgers</param>
        /// <param name="ALatestAccountingPeriodEndDate">The latest accounting period end date in all the active ledgers</param>
        public static void ValidateDailyExchangeRate(object AContext,
                                                     ADailyExchangeRateRow ARow,
                                                     ref TVerificationResultCollection AVerificationResultCollection,
                                                     DateTime AMinDateTime,
                                                     DateTime AMaxDateTime,
                                                     bool AIgnoreZeroRateCheck,
                                                     ALedgerTable ALedgerTableRef,
                                                     DateTime AEarliestAccountingPeriodStartDate,
                                                     DateTime ALatestAccountingPeriodEndDate)
        {
            DataColumn          ValidationColumn;
            TVerificationResult VerificationResult;

            // Don't validate deleted DataRows
            if (ARow.RowState == DataRowState.Deleted)
            {
                return;
            }

            // RateOfExchange must be positive (definitely not zero unless in modal mode)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId];

            if (true)
            {
                if (AIgnoreZeroRateCheck)
                {
                    VerificationResult = TNumericalChecks.IsPositiveOrZeroDecimal(ARow.RateOfExchange,
                                                                                  String.Empty,
                                                                                  AContext, ValidationColumn);
                }
                else
                {
                    VerificationResult = TNumericalChecks.IsPositiveDecimal(ARow.RateOfExchange,
                                                                            String.Empty,
                                                                            AContext, ValidationColumn);
                }

                // Handle addition/removal to/from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult);
            }

            // Date must not be empty and must be in range
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnDateEffectiveFromId];

            if (true)
            {
                VerificationResult = TValidationControlHelper.IsNotInvalidDate(ARow.DateEffectiveFrom,
                                                                               String.Empty, AVerificationResultCollection, true,
                                                                               AContext, ValidationColumn);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult);

                if (VerificationResult == null)
                {
                    if ((AMinDateTime > DateTime.MinValue) && (AMaxDateTime < DateTime.MaxValue))
                    {
                        // Check that the date is in range
                        VerificationResult = TDateChecks.IsDateBetweenDates(ARow.DateEffectiveFrom,
                                                                            AMinDateTime,
                                                                            AMaxDateTime,
                                                                            String.Empty,
                                                                            TDateBetweenDatesCheckType.dbdctUnspecific,
                                                                            TDateBetweenDatesCheckType.dbdctUnspecific,
                                                                            AContext,
                                                                            ValidationColumn);
                    }
                    else if (AMaxDateTime < DateTime.MaxValue)
                    {
                        VerificationResult = TDateChecks.FirstLesserOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMaxDateTime,
                                                                                          String.Empty, Ict.Common.StringHelper.DateToLocalizedString(AMaxDateTime),
                                                                                          AContext, ValidationColumn);

                        if ((VerificationResult == null) && (ARow.RowState == DataRowState.Added))
                        {
                            // even without a specific minimum date it should not be too far back
                            if (ARow.DateEffectiveFrom < AEarliestAccountingPeriodStartDate)
                            {
                                VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                                                                   Catalog.GetString(
                                                                                       "The date is before the start of the earliest current accounting period of any active ledger."),
                                                                                   TResultSeverity.Resv_Noncritical);
                            }
                        }
                    }
                    else if (AMinDateTime > DateTime.MinValue)
                    {
                        VerificationResult = TDateChecks.FirstGreaterOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMinDateTime,
                                                                                           String.Empty, Ict.Common.StringHelper.DateToLocalizedString(AMinDateTime),
                                                                                           AContext, ValidationColumn);

                        if ((VerificationResult == null) && (ARow.RowState == DataRowState.Added))
                        {
                            // even without a specific maximum date it should not be too far ahead
                            if (ARow.DateEffectiveFrom > ALatestAccountingPeriodEndDate)
                            {
                                VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                                                                   Catalog.GetString(
                                                                                       "The date is after the end of the latest forwarding period of any active ledger."),
                                                                                   TResultSeverity.Resv_Noncritical);
                            }
                        }
                    }
                    else if ((AMinDateTime == DateTime.MinValue) && (AMaxDateTime == DateTime.MaxValue) &&
                             (ARow.RowState == DataRowState.Added))
                    {
                        // even without a specific maximum date it should not be too far ahead
                        if (ARow.DateEffectiveFrom > ALatestAccountingPeriodEndDate)
                        {
                            VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                                                               Catalog.GetString(
                                                                                   "The date is after the end of the latest forwarding period of any active ledger."),
                                                                               TResultSeverity.Resv_Noncritical);
                        }
                        // even without a specific minimum date it should not be too far back
                        else if (ARow.DateEffectiveFrom < AEarliestAccountingPeriodStartDate)
                        {
                            VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                                                               Catalog.GetString(
                                                                                   "The date is before the start of the earliest current accounting period of any active ledger."),
                                                                               TResultSeverity.Resv_Noncritical);
                        }
                    }

                    // Handle addition to/removal from TVerificationResultCollection
                    AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult);
                }
            }

            // Time must not be negative (indicating an error)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnTimeEffectiveFromId];

            if (true)
            {
                VerificationResult = TTimeChecks.IsValidIntegerTime(ARow.TimeEffectiveFrom,
                                                                    String.Empty,
                                                                    AContext, ValidationColumn);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult);
            }

            if (true)
            {
                // These tests are for the GUI only
                ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnToCurrencyCodeId];

                if (true)
                {
                    // One of the currencies should be the base currency of one of the ledgers
                    if (ARow.RowState == DataRowState.Added)
                    {
                        // Only do this test if the To Currency ComboBox is enabled
                        TScreenVerificationResult vr = null;
                        DataView fromView            = new DataView(ALedgerTableRef, String.Format("{0}='{1}'",
                                                                                                   ALedgerTable.GetBaseCurrencyDBName(), ARow.FromCurrencyCode), String.Empty, DataViewRowState.CurrentRows);

                        if (fromView.Count == 0)
                        {
                            DataView toView = new DataView(ALedgerTableRef, String.Format("{0}='{1}'",
                                                                                          ALedgerTable.GetBaseCurrencyDBName(), ARow.ToCurrencyCode), String.Empty, DataViewRowState.CurrentRows);

                            if (toView.Count == 0)
                            {
                                vr = new TScreenVerificationResult(AContext, ValidationColumn,
                                                                   "One of the currencies should normally be a base currency for one of the Ledgers",
                                                                   TResultSeverity.Resv_Noncritical);
                            }
                        }

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, vr);
                    }
                }
            }
        }
Beispiel #12
0
        public static ExchangeRateTDS LoadDailyExchangeRateData()
        {
            ExchangeRateTDS WorkingDS = new ExchangeRateTDS();

            WorkingDS.EnforceConstraints = false;
            TDBTransaction Transaction = null;

            DBAccess.GDBAccessObj.GetNewOrExistingAutoReadTransaction(IsolationLevel.ReadCommitted,
                                                                      TEnforceIsolationLevel.eilMinimum,
                                                                      ref Transaction,
                                                                      delegate
            {
                // Load the table so we can read bits of it into our dataset
                ADailyExchangeRateTable exchangeRates = ADailyExchangeRateAccess.LoadAll(Transaction);

                // Populate the ExchangeRateTDSADailyExchangeRate table
                //-- This is the complete query for the DAILYEXCHANGERATE TABLE
                //-- It returns all the rows from the DailyExchangeRate table
                //-- PLUS all rows from the Journal and Gift Batch tables that are NOT referenced by the Daily Exchange Rate table.
                string strSQL = "SELECT * FROM ( ";

                // -- This part of the query returns all the rows from the ExchangeRate table
                // -- All rows in Journal and Gift that DO match an entry are reported in the usage count
                // -- It will always return exactly the same number of rows that are in the Daily Exchange Rate table itself.
                // --  In the development database case it returns 312 rows.
                // --  It includes 86 Journal entries and 1 Gift Batch entry
                strSQL += "SELECT der.a_from_currency_code_c, ";
                strSQL += "  der.a_to_currency_code_c, ";
                strSQL += "  der.a_rate_of_exchange_n, ";
                strSQL += "  der.a_date_effective_from_d, ";
                strSQL += "  der.a_time_effective_from_i, ";
                strSQL += String.Format(
                    "  count(j.a_journal_number_i) AS {0}, count(gb.a_batch_number_i) AS {1}, 'DEX' as {2} ",
                    ExchangeRateTDSADailyExchangeRateTable.GetJournalUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetGiftBatchUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetTableSourceDBName());
                strSQL += "FROM PUB_a_daily_exchange_rate AS der ";
                strSQL += "LEFT OUTER JOIN PUB_a_journal j ";
                strSQL += "  ON j.a_transaction_currency_c = der.a_from_currency_code_c ";
                strSQL += "  AND j.a_base_currency_c = der.a_to_currency_code_c ";
                strSQL += "  AND j.a_exchange_rate_to_base_n = der.a_rate_of_exchange_n ";
                strSQL += "  AND j.a_date_effective_d = der.a_date_effective_from_d ";
                strSQL += "  AND a_exchange_rate_time_i = der.a_time_effective_from_i ";
                strSQL += "LEFT OUTER JOIN PUB_a_gift_batch AS gb ";
                strSQL += "  ON gb.a_currency_code_c = der.a_from_currency_code_c ";
                strSQL += "  AND gb.a_exchange_rate_to_base_n = der.a_rate_of_exchange_n ";
                strSQL += "  AND gb.a_gl_effective_date_d = der.a_date_effective_from_d ";
                strSQL += "LEFT OUTER JOIN PUB_a_ledger AS ldg ";
                strSQL += "  ON ldg.a_ledger_number_i = gb.a_ledger_number_i ";
                strSQL += "  AND ldg.a_base_currency_c = der.a_to_currency_code_c ";
                strSQL +=
                    "GROUP BY der.a_from_currency_code_c, der.a_to_currency_code_c, der.a_rate_of_exchange_n, der.a_date_effective_from_d, der.a_time_effective_from_i ";

                strSQL += Environment.NewLine;
                strSQL += "UNION ";
                strSQL += Environment.NewLine;

                // -- This part of the query returns all the rows from the journal table that do NOT have an
                // -- entry in the exchange rate table
                // -- Using the devlopment database it returns 41 unique rows associated with 53 Journal entries
                strSQL += "SELECT ";
                strSQL += "  j.a_transaction_currency_c AS a_from_currency_code_c, ";
                strSQL += "  j.a_base_currency_c AS a_to_currency_code_c, ";
                strSQL += "  j.a_exchange_rate_to_base_n AS a_rate_of_exchange_n, ";
                strSQL += "  j.a_date_effective_d AS a_date_effective_from_d, ";
                strSQL += "  j.a_exchange_rate_time_i AS a_time_effective_from_i, ";
                strSQL += String.Format(
                    "  count(j.a_transaction_currency_c) AS {0},  0 AS {1},  'J' AS {2} ",
                    ExchangeRateTDSADailyExchangeRateTable.GetJournalUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetGiftBatchUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetTableSourceDBName());
                strSQL += "FROM a_journal j ";
                strSQL += "LEFT JOIN a_daily_exchange_rate der ";
                strSQL += "  ON der.a_from_currency_code_c = j.a_transaction_currency_c ";
                strSQL += "  AND der.a_to_currency_code_c = j.a_base_currency_c ";
                strSQL += "  AND der.a_date_effective_from_d = j.a_date_effective_d ";
                strSQL += "  AND der.a_time_effective_from_i = j.a_exchange_rate_time_i ";
                strSQL += "  AND der.a_rate_of_exchange_n = j.a_exchange_rate_to_base_n ";
                strSQL += "WHERE j.a_transaction_currency_c <> j.a_base_currency_c ";
                strSQL += "  AND der.a_from_currency_code_c IS NULL ";
                strSQL +=
                    "GROUP BY j.a_transaction_currency_c, j.a_base_currency_c, j.a_exchange_rate_to_base_n, j.a_date_effective_d, j.a_exchange_rate_time_i ";

                strSQL += Environment.NewLine;
                strSQL += "UNION ";
                strSQL += Environment.NewLine;

                // -- This part of the query returns all the rows in the gift batch table that do NOT have an
                // -- entry in the exchange rate table
                // -- Using the devlopment database it returns 0 rows, because the one row in the gift batch table has already been included
                // --   in the first query (on the Daily Exchange rate table)
                strSQL += "SELECT ";
                strSQL += "  gb.a_currency_code_c AS a_from_currency_code_c, ";
                strSQL += "  ldg.a_base_currency_c AS a_to_currency_code_c, ";
                strSQL += "  gb.a_exchange_rate_to_base_n AS a_rate_of_exchange_n, ";
                strSQL += "  gb.a_gl_effective_date_d AS a_date_effective_from_d, ";
                strSQL += "  0 AS a_time_effective_from_i, ";
                strSQL += String.Format(
                    "  0 AS {0}, count(gb.a_currency_code_c) AS {1}, 'GB' AS {2} ",
                    ExchangeRateTDSADailyExchangeRateTable.GetJournalUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetGiftBatchUsageDBName(),
                    ExchangeRateTDSADailyExchangeRateTable.GetTableSourceDBName());
                strSQL += "FROM a_gift_batch gb ";
                strSQL += "LEFT JOIN a_ledger ldg ";
                strSQL += "  ON ldg.a_ledger_number_i = gb.a_ledger_number_i ";
                strSQL += "LEFT JOIN a_daily_exchange_rate der ";
                strSQL += "  ON der.a_from_currency_code_c = gb.a_currency_code_c ";
                strSQL += "  AND der.a_to_currency_code_c = ldg.a_base_currency_c ";
                strSQL += "  AND der.a_date_effective_from_d = gb.a_gl_effective_date_d ";
                strSQL += "  AND der.a_rate_of_exchange_n = gb.a_exchange_rate_to_base_n ";
                strSQL += "WHERE gb.a_currency_code_c <> ldg.a_base_currency_c ";
                strSQL += "  AND der.a_from_currency_code_c IS NULL ";
                strSQL += "GROUP BY gb.a_currency_code_c, ldg.a_base_currency_c, gb.a_exchange_rate_to_base_n, gb.a_gl_effective_date_d ";

                strSQL += ") AS allrates ";
                strSQL += "ORDER BY allrates.a_date_effective_from_d DESC, allrates.a_time_effective_from_i DESC ";

                DBAccess.GDBAccessObj.Select(WorkingDS, strSQL, WorkingDS.ADailyExchangeRate.TableName, Transaction);

                // Now populate the ExchangeRateTDSADailyExchangerateUsage table
                //-- COMPLETE QUERY TO RETURN ADailyExchangeRateUsage
                //-- Query to return the Daily Exchange Rate Usage details
                //--  Only returns rows that are in a foreign currency
                //-- Querying this table by from/to/date/time will return one row per use case
                //-- If the Journal is 0 the batch refers to a gift batch, otherwise it is a GL batch
                strSQL = "SELECT * FROM ( ";

                //-- This part of the query returns the use cases from the Journal table
                strSQL += "SELECT ";
                strSQL += "  j.a_transaction_currency_c AS a_from_currency_code_c, ";
                strSQL += "  j.a_base_currency_c AS a_to_currency_code_c, ";
                strSQL += "  j.a_exchange_rate_to_base_n AS a_rate_of_exchange_n, ";
                strSQL += "  j.a_date_effective_d AS a_date_effective_from_d, ";
                strSQL += "  j.a_exchange_rate_time_i AS a_time_effective_from_i, ";
                strSQL += String.Format(
                    "  j.a_ledger_number_i AS {0}, j.a_batch_number_i AS {1}, j.a_journal_number_i AS {2}, b.a_batch_status_c AS {3}, b.a_batch_year_i AS {4}, b.a_batch_period_i AS {5}, 'J' AS {6} ",
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetLedgerNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetJournalNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchStatusDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchYearDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchPeriodDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetTableSourceDBName());
                strSQL += "FROM a_journal j ";
                strSQL += "JOIN a_batch b ";
                strSQL += "  ON b.a_batch_number_i = j.a_batch_number_i ";
                strSQL += "  AND b.a_ledger_number_i = j.a_ledger_number_i ";
                strSQL += "WHERE j.a_transaction_currency_c <> j.a_base_currency_c ";

                strSQL += Environment.NewLine;
                strSQL += "UNION ";
                strSQL += Environment.NewLine;

                //-- This part of the query returns the use cases from the Gift Batch table
                strSQL += "SELECT ";
                strSQL += "  gb.a_currency_code_c AS a_from_currency_code_c, ";
                strSQL += "  ldg.a_base_currency_c AS a_to_currency_code_c, ";
                strSQL += "  gb.a_exchange_rate_to_base_n AS a_rate_of_exchange_n, ";
                strSQL += "  gb.a_gl_effective_date_d AS a_date_effective_from_d, ";
                strSQL += "  0 AS a_time_effective_from_i, ";
                strSQL += String.Format(
                    "  gb.a_ledger_number_i AS {0}, gb.a_batch_number_i AS {1}, 0 AS {2}, gb.a_batch_status_c AS {3}, gb.a_batch_year_i AS {4}, gb.a_batch_period_i AS {5}, 'GB' AS {6} ",
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetLedgerNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetJournalNumberDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchStatusDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchYearDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetBatchPeriodDBName(),
                    ExchangeRateTDSADailyExchangeRateUsageTable.GetTableSourceDBName());
                strSQL += "FROM a_gift_batch gb ";
                strSQL += "JOIN a_ledger ldg ";
                strSQL += "  ON ldg.a_ledger_number_i = gb.a_ledger_number_i ";
                strSQL += "WHERE gb.a_currency_code_c <> ldg.a_base_currency_c ";

                strSQL += ") AS usage ";
                strSQL += "ORDER BY usage.a_date_effective_from_d DESC, usage.a_time_effective_from_i DESC ";

                DBAccess.GDBAccessObj.Select(WorkingDS, strSQL, WorkingDS.ADailyExchangeRateUsage.TableName, Transaction);

                // Now we start a tricky bit to resolve potential primary key conflicts when the constraints are turned on.
                // By combining the Journal and Gift Batch data that is not referenced in the exchange rate table we can easily
                //  have introduced conflicts where more than one rate has been used for a given currency pair and effective date/time.
                // This is because there is no constraint that enforces the batch/journal tables to use a time from the exch rate table.
                // So we have to go through all the rows in our data table and potentially change the time to make it possible to get our primary key.

                // Start by creating a data view on the whole result set.  The oredering is important because we are going to step through the set row by row.
                // We order on: From, To, Date, Time, JournalUsage, GiftBatchUsage
                // We need to deal with the following possibilities:
                //   From  To   Date         Time   Rate  Journals  Gifts  TableSource
                //   EUR   GBP  2014-01-01   1234   2.115  0        0      DEX
                //   EUR   GBP  2014-01-01   1234   2.11   3        0      J
                //   EUR   GBP  2014-01-01   1234   2.22   1        0      J
                //   EUR   GBP  2014-01-01   1234   3.11   0        1      GB
                //
                // In the first row we have an entry from the DEX table that is not used anywhere, but a (slightly) different rate is actually used
                //   in a Journal.  So we actually don't show the DEX row.
                // In the other rows we have 3 different rates - all used somewhere.  We need to adjust the times so they are different.

                DataView dv = new DataView(WorkingDS.ADailyExchangeRate, "",
                                           String.Format("{0}, {1}, {2} DESC, {3} DESC, {4}, {5}",
                                                         ADailyExchangeRateTable.GetFromCurrencyCodeDBName(),
                                                         ADailyExchangeRateTable.GetToCurrencyCodeDBName(),
                                                         ADailyExchangeRateTable.GetDateEffectiveFromDBName(),
                                                         ADailyExchangeRateTable.GetTimeEffectiveFromDBName(),
                                                         ExchangeRateTDSADailyExchangeRateTable.GetJournalUsageDBName(),
                                                         ExchangeRateTDSADailyExchangeRateTable.GetGiftBatchUsageDBName()), DataViewRowState.CurrentRows);

                for (int i = 0; i < dv.Count - 1; i++)
                {
                    // Get the 'current' row and the 'next' one...
                    ExchangeRateTDSADailyExchangeRateRow drThis = (ExchangeRateTDSADailyExchangeRateRow)dv[i].Row;
                    ExchangeRateTDSADailyExchangeRateRow drNext = (ExchangeRateTDSADailyExchangeRateRow)dv[i + 1].Row;

                    if ((drThis.JournalUsage == 0) && (drThis.GiftBatchUsage == 0))
                    {
                        // This will be a row that the client can edit/delete, so we need to add the modification info
                        ADailyExchangeRateRow foundRow = (ADailyExchangeRateRow)exchangeRates.Rows.Find(new object[] {
                            drThis.FromCurrencyCode, drThis.ToCurrencyCode, drThis.DateEffectiveFrom, drThis.TimeEffectiveFrom
                        });

                        if (foundRow != null)
                        {
                            // it should always be non-null
                            drThis.BeginEdit();
                            drThis.ModificationId = foundRow.ModificationId;
                            drThis.DateModified   = foundRow.DateModified;
                            drThis.ModifiedBy     = foundRow.ModifiedBy;
                            drThis.DateCreated    = foundRow.DateCreated;
                            drThis.CreatedBy      = foundRow.CreatedBy;
                            drThis.EndEdit();
                        }
                    }

                    if (!drThis.FromCurrencyCode.Equals(drNext.FromCurrencyCode) ||
                        !drThis.ToCurrencyCode.Equals(drNext.ToCurrencyCode) ||
                        !drThis.DateEffectiveFrom.Equals(drNext.DateEffectiveFrom) ||
                        !drThis.TimeEffectiveFrom.Equals(drNext.TimeEffectiveFrom))
                    {
                        // Something is different so our primary key will be ok for the current row
                        continue;
                    }

                    // We have got two (or more) rows with the same potential primary key and different rates.
                    // We need to work out how many rows ahead also have the same time and adjust them all
                    bool moveForwards = (drThis.TimeEffectiveFrom < 43200);
                    int timeOffset    = 60;         // 1 minute

                    // Start by adjusting our 'next' row we are already working with
                    drNext.BeginEdit();
                    int prevTimeEffectiveFrom = drNext.TimeEffectiveFrom;
                    drNext.TimeEffectiveFrom  = (moveForwards) ? prevTimeEffectiveFrom + timeOffset : prevTimeEffectiveFrom - timeOffset;
                    timeOffset = (moveForwards) ? timeOffset + 60 : timeOffset - 60;
                    drNext.EndEdit();
                    i++;                // we can increment our main loop counter now that we have dealt with our 'next' row.

                    // Modify all the rows in the usage table that refer to the previous time
                    OnModifyEffectiveTime(WorkingDS.ADailyExchangeRateUsage, drNext.FromCurrencyCode, drNext.ToCurrencyCode,
                                          drNext.DateEffectiveFrom,
                                          prevTimeEffectiveFrom, drNext.TimeEffectiveFrom, drNext.RateOfExchange);

                    // Now look ahead even further than the 'next' row and modify those times too, adding 1 more minute to each
                    for (int k = i + 2;; k++)
                    {
                        ExchangeRateTDSADailyExchangeRateRow drLookAhead = (ExchangeRateTDSADailyExchangeRateRow)dv[k].Row;

                        if (!drThis.FromCurrencyCode.Equals(drLookAhead.FromCurrencyCode) ||
                            !drThis.ToCurrencyCode.Equals(drLookAhead.ToCurrencyCode) ||
                            !drThis.DateEffectiveFrom.Equals(drLookAhead.DateEffectiveFrom) ||
                            !drThis.TimeEffectiveFrom.Equals(drLookAhead.TimeEffectiveFrom))
                        {
                            // No more rows match our potential primary key conflict on the 'current' row.
                            break;
                        }

                        // Do exactly the same to this row as we did to the 'next' row above
                        drLookAhead.BeginEdit();
                        prevTimeEffectiveFrom         = drLookAhead.TimeEffectiveFrom;
                        drLookAhead.TimeEffectiveFrom = (moveForwards) ? prevTimeEffectiveFrom + timeOffset : prevTimeEffectiveFrom - timeOffset;
                        timeOffset = (moveForwards) ? timeOffset + 60 : timeOffset - 60;
                        drLookAhead.EndEdit();
                        i++;

                        OnModifyEffectiveTime(WorkingDS.ADailyExchangeRateUsage, drLookAhead.FromCurrencyCode, drLookAhead.ToCurrencyCode,
                                              drLookAhead.DateEffectiveFrom, prevTimeEffectiveFrom, drLookAhead.TimeEffectiveFrom, drLookAhead.RateOfExchange);
                    }
                }           // check the next row in the table so that it becomes the 'current' row.

                WorkingDS.EnforceConstraints = true;

                // Load the Corporate exchange rate table using the usual method
                ACorporateExchangeRateAccess.LoadAll(WorkingDS, Transaction);
            });

            // Accept row changes here so that the Client gets 'unmodified' rows
            WorkingDS.AcceptChanges();

            return(WorkingDS);
        }
        /// <summary>
        /// Validates the Daily Exchange Rates screen data.
        /// </summary>
        /// <param name="AContext">Context that describes where the data validation failed.</param>
        /// <param name="ARow">The <see cref="DataRow" /> which holds the the data against which the validation is run.</param>
        /// <param name="AVerificationResultCollection">Will be filled with any <see cref="TVerificationResult" /> items if
        /// data validation errors occur.</param>
        /// <param name="AValidationControlsDict">A <see cref="TValidationControlsDict" /> containing the Controls that
        /// display data that is about to be validated.</param>
        /// <param name="AMinDateTime">The earliest allowable date.</param>
        /// <param name="AMaxDateTime">The latest allowable date.</param>
        /// <param name="AIgnoreZeroRateCheck">If true a zero rate will be allowed.  This will be the case when the Daily Exchange Rate screen is modal.</param>
        /// <param name="ALedgerTableRef">A ledger table containg the available ledgers and their base currencies</param>
        /// <param name="AEarliestAccountingPeriodStartDate">The earliest accounting period start date in all the active ledgers</param>
        /// <param name="ALatestAccountingPeriodEndDate">The latest accounting period end date in all the active ledgers</param>
        public static void ValidateDailyExchangeRate(object AContext,
            ADailyExchangeRateRow ARow,
            ref TVerificationResultCollection AVerificationResultCollection,
            TValidationControlsDict AValidationControlsDict,
            DateTime AMinDateTime,
            DateTime AMaxDateTime,
            bool AIgnoreZeroRateCheck,
            ALedgerTable ALedgerTableRef,
            DateTime AEarliestAccountingPeriodStartDate,
            DateTime ALatestAccountingPeriodEndDate)
        {
            DataColumn ValidationColumn;
            TValidationControlsData ValidationControlsData;
            TVerificationResult VerificationResult;

            // Don't validate deleted DataRows
            if (ARow.RowState == DataRowState.Deleted)
            {
                return;
            }

            // RateOfExchange must be positive (definitely not zero unless in modal mode)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnRateOfExchangeId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                if (AIgnoreZeroRateCheck)
                {
                    VerificationResult = TNumericalChecks.IsPositiveOrZeroDecimal(ARow.RateOfExchange,
                        ValidationControlsData.ValidationControlLabel,
                        AContext, ValidationColumn, ValidationControlsData.ValidationControl);
                }
                else
                {
                    VerificationResult = TNumericalChecks.IsPositiveDecimal(ARow.RateOfExchange,
                        ValidationControlsData.ValidationControlLabel,
                        AContext, ValidationColumn, ValidationControlsData.ValidationControl);
                }

                // Handle addition/removal to/from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }

            // Date must not be empty and must be in range
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnDateEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TSharedValidationControlHelper.IsNotInvalidDate(ARow.DateEffectiveFrom,
                    ValidationControlsData.ValidationControlLabel, AVerificationResultCollection, true,
                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);

                if (VerificationResult == null)
                {
                    if ((AMinDateTime > DateTime.MinValue) && (AMaxDateTime < DateTime.MaxValue))
                    {
                        // Check that the date is in range
                        VerificationResult = TDateChecks.IsDateBetweenDates(ARow.DateEffectiveFrom,
                            AMinDateTime,
                            AMaxDateTime,
                            ValidationControlsData.ValidationControlLabel,
                            TDateBetweenDatesCheckType.dbdctUnspecific,
                            TDateBetweenDatesCheckType.dbdctUnspecific,
                            AContext,
                            ValidationColumn,
                            ValidationControlsData.ValidationControl);
                    }
                    else if (AMaxDateTime < DateTime.MaxValue)
                    {
                        VerificationResult = TDateChecks.FirstLesserOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMaxDateTime,
                            ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMaxDateTime),
                            AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        if ((VerificationResult == null) && (ARow.RowState == DataRowState.Added)
                            && (AContext is System.Windows.Forms.Form || AContext is System.Windows.Forms.UserControl))
                        {
                            // even without a specific minimum date it should not be too far back
                            if (ARow.DateEffectiveFrom < AEarliestAccountingPeriodStartDate)
                            {
                                VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                    Catalog.GetString(
                                        "The date is before the start of the earliest current accounting period of any active ledger."),
                                    ValidationControlsData.ValidationControl, TResultSeverity.Resv_Noncritical);
                            }
                        }
                    }
                    else if (AMinDateTime > DateTime.MinValue)
                    {
                        VerificationResult = TDateChecks.FirstGreaterOrEqualThanSecondDate(ARow.DateEffectiveFrom, AMinDateTime,
                            ValidationControlsData.ValidationControlLabel, Ict.Common.StringHelper.DateToLocalizedString(AMinDateTime),
                            AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                        if ((VerificationResult == null) && (ARow.RowState == DataRowState.Added)
                            && (AContext is System.Windows.Forms.Form || AContext is System.Windows.Forms.UserControl))
                        {
                            // even without a specific maximum date it should not be too far ahead
                            if (ARow.DateEffectiveFrom > ALatestAccountingPeriodEndDate)
                            {
                                VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                    Catalog.GetString(
                                        "The date is after the end of the latest forwarding period of any active ledger."),
                                    ValidationControlsData.ValidationControl, TResultSeverity.Resv_Noncritical);
                            }
                        }
                    }
                    else if ((AMinDateTime == DateTime.MinValue) && (AMaxDateTime == DateTime.MaxValue)
                             && (ARow.RowState == DataRowState.Added)
                             && (AContext is System.Windows.Forms.Form || AContext is System.Windows.Forms.UserControl))
                    {
                        // even without a specific maximum date it should not be too far ahead
                        if (ARow.DateEffectiveFrom > ALatestAccountingPeriodEndDate)
                        {
                            VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                Catalog.GetString(
                                    "The date is after the end of the latest forwarding period of any active ledger."),
                                ValidationControlsData.ValidationControl, TResultSeverity.Resv_Noncritical);
                        }
                        // even without a specific minimum date it should not be too far back
                        else if (ARow.DateEffectiveFrom < AEarliestAccountingPeriodStartDate)
                        {
                            VerificationResult = new TScreenVerificationResult(AContext, ValidationColumn,
                                Catalog.GetString(
                                    "The date is before the start of the earliest current accounting period of any active ledger."),
                                ValidationControlsData.ValidationControl, TResultSeverity.Resv_Noncritical);
                        }
                    }

                    // Handle addition to/removal from TVerificationResultCollection
                    AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
                }
            }

            // Time must not be negative (indicating an error)
            ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnTimeEffectiveFromId];

            if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
            {
                VerificationResult = TTimeChecks.IsValidIntegerTime(ARow.TimeEffectiveFrom,
                    ValidationControlsData.ValidationControlLabel,
                    AContext, ValidationColumn, ValidationControlsData.ValidationControl);

                // Handle addition to/removal from TVerificationResultCollection
                AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, VerificationResult, ValidationColumn);
            }

            if (AContext is System.Windows.Forms.Form || AContext is System.Windows.Forms.UserControl)
            {
                // These tests are for the GUI only
                ValidationColumn = ARow.Table.Columns[ADailyExchangeRateTable.ColumnToCurrencyCodeId];

                if (AValidationControlsDict.TryGetValue(ValidationColumn, out ValidationControlsData))
                {
                    // One of the currencies should be the base currency of one of the ledgers
                    if (ValidationControlsData.ValidationControl.Enabled && (ARow.RowState == DataRowState.Added))
                    {
                        // Only do this test if the To Currency ComboBox is enabled
                        TScreenVerificationResult vr = null;
                        DataView fromView = new DataView(ALedgerTableRef, String.Format("{0}='{1}'",
                                ALedgerTable.GetBaseCurrencyDBName(), ARow.FromCurrencyCode), String.Empty, DataViewRowState.CurrentRows);

                        if (fromView.Count == 0)
                        {
                            DataView toView = new DataView(ALedgerTableRef, String.Format("{0}='{1}'",
                                    ALedgerTable.GetBaseCurrencyDBName(), ARow.ToCurrencyCode), String.Empty, DataViewRowState.CurrentRows);

                            if (toView.Count == 0)
                            {
                                vr = new TScreenVerificationResult(AContext, ValidationColumn,
                                    "One of the currencies should normally be a base currency for one of the Ledgers",
                                    ValidationControlsData.ValidationControl, TResultSeverity.Resv_Noncritical);
                            }
                        }

                        // Handle addition to/removal from TVerificationResultCollection
                        AVerificationResultCollection.Auto_Add_Or_AddOrRemove(AContext, vr, ValidationColumn);
                    }
                }
            }
        }
Beispiel #14
0
        /// <summary>
        /// init the exchange rate, to avoid messages "Cannot find exchange rate for EUR USD"
        /// </summary>
        public static void InitExchangeRate(TDataBase ADataBase = null)
        {
            TDataBase      db          = DBAccess.Connect("InitExchangeRate", ADataBase);
            TDBTransaction Transaction = new TDBTransaction();
            bool           SubmitOK    = false;

            db.WriteTransaction(ref Transaction,
                                ref SubmitOK,
                                delegate
            {
                TAccountPeriodInfo AccountingPeriodInfo =
                    new TAccountPeriodInfo(FLedgerNumber, 1);
                ADailyExchangeRateTable dailyrates = new ADailyExchangeRateTable();
                ADailyExchangeRateRow row          = dailyrates.NewRowTyped(true);

                row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
                row.TimeEffectiveFrom = 100;
                row.FromCurrencyCode  = "USD";
                row.ToCurrencyCode    = "EUR";
                row.RateOfExchange    = 1.34m;
                dailyrates.Rows.Add(row);
                row = dailyrates.NewRowTyped(true);
                row.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
                row.TimeEffectiveFrom = 100;
                row.FromCurrencyCode  = "USD";
                row.ToCurrencyCode    = "GBP";
                row.RateOfExchange    = 1.57m;
                dailyrates.Rows.Add(row);

                if (!ADailyExchangeRateAccess.Exists(row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom, row.TimeEffectiveFrom, Transaction))
                {
                    ADailyExchangeRateAccess.SubmitChanges(dailyrates, Transaction);
                }

                ALedgerTable Ledger = ALedgerAccess.LoadByPrimaryKey(FLedgerNumber, Transaction);

                for (int periodCounter = 1; periodCounter <= Ledger[0].NumberOfAccountingPeriods + Ledger[0].NumberFwdPostingPeriods; periodCounter++)
                {
                    AccountingPeriodInfo = new TAccountPeriodInfo(FLedgerNumber, periodCounter);

                    ACorporateExchangeRateTable corprates = new ACorporateExchangeRateTable();
                    ACorporateExchangeRateRow corprow     = corprates.NewRowTyped(true);
                    corprow.DateEffectiveFrom             = AccountingPeriodInfo.PeriodStartDate;
                    corprow.TimeEffectiveFrom             = 100;
                    corprow.FromCurrencyCode = "USD";
                    corprow.ToCurrencyCode   = "EUR";
                    corprow.RateOfExchange   = 1.34m;
                    corprates.Rows.Add(corprow);
                    corprow = corprates.NewRowTyped(true);
                    corprow.DateEffectiveFrom = AccountingPeriodInfo.PeriodStartDate;
                    corprow.TimeEffectiveFrom = 100;
                    corprow.FromCurrencyCode  = "USD";
                    corprow.ToCurrencyCode    = "GBP";
                    corprow.RateOfExchange    = 1.57m;
                    corprates.Rows.Add(corprow);

                    if (!ACorporateExchangeRateAccess.Exists(corprow.FromCurrencyCode, corprow.ToCurrencyCode, corprow.DateEffectiveFrom, Transaction))
                    {
                        ACorporateExchangeRateAccess.SubmitChanges(corprates, Transaction);
                    }
                }
                SubmitOK = true;
            });

            if (ADataBase == null)
            {
                db.CloseDBConnection();
            }
        }
        /// <summary>
        /// Imports currency exchange rates, daily and corporate,
        /// from a one-of-two-styles formatted CSV file
        /// </summary>
        /// <param name="AExchangeRDT">Daily or Corporate exchange rate table</param>
        /// <param name="ADataFilename">The .CSV file to process</param>
        /// <param name="ACSVSeparator"></param>
        /// <param name="ANumberFormat"></param>
        /// <param name="ADateFormat"></param>
        /// <param name="AImportMode">Daily or Corporate</param>
        /// <param name="AResultCollection">A validation collection to which errors will be added</param>
        /// <returns>The number of rows that were actually imported.  Rows that duplicate existing rows do not count.
        /// This is usually because this is an attempt to import again after a failed previous attempt.</returns>
        private static int ImportCurrencyExRatesFromCSV(TTypedDataTable AExchangeRDT,
                                                        string ADataFilename,
                                                        string ACSVSeparator,
                                                        string ANumberFormat,
                                                        string ADateFormat,
                                                        string AImportMode,
                                                        TVerificationResultCollection AResultCollection)
        {
            if ((AImportMode != "Corporate") && (AImportMode != "Daily"))
            {
                throw new ArgumentException("Invalid value '" + AImportMode + "' for mode argument: Valid values are Corporate and Daily");
            }
            else if ((AImportMode == "Corporate") && (AExchangeRDT.GetType() != typeof(ACorporateExchangeRateTable)))
            {
                throw new ArgumentException("Invalid type of exchangeRateDT argument for mode: 'Corporate'. Needs to be: ACorporateExchangeRateTable");
            }
            else if ((AImportMode == "Daily") && (AExchangeRDT.GetType() != typeof(ADailyExchangeRateTable)))
            {
                throw new ArgumentException("Invalid type of exchangeRateDT argument for mode: 'Daily'. Needs to be: ADailyExchangeRateTable");
            }

            bool IsShortFileFormat;
            int  x, y;

            // To store the From and To currencies
            // Use an array to store these to make for easy
            //   inverting of the two currencies when calculating
            //   the inverse value.
            string[] Currencies = new string[2];

            Type DataTableType;
            int  RowsImported = 0;

            ACurrencyTable allCurrencies = new ACurrencyTable();
            DataTable      CacheDT       = TDataCache.GetCacheableDataTableFromCache("CurrencyCodeList", String.Empty, null, out DataTableType);

            allCurrencies.Merge(CacheDT);

            using (StreamReader DataFile = new StreamReader(ADataFilename, System.Text.Encoding.Default))
            {
                string ThousandsSeparator = (ANumberFormat == TDlgSelectCSVSeparator.NUMBERFORMAT_AMERICAN ? "," : ".");
                string DecimalSeparator   = (ANumberFormat == TDlgSelectCSVSeparator.NUMBERFORMAT_AMERICAN ? "." : ",");

                CultureInfo MyCultureInfoDate = new CultureInfo("en-GB");
                MyCultureInfoDate.DateTimeFormat.ShortDatePattern = ADateFormat;

                // TODO: disconnect the grid from the datasource to avoid flickering?

                string FileNameWithoutExtension = Path.GetFileNameWithoutExtension(ADataFilename);

                if ((FileNameWithoutExtension.IndexOf("_") == 3) &&
                    (FileNameWithoutExtension.LastIndexOf("_") == 3) &&
                    (FileNameWithoutExtension.Length == 7))
                {
                    // File name format assumed to be like this: USD_HKD.csv
                    IsShortFileFormat = true;
                    Currencies        = FileNameWithoutExtension.Split(new char[] { '_' });
                }
                else
                {
                    IsShortFileFormat = false;
                }

                int LineNumber = 0;

                while (!DataFile.EndOfStream)
                {
                    string Line = DataFile.ReadLine();
                    LineNumber++;

                    // See if the first line is a special case??
                    if (LineNumber == 1)
                    {
                        // see if the first line is a text header - look for digits
                        // A valid header would look like:  From,To,Date,Rate
                        bool bFoundDigit = false;

                        for (int i = 0; i < Line.Length; i++)
                        {
                            char c = Line[i];

                            if ((c >= '0') && (c <= '9'))
                            {
                                bFoundDigit = true;
                                break;
                            }
                        }

                        if (!bFoundDigit)
                        {
                            // No digits so we will assume the line is a header
                            continue;
                        }
                    }

                    //Convert separator to a char
                    char Sep = ACSVSeparator[0];
                    //Turn current line into string array of column values
                    string[] CsvColumns = Line.Split(Sep);

                    int NumCols = CsvColumns.Length;

                    //If number of columns is not 4 then import csv file is wrongly formed.
                    if (IsShortFileFormat && (NumCols < 2))
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString("Failed to import the CSV currency file:{0}   {1}{0}{0}"),
                                                          Environment.NewLine, ADataFilename);
                        resultText +=
                            String.Format(Catalog.GetString(
                                              "Line #{1} contains {2} column(s). Import files with names like 'USD_HKD.csv', where the From and To currencies are given in the name, should contain 2 or 3 columns:{0}{0}"),
                                          Environment.NewLine, LineNumber, NumCols.ToString());
                        resultText +=
                            String.Format(Catalog.GetString(
                                              "  1. Effective Date{0}  2. Exchange Rate{0}  3. Effective time in seconds (Optional for Daily Rate only)"),
                                          Environment.NewLine);
                        TVerificationResult result = new TVerificationResult(AImportMode,
                                                                             resultText,
                                                                             CommonErrorCodes.ERR_INFORMATIONMISSING,
                                                                             TResultSeverity.Resv_Critical);
                        AResultCollection.Add(result);
                        return(RowsImported);
                    }
                    else if (!IsShortFileFormat && (NumCols < 4))
                    {
                        string resultText = String.Format(Catalog.GetString("Failed to import the CSV currency file:{0}   {1}{0}{0}"),
                                                          Environment.NewLine, ADataFilename);
                        resultText += String.Format(Catalog.GetString("Line #{1} contains {2} column(s). It should have 4 or 5 as follows:{0}{0}"),
                                                    Environment.NewLine, LineNumber, NumCols.ToString());
                        resultText +=
                            String.Format(Catalog.GetString(
                                              "    1. From Currency{0}    2. To Currency{0}    3. Effective Date{0}    4. Exchange Rate{0}    5. Effective time in seconds (Optional for Daily Rate only)"),
                                          Environment.NewLine);
                        TVerificationResult result = new TVerificationResult(AImportMode,
                                                                             resultText,
                                                                             CommonErrorCodes.ERR_INFORMATIONMISSING,
                                                                             TResultSeverity.Resv_Critical);
                        AResultCollection.Add(result);
                        return(RowsImported);
                    }

                    if (!IsShortFileFormat)
                    {
                        //Read the values for the current line
                        //From currency
                        Currencies[0] = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).ToString();
                        //To currency
                        Currencies[1] = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).ToString();
                    }

                    // Perform validation on the From and To currencies at this point!!
                    if ((allCurrencies.Rows.Find(Currencies[0]) == null) || (allCurrencies.Rows.Find(Currencies[1]) == null))
                    {
                        // raise an error
                        string resultText          = String.Format(Catalog.GetString("Invalid currency in import file in line #{0}"), LineNumber.ToString());
                        TVerificationResult result = new TVerificationResult(AImportMode,
                                                                             resultText,
                                                                             CommonErrorCodes.ERR_INCONGRUOUSSTRINGS,
                                                                             TResultSeverity.Resv_Critical);
                        AResultCollection.Add(result);
                        return(RowsImported);
                    }

                    // Date parsing as in Petra 2.x instead of using XML date format!!!
                    string   DateEffectiveStr = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).Replace("\"", String.Empty);
                    DateTime DateEffective;

                    if (!DateTime.TryParse(DateEffectiveStr, MyCultureInfoDate, DateTimeStyles.None, out DateEffective))
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Invalid date ({0}) in import file in line #{1}"), DateEffectiveStr, LineNumber.ToString());
                        TVerificationResult result = new TVerificationResult(AImportMode,
                                                                             resultText,
                                                                             CommonErrorCodes.ERR_INVALIDDATE,
                                                                             TResultSeverity.Resv_Critical);
                        AResultCollection.Add(result);
                        return(RowsImported);
                    }

                    decimal ExchangeRate = 0.0m;
                    try
                    {
                        string ExchangeRateString =
                            StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true).Replace(ThousandsSeparator, "").Replace(
                                DecimalSeparator, ".").Replace("\"", String.Empty);

                        ExchangeRate = Convert.ToDecimal(ExchangeRateString, System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (Exception)
                    {
                        // raise an error
                        string resultText = String.Format(Catalog.GetString(
                                                              "Invalid rate of exchange in import file in line #{0}"), LineNumber.ToString());
                        TVerificationResult result = new TVerificationResult(AImportMode,
                                                                             resultText,
                                                                             CommonErrorCodes.ERR_INVALIDNUMBER,
                                                                             TResultSeverity.Resv_Critical);
                        AResultCollection.Add(result);
                        return(RowsImported);
                    }

                    int TimeEffective = 7200;

                    if (AImportMode == "Daily")
                    {
                        // Daily rate imports can have an optional final column which is the time
                        // Otherwise we assume the time is a default of 7200 (02:00am)
                        if ((IsShortFileFormat && (NumCols == 3)) ||
                            (!IsShortFileFormat && (NumCols == 5)))
                        {
                            string timeEffectiveStr = StringHelper.GetNextCSV(ref Line, ACSVSeparator, false, true);
                            int    t = (int)new Ict.Common.TypeConverter.TShortTimeConverter().ConvertTo(timeEffectiveStr, typeof(int));

                            if (t < 0)
                            {
                                // it wasn't in the format 02:00
                                if (!Int32.TryParse(timeEffectiveStr, out t))
                                {
                                    // Not a regular Int32 either
                                    t = -1;
                                }
                            }

                            if ((t >= 0) && (t < 86400))
                            {
                                TimeEffective = t;
                            }
                            else
                            {
                                // raise an error
                                string resultText = String.Format(Catalog.GetString(
                                                                      "Invalid effective time in import file in line #{0}"), LineNumber.ToString());
                                TVerificationResult result = new TVerificationResult(AImportMode,
                                                                                     resultText,
                                                                                     CommonErrorCodes.ERR_INVALIDINTEGERTIME,
                                                                                     TResultSeverity.Resv_Critical);
                                AResultCollection.Add(result);
                                return(RowsImported);
                            }
                        }
                    }

                    if ((AImportMode == "Corporate") && AExchangeRDT is ACorporateExchangeRateTable)
                    {
                        ACorporateExchangeRateTable ExchangeRateDT = (ACorporateExchangeRateTable)AExchangeRDT;

                        // run this code in the loop twice to get ExchangeRate value and its inverse
                        for (int i = 0; i <= 1; i++)
                        {
                            //this will cause x and y to go from 0 to 1 and 1 to 0 respectively
                            x = i;
                            y = Math.Abs(i - 1);

                            ACorporateExchangeRateRow ExchangeRow = (ACorporateExchangeRateRow)ExchangeRateDT.Rows.
                                                                    Find(new object[] { Currencies[x], Currencies[y], DateEffective });

                            if (ExchangeRow == null)                                                                                    // remove 0 for Corporate
                            {
                                ExchangeRow = (ACorporateExchangeRateRow)ExchangeRateDT.NewRowTyped();
                                ExchangeRow.FromCurrencyCode  = Currencies[x];
                                ExchangeRow.ToCurrencyCode    = Currencies[y];
                                ExchangeRow.DateEffectiveFrom = DateEffective;
                                ExchangeRateDT.Rows.Add(ExchangeRow);
                                RowsImported++;
                            }

                            if (i == 0)
                            {
                                ExchangeRow.RateOfExchange = ExchangeRate;
                            }
                            else
                            {
                                ExchangeRow.RateOfExchange = 1 / ExchangeRate;
                            }
                        }
                    }
                    else if ((AImportMode == "Daily") && AExchangeRDT is ADailyExchangeRateTable)
                    {
                        ADailyExchangeRateTable ExchangeRateDT = (ADailyExchangeRateTable)AExchangeRDT;

                        // run this code in the loop twice to get ExchangeRate value and its inverse
                        for (int i = 0; i <= 1; i++)
                        {
                            //this will cause x and y to go from 0 to 1 and 1 to 0 respectively
                            x = i;
                            y = Math.Abs(i - 1);

                            ADailyExchangeRateRow ExchangeRow = (ADailyExchangeRateRow)ExchangeRateDT.Rows.
                                                                Find(new object[] { Currencies[x], Currencies[y], DateEffective, TimeEffective });

                            if (ExchangeRow == null)                                                                                    // remove 0 for Corporate
                            {
                                ExchangeRow = (ADailyExchangeRateRow)ExchangeRateDT.NewRowTyped();
                                ExchangeRow.FromCurrencyCode  = Currencies[x];
                                ExchangeRow.ToCurrencyCode    = Currencies[y];
                                ExchangeRow.DateEffectiveFrom = DateEffective;
                                ExchangeRow.TimeEffectiveFrom = TimeEffective;
                                ExchangeRateDT.Rows.Add(ExchangeRow);
                                RowsImported++;
                            }

                            if (i == 0)
                            {
                                ExchangeRow.RateOfExchange = ExchangeRate;
                            }
                            else
                            {
                                ExchangeRow.RateOfExchange = 1 / ExchangeRate;
                            }
                        }
                    }
                }

                DataFile.Close();

                return(RowsImported);
            }
        }