private async Task SaveBankTransferFeeAsync(Collation collation,
                                             CollationSearch option,
                                             DateTime updateAt,
                                             CancellationToken token = default(CancellationToken))
 {
     if (collation.CustomerId > 0)
     {
         var fee = new CustomerFee {
             CustomerId = collation.CustomerId,
             CurrencyId = collation.CurrencyId,
             Fee        = collation.BankTransferFee,
             NewFee     = collation.BankTransferFee,
             UpdateAt   = updateAt,
             CreateAt   = updateAt,
             CreateBy   = option.LoginUserId,
             UpdateBy   = option.LoginUserId,
         };
         await addCustomerFeeQueryProcessor.SaveAsync(fee, token);
     }
     if (collation.PaymentAgencyId > 0)
     {
         var fee = new PaymentAgencyFee {
             PaymentAgencyId = collation.PaymentAgencyId,
             CurrencyId      = collation.CurrencyId,
             Fee             = collation.BankTransferFee,
             NewFee          = collation.BankTransferFee,
             UpdateAt        = updateAt,
             CreateAt        = updateAt,
             CreateBy        = option.LoginUserId,
             UpdateBy        = option.LoginUserId,
         };
         await addPaymentAgencyFeeQueryProcessor.SaveAsync(fee, token);
     }
 }
Exemple #2
0
        public Task <int> CollateMissingAsync(CollationSearch collationSearch, CancellationToken token = default(CancellationToken))
        {
            var query = @"
EXECUTE [dbo].[uspCollationMissing] @ClientKey
";

            return(dbHelper.ExecuteAsync(query, collationSearch, token));
        }
Exemple #3
0
        public async Task <IEnumerable <MatchingHeader> > SearchMatchedDataR(CollationSearch option)
        => await hubContext.DoAsync(option.ConnectionId, async (notifier, token) =>
        {
            var result = (await matchingProcessor.SearchMatchedDataAsync(option, token)).ToArray();
            notifier?.UpdateState();

            return(result);
        });
Exemple #4
0
        public async Task <IEnumerable <MatchingHeader> > SearchMatchedDataAsync(CollationSearch CollationSearch, CancellationToken token = default(CancellationToken))
        {
            var collationSetting = (await getCollationSettingQueryProcessor.GetAsync(CollationSearch.CompanyId, token));

            CollationSearch.SortOrderDirection = collationSetting.SortOrderDirection;

            var items = await matchingQueryProcessor.SearchMatchedDataAsync(CollationSearch, token);

            return(items);
        }
Exemple #5
0
 public async Task< MatchingResult> SequentialMatchingAsync(string SessionKey,
     Collation[] Collations,
     CollationSearch CollationSearch,
     string connectionId)
     => await authorizationProcessor.DoAuthorizeAsync(SessionKey, async token =>
     {
         var notifier = hubContext.CreateNotifier(connectionId);
         try
         {
             var result = await matchingSequentialProcessor.MatchAsync(Collations, CollationSearch, token, notifier);
             return result;
         }
         catch (Exception)
         {
             notifier?.Abort(); // todo: cancellation
             throw;
         }
     }, logger, connectionId);
Exemple #6
0
        public async Task <IEnumerable <Collation> > CollateAsync(CollationSearch collationSearch,
                                                                  CancellationToken token    = default(CancellationToken),
                                                                  IProgressNotifier notifier = null)
        {
            var orders = (await getCollationOrderQueryProcessor.GetItemsAsync(collationSearch.CompanyId, token))
                         .Where(x => x.Available == 1)
                         .OrderBy(x => x.ExecutionOrder).ToArray();

            using (var scope = transactionScopeBuilder.Create())
            {
                var collationSetting = (await getCollationSettingQueryProcessor.GetAsync(collationSearch.CompanyId, token));
                collationSearch.SortOrderDirection = collationSetting.SortOrderDirection;

                await collationQueryProcessor.InitializeAsync(collationSearch, token);

                notifier?.UpdateState();

                foreach (var order in orders)
                {
                    switch (order.CollationTypeId)
                    {
                    case 0: await collationQueryProcessor.CollatePayerCodeAsync(collationSearch, token); break;

                    case 1: await collationQueryProcessor.CollateCustomerIdAsync(collationSearch, token); break;

                    case 2: await collationQueryProcessor.CollateHistoryAsync(collationSearch, token); break;

                    case 3: await collationQueryProcessor.CollatePayerNameAsync(collationSearch, token); break;

                    case 4: await collationQueryProcessor.CollateKeyAsync(collationSearch, token); break;
                    }
                    notifier?.UpdateState();
                }
                await collationQueryProcessor.CollateMissingAsync(collationSearch, token);

                notifier?.UpdateState();
                var items = await collationQueryProcessor.GetItemsAsync(collationSearch, token);

                notifier?.UpdateState();

                scope.Complete();
                return(items);
            }
        }
Exemple #7
0
 public async Task<CollationsResult> CollateAsync(string SessionKey, CollationSearch CollationSearch, string connectionId)
     => await authorizationProcessor.DoAuthorizeAsync(SessionKey, async token =>
     {
         var notifier = hubContext.CreateNotifier(connectionId);
         try
         {
             var result = (await collationProcessor.CollateAsync(CollationSearch, token, notifier)).ToList();
             return new CollationsResult
             {
                 ProcessResult = new ProcessResult { Result = true },
                 Collation = result,
             };
         }
         catch (Exception) // cancellation
         {
             notifier?.Abort();
             throw;
         }
     }, logger, connectionId);
Exemple #8
0
        public Task <int> InitializeAsync(CollationSearch collationSearch, CancellationToken token = default(CancellationToken))
        {
            var query = @"
EXECUTE [dbo].[uspCollationInitialize]
  @ClientKey
, @CompanyId
, @CurrencyId
, @RecordedAtFrom
, @RecordedAtTo
, @DueAtFrom
, @DueAtTo
, @BillingType
, @LimitDateType
, @AmountType
, @UseDepartmentWork, @UseSectionWork
";

            return(dbHelper.ExecuteAsync(query, collationSearch, token));
        }
Exemple #9
0
 public async Task<MatchingSourceResult> SolveAsync(string SessionKey, MatchingSource source, CollationSearch option)
     => await authorizationProcessor.DoAuthorizeAsync(SessionKey, async token =>
     {
         var result = await matchingSolveProcessor.SolveAsync(source, option, token: token);
         return new MatchingSourceResult
         {
             MatchingSource = result,
             ProcessResult = new ProcessResult { Result = true},
         };
     }, logger);
Exemple #10
0
 public async Task<MatchingHeadersResult> SearchMatchedDataAsync(string SessionKey, CollationSearch CollationSearch, string connectionId)
     => await authorizationProcessor.DoAuthorizeAsync(SessionKey, async token =>
     {
         var notifier = hubContext.CreateNotifier(connectionId);
         var result = (await matchingProcessor.SearchMatchedDataAsync(CollationSearch, token)).ToList();
         notifier?.UpdateState();
         return new MatchingHeadersResult
         {
             ProcessResult = new ProcessResult { Result = true },
             MatchingHeaders = result,
         };
     }, logger, connectionId);
Exemple #11
0
 public async Task <ActionResult <IEnumerable <Collation> > > CollateR(CollationSearch option)
 => await hubContext.DoAsync(option.ConnectionId, async (notifier, token) => (await collationProcessor.CollateAsync(option, token, notifier)).ToArray());
Exemple #12
0
 public async Task <ActionResult <IEnumerable <Collation> > > Collate(CollationSearch option, CancellationToken token)
 => (await collationProcessor.CollateAsync(option, token, null)).ToArray();
Exemple #13
0
        public async Task <ActionResult <IEnumerable <MatchingHeader> > > SearchMatchedData(CollationSearch option, CancellationToken token)
        {
            var result = (await matchingProcessor.SearchMatchedDataAsync(option, token)).ToArray();

            return(result);
        }
Exemple #14
0
        public Task <IEnumerable <Collation> > GetItemsAsync(CollationSearch option, CancellationToken token = default(CancellationToken))
        {
            var receiptDisplayOrder = "";

            switch (option.SortOrderDirection)
            {
            case SortOrderColumnType.MinRecordedAt:
                receiptDisplayOrder = "COALESCE(wc.MinReceiptRecordedAt, '9999/12/31') ASC";
                break;

            case SortOrderColumnType.MaxRecordedAt:
                receiptDisplayOrder = "COALESCE(wc.MaxReceiptRecordedAt, '9999/12/31') DESC";
                break;

            case SortOrderColumnType.MinReceiptId:
                receiptDisplayOrder = "wc.MinReceiptId ASC";
                break;

            case SortOrderColumnType.MaxReceiptId:
                receiptDisplayOrder = "wc.MaxReceiptId DESC";
                break;

            case SortOrderColumnType.PayerNameDesc:
                receiptDisplayOrder = "wc.PayerName DESC";
                break;

            default:
                receiptDisplayOrder = "wc.PayerName ASC";
                break;
            }

            var query = $@"
SELECT wc.BillingAmount
     , wc.BillingCount
     , wc.ReceiptAmount
     , wc.ReceiptCount
     , wc.ParentCustomerId
     , wc.ParentCustomerId [CustomerId]
     , wc.PaymentAgencyId
     , wc.PayerName
     , wc.CurrencyId
     , wc.AdvanceReceivedCount
     , cs.Code [CustomerCode]
     , cs.Name [CustomerName]
     , pa.Code [PaymentAgencyCode]
     , pa.Name [PaymentAgencyName]
     , cs.IsParent
     , cy.Code [CurrencyCode]
     , cy.DisplayOrder [DisplayOrder]
     , cy.Tolerance [CurrencyTolerance]
     , COALESCE(pa.ShareTransferFee, cs.ShareTransferFee) [ShareTransferFee]
     , COALESCE(pa.UseFeeTolerance , cs.UseFeeTolerance)  [UseFeeTolerance]
     , COALESCE(pa.UseFeeLearning  , cs.UseFeeLearning )  [UseFeeLearning]
     , COALESCE(pa.UseKanaLearning , cs.UseKanaLearning)  [UseKanaLearning]
     , COALESCE(pa.Code, cs.Code) [DispCustomerCode]
     , COALESCE(pa.Name, cs.Name) [DispCustomerName]
     , COALESCE(pa.Kana, cs.Kana) [DispCustomerKana]
     , CASE wc.BillingCount WHEN 0 THEN NULL ELSE wc.BillingAmount END [DispBillingAmount]
     , CASE wc.ReceiptCount WHEN 0 THEN NULL ELSE wc.ReceiptAmount END [DispReceiptAmount]
     , CASE wc.BillingCount WHEN 0 THEN NULL ELSE wc.BillingCount  END [DispBillingCount]
     , CASE wc.ReceiptCount WHEN 0 THEN NULL ELSE wc.ReceiptCount  END [DispReceiptCount]
     , CASE wc.BillingCount WHEN 0 THEN 1    ELSE 0                END [BillingPriority]
     , CASE wc.ReceiptCount WHEN 0 THEN 1    ELSE 0                END [ReceiptPriority]
     , wc.ForceMatchingIndividually
     , 0 [Checked]
     , 0 [BankTransferFee]
     , CASE WHEN dupe.[CurrencyId] IS NULL THEN 0 ELSE 1 END [DupeCheck]
     , cs.PrioritizeMatchingIndividually
     , ROW_NUMBER() OVER (ORDER BY COALESCE(pa.Code, cs.Code))    [BillingDisplayOrder]
     , ROW_NUMBER() OVER (ORDER BY {receiptDisplayOrder} )        [ReceiptDisplayOrder]
 FROM (
      SELECT wc.CurrencyId
           , wc.ParentCustomerId
           , wc.PaymentAgencyId
           , SUM(DISTINCT BillingCount) [BillingCount]
           , SUM(DISTINCT CASE @AmountType WHEN 0 THEN BillingAmount ELSE BillingRemainAmount END) [BillingAmount]
           , SUM(ReceiptCount) [ReceiptCount]
           , SUM(ReceiptRemainAmount) [ReceiptAmount]
           , CASE SUM(AdvanceReceivedCount) WHEN 0 THEN 0 WHEN SUM(ReceiptCount) THEN 2 ELSE 1 END [AdvanceReceivedCount]
           , CASE MIN(COALESCE(wc.PayerName, '')) WHEN '' THEN NULL ELSE MIN(COALESCE(wc.PayerName, '')) END [PayerName]
           , MAX(wc.ForceMatchingIndividually) [ForceMatchingIndividually]
           , MIN(wc.MinReceiptRecordedAt) [MinReceiptRecordedAt]
           , MAX(wc.MaxReceiptRecordedAt) [MaxReceiptRecordedAt]
           , MIN(wc.MinReceiptId) [MinReceiptId]
           , MAX(wc.MaxReceiptId) [MaxReceiptId]
        FROM WorkCollation wc
       WHERE wc.ClientKey = @ClientKey
         AND wc.CompanyId = @CompanyId
       GROUP BY
             wc.CurrencyId
           , wc.ParentCustomerId
           , wc.PaymentAgencyId
           , CASE WHEN wc.ParentCustomerId = 0 AND wc.PaymentAgencyId = 0 THEN wc.PayerName END
       ) wc
  LEFT JOIN Customer cs      ON cs.Id         = wc.ParentCustomerId
  LEFT JOIN PaymentAgency pa ON pa.Id         = wc.PaymentAgencyId
  LEFT JOIN Currency cy      ON cy.Id         = wc.CurrencyId
  LEFT JOIN (
       SELECT DISTINCT
              wc.CurrencyId
            , wc.ParentCustomerId
            , wc.PaymentAgencyId
         FROM (
              SELECT wc.PayerName
                   , wc.PayerCode
                   , wc.BankCode
                   , wc.BranchCode
                   , wc.SourceBankName
                   , wc.SourceBranchName
                   , wc.CustomerId
                   , wc.CollationKey
                   , wc.CurrencyId
                FROM WorkCollation wc
               WHERE wc.ClientKey         = @ClientKey
                 AND wc.CompanyId         = @CompanyId
                 AND wc.CurrencyId        > 0
                 AND wc.ParentCustomerId >= 0
                 AND wc.PaymentAgencyId  >= 0
                 AND wc.CollationType     > 0
               GROUP BY
                     wc.PayerName
                   , wc.PayerCode
                   , wc.BankCode
                   , wc.BranchCode
                   , wc.SourceBankName
                   , wc.SourceBranchName
                   , wc.CustomerId
                   , wc.CollationKey
                   , wc.CurrencyId
              HAVING COUNT(1) > 1
              ) dupe
        INNER JOIN WorkCollation wc
           ON wc.PayerName         = dupe.PayerName
          AND wc.PayerCode         = dupe.PayerCode
          AND wc.BankCode          = dupe.BankCode
          ANd wc.BranchCode        = dupe.BranchCode
          AND wc.SourceBankName    = dupe.SourceBankName
          AND wc.SourceBranchName  = dupe.SourceBranchName
          AND wc.CustomerId        = dupe.CustomerId
          AND wc.CollationKey      = dupe.CollationKey
          AND wc.CurrencyId        = dupe.CurrencyId
          AND wc.ClientKey         = @ClientKey
          AND wc.CompanyId         = @CompanyId
          AND wc.CurrencyId        > 0
          AND wc.ParentCustomerId >= 0
          AND wc.PaymentAgencyId  >= 0
          AND wc.CollationType     > 0
       ) dupe
    ON wc.CurrencyId        = dupe.CurrencyId
   AND wc.ParentCustomerId  = dupe.ParentCustomerId
   AND wc.PaymentAgencyId   = dupe.PaymentAgencyId
";

            return(dbHelper.GetItemsAsync <Collation>(query, option, token));
        }
Exemple #15
0
        public void SetPageDataSetting(List <Collation> collationList, bool timeSort, int useForeignCurrency, int Precision, CollationSearch cs)
        {
            this.useForeignCurrency = useForeignCurrency;

            if (useForeignCurrency == 1)
            {
                SetDisplayFormat(Precision);
            }

            DataSource                 = new BindingSource(collationList.ToArray(), null);
            txtChecked.DataField       = "Checked";
            txtCurrency.DataField      = "CurrencyCode";
            txtCustomerCode.DataField  = "DispCustomerCode";
            txtCustomerName.DataField  = "DispCustomerName";
            txtBillingCount.DataField  = "DispBillingCount";
            txtBillingAmount.DataField = "DispBillingAmount";
            txtPayerName.DataField     = "PayerName";
            txtReceiptCount.DataField  = "DispReceiptCount";
            txtReceiptAmount.DataField = "DispReceiptAmount";
            txtFee.DataField           = "ReportDispShareTransferFee";
            txtDifference.DataField    = "DispDifferent";
        }
        public async Task <MatchingResult> MatchAsync(
            IEnumerable <Collation> collations,
            CollationSearch option,
            CancellationToken token    = default(CancellationToken),
            IProgressNotifier notifier = null)
        {
            var appControl = await applicationControlQueryProcessor.GetAsync(option.CompanyId, token);

            var nettingReceipts = new List <Receipt>();

            var updateAt = await dbSystemDateTimeQueryProcessor.GetAsync(token);

            var matchingOrders = await matchingOrderQueryProcessor.GetItemsAsync(option.CompanyId, token);

            var matchingBillingOrder = matchingOrders.Where(x => x.TransactionCategory == 1 && x.Available == 1).ToArray();
            var matchingReceiptOrder = matchingOrders.Where(x => x.TransactionCategory == 2 && x.Available == 1).ToArray();

            var            matchings        = new List <Matching>();
            var            advanceReceiveds = new List <AdvanceReceived>();
            List <Billing> billings         = null;
            List <Netting> nettings         = null;
            List <Receipt> receipts         = null;
            var            index            = 0;

            using (var scope = transactionScopeBuilder.Create())
            {
                foreach (var collation in collations)
                {
                    // 請求データ取得
                    var billingSearchOption = new MatchingBillingSearch
                    {
                        ClientKey        = option.ClientKey,
                        ParentCustomerId = collation.CustomerId,
                        PaymentAgencyId  = collation.PaymentAgencyId,
                        CurrencyId       = collation.CurrencyId,
                    };

                    billings = (await matchingQueryProcessor.GetBillingsForSequentialMatchingAsync(billingSearchOption, matchingBillingOrder, token)).ToList();
                    var billingAmount = billings.Sum(item => (item.RemainAmount - item.DiscountAmount - item.OffsetAmount));
                    var billingCount  = billings.Count;

                    // 請求データチェック
                    if (billingAmount != collation.BillingAmount ||
                        billingCount != collation.BillingCount)
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.BillingRemainChanged,
                            ErrorIndex = index,
                        });
                    }

                    // 相殺データ取得
                    nettings = (await matchingQueryProcessor.SearchMatchingNettingAsync(option, collation, token)).ToList();

                    // 相殺データ登録
                    var createdReceipstFromNetting = new List <Receipt>();
                    foreach (var netting in nettings)
                    {
                        var receipt        = netting.ConvertToReceiptInput(option.LoginUserId, updateAt);
                        var nettingReceipt = await matchingQueryProcessor.SaveMatchingReceiptAsync(receipt, token);

                        if (!string.IsNullOrEmpty(netting.ReceiptMemo))
                        {
                            await addReceiptMemoQueryProcessor.SaveAsync(nettingReceipt.Id, netting.ReceiptMemo, token);
                        }
                        netting.ReceiptId = nettingReceipt.Id;
                        createdReceipstFromNetting.Add(nettingReceipt);
                    }

                    // 入金データ取得
                    var receiptSearch = new MatchingReceiptSearch
                    {
                        ClientKey           = option.ClientKey,
                        CompanyId           = option.CompanyId,
                        CurrencyId          = collation.CurrencyId,
                        ParentCustomerId    = collation.CustomerId,
                        PaymentAgencyId     = collation.PaymentAgencyId,
                        UseScheduledPayment = appControl.UseScheduledPayment,
                    };

                    receipts = (await matchingQueryProcessor.GetReceiptsForSequentialMatchingAsync(receiptSearch, matchingReceiptOrder, token)).ToList();

                    var receiptAmount      = receipts.Sum(item => (item.RemainAmount));
                    var receiptCount       = receipts.Count();
                    var hasAdvanceReceived = receipts.Exists(item => item.UseAdvanceReceived == 1);

                    // 入金データチェック
                    if (receiptAmount != collation.ReceiptAmount ||
                        receiptCount != collation.ReceiptCount)
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.ReceiptRemainChanged,
                            ErrorIndex = index,
                        });
                    }

                    foreach (var netting in nettings)
                    {
                        await updateNettingQueryProcessor.UpdateMatchingNettingAsync(netting.CompanyId, netting.ReceiptId.Value, netting.Id, CancelFlg : 0, token : token);

                        foreach (var receipt in receipts
                                 .Where(x => x.NettingId.HasValue && x.NettingId == netting.Id))
                        {
                            receipt.Id = netting.ReceiptId.Value;
                        }
                    }

                    // 前受処理日付取得
                    var recordedAt = option.GetRecordedAt(hasAdvanceReceived ? billings : null);
                    option.AdvanceReceivedRecordedAt = recordedAt;

                    var requestSource = new MatchingSource
                    {
                        Billings        = billings,
                        Receipts        = receipts,
                        BankTransferFee = collation.BankTransferFee,
                        TaxDifference   = collation.TaxDifference,
                    };
                    var source = await matchingSolveProcessor.SolveAsync(requestSource, option, appControl, token);

                    foreach (var netting in nettings)
                    {
                        foreach (var matching in source.Matchings.Where(x => x.IsNetting && x.ReceiptId == netting.Id))
                        {
                            matching.ReceiptId = netting.ReceiptId.Value;
                        }
                    }

                    if (collation.UseFeeLearning == 1 && collation.BankTransferFee != 0M)
                    {
                        await SaveBankTransferFeeAsync(collation, option, updateAt, token);
                    }

                    // データの更新処理
                    int?customerId = collation.CustomerId;
                    if (customerId == 0)
                    {
                        customerId = null;
                    }

                    int?paymentAgencyId = collation.PaymentAgencyId;
                    if (paymentAgencyId == 0)
                    {
                        paymentAgencyId = null;
                    }

                    source.LoginUserId               = option.LoginUserId;
                    source.UpdateAt                  = updateAt;
                    source.MatchingProcessType       = 0;
                    source.CustomerId                = customerId;
                    source.PaymentAgencyId           = paymentAgencyId;
                    source.AdvanceReceivedCustomerId = option.DoTransferAdvanceReceived ? customerId : null;
                    source.ClientKey                 = option.ClientKey;

                    foreach (var r in createdReceipstFromNetting)
                    {
                        var item = receipts.First(x => x.Id == r.Id);
                        item.UpdateAt = r.UpdateAt;
                    }

                    var matchingResult = await matchingSaveProcessor.SaveAsync(source, appControl, token);

                    if (!matchingResult.ProcessResult.Result)
                    {
                        notifier?.Abort();
                        return(matchingResult);
                    }
                    matchings.AddRange(matchingResult.Matchings);
                    advanceReceiveds.AddRange(matchingResult.AdvanceReceiveds);
                    nettingReceipts.AddRange(createdReceipstFromNetting);

                    notifier?.UpdateState();
                    index++;
                }

                scope.Complete();
            }


            return(new MatchingResult
            {
                ProcessResult = new ProcessResult {
                    Result = true
                },
                Matchings = matchings,
                AdvanceReceiveds = advanceReceiveds,
                NettingReceipts = nettingReceipts
            });
        }
        public async Task <MatchingSource> SolveAsync(
            MatchingSource source,
            CollationSearch option,
            ApplicationControl control = null,
            CancellationToken token    = default(CancellationToken))
        {
            if (control == null)
            {
                control = await applicationControlQueryProcessor.GetAsync(option.CompanyId, token);
            }

            var useCashOnDueDates       = control.UseCashOnDueDates;
            var useScheduledPayment     = control.UseScheduledPayment;
            var useDeclaredAmount       = control.UseDeclaredAmount;
            int?doCreateAdvanceReceived = option.DoTransferAdvanceReceived ? 1 : 0;
            var taxDifference           = source.TaxDifference;
            var bankTransferFee         = source.BankTransferFee;


            var billingItems = source.Billings;
            var receiptItems = source.Receipts;

            if (!(billingItems?.Any() ?? false))
            {
                throw new ArgumentNullException("Billings");
            }

            if (!(receiptItems?.Any() ?? false))
            {
                throw new ArgumentNullException("Receipts");
            }

            int companyId;
            int currencyId;
            {
                var billing = billingItems.First();
                companyId  = billing.CompanyId;
                currencyId = billing.CurrencyId;
            }

            var isAllMinusBilling = true;
            var billingTotal      = 0M;

            var billingPair = GetBillingPair(billingItems, useScheduledPayment, useDeclaredAmount,
                                             ref doCreateAdvanceReceived, ref billingTotal, ref isAllMinusBilling);

            var isAllMinusReceipt = true;
            var receiptTotal      = 0M;
            var receiptPair       = GetReceiptPair(receiptItems, ref receiptTotal, ref isAllMinusReceipt);

            var isAllMinus = isAllMinusBilling && isAllMinusReceipt;
            var taxDiff    = taxDifference;
            var bankFee    = bankTransferFee;
            var isEqual    = (billingTotal == receiptTotal + bankFee - taxDiff);
            Func <decimal, decimal, decimal> amountSolver = Math.Min;

            if (isAllMinus)
            {
                amountSolver = Math.Max;
            }
            var header = new MatchingHeader
            {
                Id              = 1,
                CompanyId       = companyId,
                CurrencyId      = currencyId,
                BankTransferFee = bankFee,
                TaxDifference   = taxDiff,
            };

            var recordedAt = option.AdvanceReceivedRecordedAt;

            var matchings = new List <Matching>();
            var billingScheduledIncomes  = new List <BillingScheduledIncome>();
            var matchingBillingDiscounts = new List <MatchingBillingDiscount>();
            var billingDiscounts         = new HashSet <long>();
            var discountTotal            = 0M;
            var beforBillingRemainTotal  = 0M;
            var beforReceiptRemainTotal  = 0M;

            var     bindex      = 0;
            var     rindex      = 0;
            var     nextBilling = true;
            var     nextReceipt = true;
            Billing bill        = null;
            Receipt rcpt        = null;

            while (bindex < billingItems.Count &&
                   rindex < receiptItems.Count &&
                   (nextBilling || nextReceipt))
            {
                if (token.IsCancellationRequested)
                {
                    throw new OperationCanceledException();
                }

                if (nextBilling)
                {
                    bill = billingItems[bindex];
                }
                if (nextReceipt)
                {
                    rcpt = receiptItems[rindex];
                }
                var isLastBill = bindex == billingItems.Count - 1;
                var isLastRcpt = rindex == receiptItems.Count - 1;

                var discount    = bill.DiscountAmount;
                var billTaxDiff = (0M < taxDiff) ? taxDiff : 0M;
                var rcptTaxDiff = (0M < taxDiff) ? 0M : -taxDiff;

                var matching = new Matching();
                matching.Id               = matchings.Count;
                matching.CompanyId        = companyId;
                matching.CurrencyId       = currencyId;
                matching.MatchingHeaderId = header.Id;
                matching.ReceiptId        = rcpt.NettingId ?? rcpt.Id;
                matching.PayerName        = rcpt.PayerName;
                matching.SourceBankName   = rcpt.SourceBankName;
                matching.SourceBranchName = rcpt.SourceBranchName;
                matching.IsNetting        = rcpt.NettingId.HasValue;
                matching.RecordedAt       = (rcpt.OriginalReceiptId.HasValue ? recordedAt : null) ?? rcpt.RecordedAt;
                matching.ReceiptHeaderId  = rcpt.ReceiptHeaderId;
                if (useCashOnDueDates == 1 && rcpt.UseCashOnDueDates == 1)
                {
                    matching.UseCashOnDueDates = 1;
                }

                matching.BillingId = bill.Id;
                if (useScheduledPayment == 1 && useDeclaredAmount == 1)
                {
                    matching.OffsetAmount = bill.OffsetAmount;
                }

                if (discount != 0M)
                {
                    #region billing discount
                    matching.DiscountAmount1 = bill.DiscountAmount1;
                    matching.DiscountAmount2 = bill.DiscountAmount2;
                    matching.DiscountAmount3 = bill.DiscountAmount3;
                    matching.DiscountAmount4 = bill.DiscountAmount4;
                    matching.DiscountAmount5 = bill.DiscountAmount5;
                    if (!billingDiscounts.Contains(bill.Id))
                    {
                        billingDiscounts.Add(bill.Id);
                    }
                    matchingBillingDiscounts.AddRange(ConvertBillingToDiscounts(bill, matching.Id));
                    discountTotal       += discount;
                    bill.DiscountAmount  = 0M;
                    bill.DiscountAmount1 = 0M;
                    bill.DiscountAmount2 = 0M;
                    bill.DiscountAmount3 = 0M;
                    bill.DiscountAmount4 = 0M;
                    bill.DiscountAmount5 = 0M;
                    #endregion
                }
                matching.BankTransferFee = bankFee;
                matching.TaxDifference   = taxDiff;
                matching.Memo            = bill.Memo;
                var billAmount = billingPair[bindex] - rcptTaxDiff - bankFee - discount;
                var rcptAmount = receiptPair[rindex] - billTaxDiff;

                matching.Amount
                    = (isEqual && isLastRcpt && !isLastBill) ? billAmount
                    : (isEqual && !isLastRcpt && isLastBill) ? rcptAmount
                    : amountSolver(billAmount, rcptAmount);

                header.Amount += matching.Amount;

                if (matching.Amount == 0M && taxDiff == 0M && bankFee == 0M && discount == 0M)
                {
                    break;
                }

                var billAssignAmount = (matching.Amount + rcptTaxDiff + bankFee + discount);
                var rcptAssignAmount = (matching.Amount + billTaxDiff);
                if (nextBilling)
                {
                    beforBillingRemainTotal += bill.RemainAmount;
                    bill.AssignmentAmount    = billAssignAmount;
                }
                else
                {
                    bill.AssignmentAmount += billAssignAmount;
                }

                if (nextReceipt)
                {
                    beforReceiptRemainTotal += rcpt.RemainAmount;
                    rcpt.AssignmentAmount    = rcptAssignAmount;
                }
                else
                {
                    rcpt.AssignmentAmount += rcptAssignAmount;
                }

                billingPair[bindex] -= billAssignAmount;
                receiptPair[rindex] -= rcptAssignAmount;

                nextBilling = billingPair[bindex] == 0M && !isLastBill;
                nextReceipt = receiptPair[rindex] == 0M && !isLastRcpt;

                matchings.Add(matching);


                if (!isEqual)
                {
                    if (!nextBilling && isLastBill && receiptPair[rindex] > 0M && !rcpt.OriginalReceiptId.HasValue)
                    {
                        matching.AdvanceReceivedOccured = 1;
                    }
                    if (!nextBilling && isLastBill && receiptPair[rindex] != 0M)
                    {
                        foreach (var m in matchings.Where(x => x.ReceiptId == rcpt.Id))
                        {
                            m.ReceiptRemain = receiptPair[rindex];
                        }
                    }
                    if (!nextReceipt && isLastRcpt && billingPair[bindex] != 0M)
                    {
                        foreach (var m in matchings.Where(x => x.BillingId == bill.Id))
                        {
                            m.BillingRemain = billingPair[bindex];
                        }
                    }
                    if (billingPair[bindex] == 0M && isLastBill ||
                        receiptPair[rindex] == 0M && isLastRcpt)
                    {
                        break;
                    }
                }

                if (nextBilling)
                {
                    bindex++;
                }
                if (nextReceipt)
                {
                    rindex++;
                }

                taxDiff = 0M;
                bankFee = 0M;
            }

            var remainType = 0;

            var billingRemainTotal = billingPair.Sum(x => x.Value);
            var receiptRemainTotal = receiptPair.Sum(x => x.Value);
            if (billingRemainTotal == 0M && receiptRemainTotal == 0M)
            {
                remainType = 0;
            }
            else if (billingRemainTotal != 0M)
            {
                remainType = 1;
            }
            else
            {
                if (!rcpt.OriginalReceiptId.HasValue &&
                    option.UseAdvanceReceived &&
                    receiptPair[rindex] > 0M)
                {
                    remainType = 3;
                }
                else
                {
                    remainType = 2;
                }
            }

            if (useCashOnDueDates == 1)
            {
                billingScheduledIncomes.AddRange(ConvertMatchingToScheduled(matchings));
            }


            billingItems        = billingItems.Take(bindex + 1).ToList();
            receiptItems        = receiptItems.Take(rindex + 1).ToList();
            header.BillingCount = billingItems.Count;
            header.ReceiptCount = receiptItems.Count;

            if (token.IsCancellationRequested)
            {
                throw new OperationCanceledException();
            }

            return(new MatchingSource
            {
                RemainType = remainType,
                Matchings = matchings,
                Billings = billingItems,
                Receipts = receiptItems,
                BillingDiscounts = billingDiscounts,
                MatchingBillingDiscounts = matchingBillingDiscounts,
                MatchingHeader = header,
                BillingScheduledIncomes = billingScheduledIncomes,
                BillingRemainTotal = beforBillingRemainTotal,
                ReceiptRemainTotal = beforReceiptRemainTotal,
                BillingDiscountTotal = discountTotal
            });
        }