Beispiel #1
0
        public async Task <long> CreateDocumentMatchingAsync(DocumentMatching documentMatching)
        {
            var queryParameters = new DynamicParameters();

            queryParameters.Add("@DataversionId", documentMatching.DataversionId);
            queryParameters.Add("@TransactionDocumentId", documentMatching.TransactionDocumentId);
            queryParameters.Add("@Amount", documentMatching.Amount);
            queryParameters.Add("@MatchFlagId", documentMatching.MatchFlagId);
            queryParameters.Add("@AmountInFunctionalCurrency", documentMatching.AmountInFunctionalCurrency);
            queryParameters.Add("@AmountInStatutoryCurrency", documentMatching.AmountInStatutoryCurrency);
            queryParameters.Add("@ValueDate", documentMatching.ValueDate);
            queryParameters.Add("@CompanyId", documentMatching.CompanyId);
            queryParameters.Add("@DepartmentId", documentMatching.DepartmentId);
            queryParameters.Add("@TransactionDirectionId", documentMatching.TransactionDirectionId);
            queryParameters.Add("@LineId", documentMatching.LineId);
            queryParameters.Add("@SecondaryDocumentReferenceId", documentMatching.SecondaryDocumentReferenceId);
            queryParameters.Add("@SourceJournalLineId", documentMatching.SourceJournalLineId);
            queryParameters.Add("@SourceInvoiceId", documentMatching.SourceInvoiceId);
            queryParameters.Add("@SourceCashLineId", documentMatching.SourceCashLineId);
            queryParameters.Add("@MatchedJournalLineId", documentMatching.MatchedJournalLineId);
            queryParameters.Add("@MatchedInvoiceId", documentMatching.MatchedInvoiceId);
            queryParameters.Add("@MatchedCashLineId", documentMatching.MatchedCashLineId);

            return(await ExecuteScalarAsync <long>(StoredProcedureNames.CreateDocumentMatching, queryParameters, true));
        }
Beispiel #2
0
        private static void GenerateInformationForCashByPickingDiffClient(
            Cash cash,
            out IEnumerable <ManualJournalLine> counterpartyTransferJournalLines,
            out SortedList <int, DocumentMatching> linkBetweenJournalLineNumberAndDM,
            out MatchFlag matchFlagCpForMatchedDocument,
            out MatchFlag matchFlagCpForCash,
            CostType fxRealCostType)
        {
            linkBetweenJournalLineNumberAndDM = new SortedList <int, DocumentMatching>();
            var counter            = 1;
            var manualJournalLines = new List <ManualJournalLine>();

            matchFlagCpForMatchedDocument = new MatchFlag()
            {
                CompanyId        = cash.CompanyId,
                CounterPartyCode = cash.CounterPartyCode,
                // Apparently, the only way to get the counterparty of the matched documents is in the document matching
                CounterPartyId      = cash.DocumentMatchings.First().MatchingCounterpartyId,
                CurrencyCode        = cash.CurrencyCode,
                IsPrematch          = true,
                PaymentDocumentDate = cash.DocumentDate,
                DocumentMatchings   = new List <DocumentMatching>(),
            };
            matchFlagCpForCash = new MatchFlag()
            {
                CashIdOfCashByPicking = cash.CashId,
                CompanyId             = cash.CompanyId,
                CounterPartyCode      = cash.CounterPartyCode,
                CounterPartyId        = cash.CounterPartyId,
                CurrencyCode          = cash.CurrencyCode,
                IsPrematch            = true,
                PaymentDocumentDate   = cash.DocumentDate,
                DocumentMatchings     = new List <DocumentMatching>()
            };

            foreach (var documentMatching in cash.DocumentMatchings)
            {
                var cashLine = cash.CashLines.ToList().Find(cl =>
                                                            ((cl.InitiallyMatchedInvoiceId.HasValue && documentMatching.SourceInvoiceId.HasValue) && cl.InitiallyMatchedInvoiceId.Value == documentMatching.SourceInvoiceId.Value) ||
                                                            ((cl.InitiallyMatchedCashLineId.HasValue && documentMatching.SourceCashLineId.HasValue) && cl.InitiallyMatchedCashLineId.Value == documentMatching.SourceCashLineId.Value) ||
                                                            ((cl.InitiallyMatchedJournalLineId.HasValue && documentMatching.SourceJournalLineId.HasValue) && cl.InitiallyMatchedJournalLineId.Value == documentMatching.SourceJournalLineId.Value));

                linkBetweenJournalLineNumberAndDM[counter] = documentMatching;

                // dm for the matched object
                matchFlagCpForMatchedDocument.DocumentMatchings.Add(new DocumentMatching()
                {
                    TransactionDocumentId        = documentMatching.TransactionDocumentId,
                    MatchedAmount                = documentMatching.AmountToBePaid,
                    ValueDate                    = documentMatching.InvoiceGLDate ?? documentMatching.DocumentDate,
                    DepartmentId                 = documentMatching.DepartmentId,
                    TransactionDirectionId       = documentMatching.TransactionDirectionId,
                    AmountInFunctionalCurrency   = documentMatching.AmountInFunctionalCurrency,
                    AmountInStatutoryCurrency    = documentMatching.AmountInStatutoryCurrency,
                    SecondaryDocumentReferenceId = cash.TransactionDocumentId,
                    SourceCashLineId             = documentMatching.SourceCashLineId,
                    SourceInvoiceId              = documentMatching.SourceInvoiceId,
                    SourceJournalLineId          = documentMatching.SourceJournalLineId,
                    MatchedCashLineId            = cashLine?.CashLineId,
                    PaymentDocumentDate          = cash.DocumentDate
                });

                int signForAccountingDocuments = 1; // Depends on the matched document
                switch ((MasterDocumentType)documentMatching.TransactionDocumentTypeId)
                {
                case MasterDocumentType.CP:
                case MasterDocumentType.SI:
                case MasterDocumentType.DN:
                case MasterDocumentType.JL:
                    signForAccountingDocuments = -1;
                    break;

                case MasterDocumentType.CI:
                case MasterDocumentType.PI:
                case MasterDocumentType.CN:
                    signForAccountingDocuments = 1;
                    break;
                }

                // dm for the journal - 'removal' from customer 1
                var jlDM = new DocumentMatching()
                {
                    TransactionDocumentId        = null, // Will be set later, to represent one of the JL lines
                    MatchedAmount                = documentMatching.AmountToBePaid * signForAccountingDocuments,
                    ValueDate                    = documentMatching.InvoiceGLDate ?? documentMatching.DocumentDate,
                    DepartmentId                 = documentMatching.DepartmentId,
                    TransactionDirectionId       = documentMatching.TransactionDirectionId,
                    AmountInFunctionalCurrency   = documentMatching.AmountInFunctionalCurrency * signForAccountingDocuments,
                    AmountInStatutoryCurrency    = documentMatching.AmountInStatutoryCurrency * signForAccountingDocuments,
                    SecondaryDocumentReferenceId = cash.TransactionDocumentId,
                    PaymentDocumentDate          = cash.DocumentDate
                };
                matchFlagCpForMatchedDocument.DocumentMatchings.Add(jlDM);
                linkBetweenJournalLineNumberAndDM[counter] = jlDM;

                // Creation of the manual journal which must have the exact same amounts than the document matching
                var matchedCounterpartyJL = new ManualJournalLine()
                {
                    ClientAccountId     = cash.MatchingCounterpartyId,
                    AssociatedAccountId = cash.MatchingCounterpartyId,
                    AccountLineTypeId   = documentMatching.AccountLineTypeId,
                    Amount       = documentMatching.AmountToBePaid * signForAccountingDocuments,
                    Narrative    = documentMatching.Narrative,
                    DepartmentId = cash.DepartmentId,

                    // On Diff Client, SecondaryDocuementReference should be the reference to the cash
                    SecondaryDocumentReference = cash.DocumentReference,

                    ExternalDocumentReference = documentMatching.ExternalReference,
                    CharterId  = null,
                    SectionId  = null,
                    LineNumber = counter++,
                    CostTypeId = fxRealCostType.CostTypeId
                };

                manualJournalLines.Add(matchedCounterpartyJL);

                // dm of the cash
                decimal?signedAmountForCash = documentMatching.AmountToBePaid * CalculateSignForCashDocumentMatching(
                    (DirectionType)cash.CashTypeId, (MasterDocumentType)documentMatching.TransactionDocumentTypeId);
                matchFlagCpForCash.DocumentMatchings.Add(new DocumentMatching()
                {
                    TransactionDocumentId        = cash.TransactionDocumentId,
                    MatchedAmount                = cashLine.Amount.Value, // The cashline contains the signed value
                    ValueDate                    = cash.DocumentDate,
                    DepartmentId                 = documentMatching.DepartmentId,
                    TransactionDirectionId       = documentMatching.TransactionDirectionId,
                    AmountInFunctionalCurrency   = cashLine?.AmountInFunctionalCurrency, // The cashline contains the signed value
                    AmountInStatutoryCurrency    = cashLine?.AmountInStatutoryCurrency,  // The cashline contains the signed value
                    SecondaryDocumentReferenceId = documentMatching.TransactionDocumentId,
                    SourceCashLineId             = cashLine?.CashLineId,
                    MatchedJournalLineId         = documentMatching.SourceJournalLineId,
                    MatchedCashLineId            = documentMatching.SourceCashLineId,
                    MatchedInvoiceId             = documentMatching.SourceInvoiceId,
                    PaymentDocumentDate          = cash.DocumentDate
                });

                // Second dm for the journal
                jlDM = new DocumentMatching()
                {
                    TransactionDocumentId        = null, // Will be set later, to represent one of the JL lines
                    MatchedAmount                = documentMatching.AmountToBePaid * signForAccountingDocuments * -1,
                    ValueDate                    = cash.DocumentDate,
                    DepartmentId                 = documentMatching.DepartmentId,
                    TransactionDirectionId       = documentMatching.TransactionDirectionId,
                    AmountInFunctionalCurrency   = documentMatching.AmountInFunctionalCurrency * signForAccountingDocuments * -1, // The cashline contains the signed value
                    AmountInStatutoryCurrency    = documentMatching.AmountInStatutoryCurrency * signForAccountingDocuments * -1,  // The cashline contains the signed value
                    SecondaryDocumentReferenceId = documentMatching.TransactionDocumentId,
                    PaymentDocumentDate          = cash.DocumentDate
                };
                matchFlagCpForCash.DocumentMatchings.Add(jlDM);
                linkBetweenJournalLineNumberAndDM[counter] = jlDM;

                var cashCounterpartyJL = new ManualJournalLine()
                {
                    ClientAccountId     = cash.PaymentCounterpartyId,
                    AssociatedAccountId = cash.PaymentCounterpartyId,
                    AccountLineTypeId   = documentMatching.AccountLineTypeId,
                    Amount       = documentMatching.AmountToBePaid * signForAccountingDocuments * -1,
                    Narrative    = documentMatching.Narrative,
                    DepartmentId = cash.DepartmentId,

                    // On Diff Client, SecondaryDocuementReference should be the reference to the cash
                    SecondaryDocumentReference = cash.DocumentReference,

                    ExternalDocumentReference = documentMatching.ExternalReference,
                    CharterId  = null,
                    SectionId  = null,
                    LineNumber = counter++,
                    CostTypeId = fxRealCostType.CostTypeId
                };

                manualJournalLines.Add(cashCounterpartyJL);
            }

            counterpartyTransferJournalLines = manualJournalLines;
        }
Beispiel #3
0
        /// <summary>
        /// Handles the creation of a manual document matching
        /// </summary>
        /// <param name="request">request</param>
        /// <param name="cancellationToken">cancellationToken</param>
        public async Task <ManualDocumentMatchingRecord> Handle(CreateManualDocumentMatchingCommand request, CancellationToken cancellationToken)
        {
            _unitOfWork.BeginTransaction();
            try
            {
                var manualDocumentMatchingRecord = _mapper.Map <ManualDocumentMatchingRecord>(request);

                var matchableDocumentIds = new List <MatchableSourceIdDto>();

                // Getting the information for the documents that we want to match
                // We get mostly the rates of exchange which have been fixed during the posting of these documents
                foreach (var document in manualDocumentMatchingRecord.DocumentMatchings)
                {
                    matchableDocumentIds.Add(new MatchableSourceIdDto()
                    {
                        SourceCashLineId = document.SourceCashLineId, SourceInvoiceId = document.SourceInvoiceId, SourceJournalLineId = document.SourceJournalLineId
                    });
                }

                var matchableDocumentsSummaryInformation = await _manualDocumentMatchingQueries.GetMatchableDocumentsSummaryInformation(
                    request.Company,
                    matchableDocumentIds);

                // Rebuilding a complete list of records to be pushed as a parameter of the "create match" SP
                List <DocumentMatching> documentMatchingRecords = new List <DocumentMatching>();
                foreach (var documentMatchingRecord in manualDocumentMatchingRecord.DocumentMatchings)
                {
                    DocumentMatching documentMatching = new DocumentMatching();
                    documentMatching.TransactionDocumentId = documentMatchingRecord.TransactionDocumentId;

                    // There is a complete mismatch into the names used in the exchange with the
                    // app (documentmatching.Amount is the full document amount), and the one
                    // understood by the DB (documentmatching.Amount is the amount matched in the doc matching)
                    // The right solution should be to use different classes...
                    documentMatching.Amount = documentMatchingRecord.DocumentAmount;

                    var matchableDocumentSummaryInformation = matchableDocumentsSummaryInformation
                                                              .Where(d => d.TransactionDocumentId == documentMatchingRecord.TransactionDocumentId).FirstOrDefault();
                    switch ((MasterDocumentType)matchableDocumentSummaryInformation.TransactionDocumentTypeId)
                    {
                    case MasterDocumentType.PI:
                    case MasterDocumentType.CN:
                    case MasterDocumentType.CP:
                        documentMatching.TransactionDirectionId = (int)TransactionDirection.Pay;
                        break;

                    case MasterDocumentType.SI:
                    case MasterDocumentType.DN:
                    case MasterDocumentType.CI:
                        documentMatching.TransactionDirectionId = (int)TransactionDirection.Recieve;
                        break;

                    case MasterDocumentType.JL:
                        documentMatching.TransactionDirectionId = documentMatching.DocumentAmount > 0 ?
                                                                  (int)TransactionDirection.Pay : (int)TransactionDirection.Recieve;
                        break;

                    default:
                        // We should not be there...
                        break;
                    }

                    switch ((MasterDocumentType)matchableDocumentSummaryInformation.TransactionDocumentTypeId)
                    {
                    case MasterDocumentType.PI:
                    case MasterDocumentType.CN:
                    case MasterDocumentType.CI:
                        documentMatching.SigningFactor = -1;
                        break;

                    case MasterDocumentType.CP:
                    case MasterDocumentType.SI:
                    case MasterDocumentType.DN:
                    case MasterDocumentType.JL:
                        documentMatching.SigningFactor = 1;
                        break;
                    }

                    documentMatching.TransactionDocumentTypeId = documentMatchingRecord.TransactionDocumentTypeId;
                    documentMatching.DepartmentId = documentMatchingRecord.DepartmentId;
                    documentMatching.DocumentDate = documentMatchingRecord.DocumentDate;
                    decimal?matchedAmount = documentMatching.Amount;

                    // Calculation of the statutory / fct ccy amounts for the document matching
                    if (matchedAmount == matchableDocumentSummaryInformation.UnmatchedAmount)
                    {
                        // In the case we are fully matching the document, we set the statutory & functional matched amounts
                        // to the value of the remaining unmatched amount. This way, we are sure that, in the case a document has
                        // multiple matchings, the sum of the matched amounts in statutory & fct ccies are = to the total amount
                        // in these ccies
                        documentMatching.StatutoryCcyAmount  = matchableDocumentSummaryInformation.UnmatchedAmountInStatutoryCurrency;
                        documentMatching.FunctionalCcyAmount = matchableDocumentSummaryInformation.UnmatchedAmountInFunctionalCurrency;
                    }
                    else
                    {
                        // We are not fully matching the matchable document.
                        // Calculate the statutory & functional currencies based on the rate of exchange stored
                        // in the transaction document which is matched, at the time of its posting
                        // NOTE THAT the unmatched ccies will be updated by a SP called when saving the document matching
                        documentMatching.StatutoryCcyAmount = AmountConverter.ConvertAmountThroughIntermediateRoeToUSD(
                            documentMatching.Amount.Value,
                            matchableDocumentSummaryInformation.RoeDocumentCurrency,
                            matchableDocumentSummaryInformation.RoeDocumentCurrencyType,
                            matchableDocumentSummaryInformation.RoeStatutoryCurrency,
                            matchableDocumentSummaryInformation.RoeStatutoryCurrencyType,
                            2);
                        documentMatching.FunctionalCcyAmount = AmountConverter.ConvertAmountThroughIntermediateRoeToUSD(
                            documentMatching.Amount.Value,
                            matchableDocumentSummaryInformation.RoeDocumentCurrency,
                            matchableDocumentSummaryInformation.RoeDocumentCurrencyType,
                            matchableDocumentSummaryInformation.RoeFunctionalCurrency,
                            matchableDocumentSummaryInformation.RoeFunctionalCurrencyType,
                            2);
                    }

                    documentMatching.IsCreditOrDebit = documentMatchingRecord.IsCreditOrDebit;
                    if (documentMatchingRecord.LineId == 0)
                    {
                        documentMatching.LineId = null;
                    }
                    else
                    {
                        documentMatching.LineId = documentMatchingRecord.LineId;
                    }

                    documentMatching.Credit              = documentMatchingRecord.Credit;
                    documentMatching.Debit               = documentMatchingRecord.Debit;
                    documentMatching.SourceCashLineId    = documentMatchingRecord.SourceCashLineId == 0 ? null : documentMatchingRecord.SourceCashLineId;
                    documentMatching.SourceInvoiceId     = documentMatchingRecord.SourceInvoiceId == 0 ? null : documentMatchingRecord.SourceInvoiceId;
                    documentMatching.SourceJournalLineId = documentMatchingRecord.SourceJournalLineId == 0 ? null : documentMatchingRecord.SourceJournalLineId;
                    documentMatchingRecords.Add(documentMatching);
                }

                // Saving the matchflag in the database
                manualDocumentMatchingRecord.DocumentMatchings = documentMatchingRecords;
                var matchingRecord = await _manualDocumentMatchingRepository.CreateUpdateDocumentMatching(manualDocumentMatchingRecord);

                manualDocumentMatchingRecord.MatchFlagCode = matchingRecord.MatchFlagCode;
                manualDocumentMatchingRecord.MatchFlagId   = matchingRecord.MatchFlagId;

                // Calculating the sums of the fct & statut ccy amounts for all the documents, to check if they are = 0 or not
                // This will determine the need for a reval record
                var sumsByDepartmentAndDirection = documentMatchingRecords.GroupBy(d => new { d.DepartmentId, d.TransactionDirectionId })
                                                   .Select(g => new
                {
                    g.Key.DepartmentId,
                    g.Key.TransactionDirectionId,
                    Amount = g.Sum(s => s.Amount * s.SigningFactor),
                    FunctionalCcyAmount = g.Sum(s => s.FunctionalCcyAmount * s.SigningFactor),
                    StatutoryCcyAmount  = g.Sum(s => s.StatutoryCcyAmount * s.SigningFactor),
                }).ToList();

                if (sumsByDepartmentAndDirection.Any(sum => sum.FunctionalCcyAmount != 0 || sum.StatutoryCcyAmount != 0))
                {
                    // Need to create a reval document
                    // Note that reval documents are created (for cash by picking) at posting time of the cbp
                    var revaluationInformation = await _transactionDocumentRepository.CreateRevaluation(
                        request.Company,
                        null,
                        request.CurrencyCode,
                        matchingRecord.MatchFlagId.Value,
                        request.PaymentDocumentDate,
                        request.PaymentDocumentDate,
                        request.PaymentDocumentDate);
                    await EnqueueMessageForAccountingDocumentCreation(revaluationInformation.TransactionDocumentId, request.Company);

                    manualDocumentMatchingRecord.ReversalRevalJournalCode = revaluationInformation.DocumentReference;
                }

                _unitOfWork.Commit();
                _logger.LogInformation("Document match created with match id {Atlas.MatchFlagId}.", manualDocumentMatchingRecord.MatchFlagId.Value);
                return(manualDocumentMatchingRecord);
            }
            catch
            {
                _unitOfWork.Rollback();
                throw;
            }
        }