Esempio n. 1
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;
            }
        }