Esempio n. 1
0
        public async Task <MatchingResult> CancelAsync(IEnumerable <MatchingHeader> headers, int loginUserId,
                                                       CancellationToken token    = default(CancellationToken),
                                                       IProgressNotifier notifier = null)
        {
            var headersArray = headers.ToArray();

            var updateAt = await dbSystemDatetimeQueryProcessor.GetAsync(token);

            List <Matching> details  = null;
            List <Billing>  billings = null;
            List <Receipt>  receipts = null;

            var deleteMatchings = new List <Matching>();
            var deleteReceipts  = new List <Receipt>();

            var companyId  = headers.Select(x => x.CompanyId).First();
            var appControl = await applicationControlGetByCompanyIdQueryProcessor.GetAsync(companyId, token);

            var useCashOnDueDates = appControl.UseCashOnDueDates == 1;

            using (var scope = transactionScopeBuilder.Create())
            {
                foreach (var header in headers)
                {
                    if (token.IsCancellationRequested)
                    {
                        return new MatchingResult {
                                   MatchingErrorType = MatchingErrorType.ProcessCanceled
                        }
                    }
                    ;

                    #region 1.消込データ(Matching)取得

                    details = (await cancelMatchingQueryProcessor.GetByHeaderIdAsync(header.Id, token)).ToList();

                    deleteMatchings.AddRange(details);

                    #endregion

                    #region 2.消込済請求データ取得
                    // billing deleteAt など matching 以外の情報が必要なので、 query で呼び出し必須
                    billings = (await cancelMatchingQueryProcessor.GetMatchedBillingsForCancelAsync(header.Id, token)).ToList();

                    #endregion

                    if (billings.Count == 0)
                    {
                        notifier?.Abort();

                        return(new MatchingResult {
                            MatchingErrorType = MatchingErrorType.NotExistBillingData
                        });
                    }

                    //3.請求データチェック

                    if (billings.Any(e => e.DeleteAt.HasValue))
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.BillingOmitted,
                            ErrorIndex = Array.IndexOf(headersArray, header),
                        });
                    }

                    if (useCashOnDueDates &&
                        await cancelMatchingQueryProcessor.ExistAssignmentScheduledIncomeAsync(details.Select(x => x.Id).ToArray(), token))
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.CashOnDueDateOmitted,
                            ErrorIndex = Array.IndexOf(headersArray, header),
                        });
                    }

                    #region 4.請求データ消込解除処理

                    // 期日現金管理用 請求ID -> 消込ID の Dictionary
                    Dictionary <long, long[]> billingIdToMatchingIds = null;
                    if (useCashOnDueDates)
                    {
                        billingIdToMatchingIds = details.GroupBy(x => x.BillingId)
                                                 .ToDictionary(x => x.Key, x => x.Select(y => y.Id).ToArray());
                    }

                    foreach (var billing in billings)
                    {
                        var item           = billing;
                        var matchingAmount = billing.AssignmentAmount;
                        var bankFee        = billing.BankTransferFee;
                        var taxDiff        = billing.TaxDifference;
                        var discount       = billing.DiscountAmount;
                        var amount         = matchingAmount + bankFee + discount - (taxDiff < 0 ? taxDiff : 0M);

                        item.CompanyId        = header.CompanyId;
                        item.RemainAmount     = amount;
                        item.AssignmentAmount = amount;
                        item.UpdateBy         = loginUserId;
                        item.UpdateAt         = updateAt;

                        var updatedBilling = await cancelMatchingQueryProcessor.UpdateBillingForCancelMatchingAsync(item, token);

                        if (updatedBilling == null)
                        {
                            notifier?.Abort();
                            return(new MatchingResult {
                                MatchingErrorType = MatchingErrorType.CancelError
                            });
                        }

                        await cancelMatchingQueryProcessor.UpdatePreviousBillingLogsAsync(header.Id, billing.Id, amount, loginUserId, updateAt, token);

                        if (useCashOnDueDates)
                        {
                            foreach (var matchingId in billingIdToMatchingIds[billing.Id])
                            {
                                var income = await cancelMatchingQueryProcessor.GetBillingScheduledIncomeAsync(matchingId, token);

                                if (income != null)
                                {
                                    await cancelMatchingQueryProcessor.DeleteBillingShceduledIncomeAsync(income.BillingId, token);

                                    await deleteBillingByIdQueryProcessor.DeleteAsync(income.BillingId, token);
                                }
                            }
                        }
                    }

                    #endregion

                    #region 5.相殺データ消込解除処理

                    var nettings          = (await nettingQueryProcessor.GetByMatchingHeaderIdAsync(header.Id, token)).ToList();
                    var nettingReceiptIds = new List <long>();
                    foreach (var netting in nettings)
                    {
                        nettingReceiptIds.Add((long)netting.ReceiptId);
                        var cancelFlag = 1;
                        await updateNettingQueryProcessor.UpdateMatchingNettingAsync(header.CompanyId, 0, netting.Id, cancelFlag, token);
                    }

                    if (nettings.Any())
                    {
                        var receiptsForNetting = await receiptGetByIdsQueryProcessor.GetByIdsAsync(nettings.Select(n => (long)n.ReceiptId), token);

                        deleteReceipts.AddRange(receiptsForNetting);
                    }
                    #endregion

                    #region 7.消込済入金データ取得

                    receipts = (await cancelMatchingQueryProcessor.GetMatchedReceiptsForCancelAsync(header, token)).ToList();

                    #endregion

                    if (receipts.Count == 0)
                    {
                        notifier?.Abort();
                        return(new MatchingResult {
                            MatchingErrorType = MatchingErrorType.NotExistReceiptData
                        });
                    }

                    #region 8.入金データチェック

                    if (receipts.Any(e => e.DeleteAt.HasValue))
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.ReceiptOmitted,
                            ErrorIndex = Array.IndexOf(headersArray, header),
                        });
                    }

                    #endregion

                    #region 9.入金データ消込解除処理

                    var hasAdvanceReceivedOccured = details.Any(x => (x.AdvanceReceivedOccured == 1));
                    foreach (var receipt in receipts)
                    {
                        var prepare_receipt_update = receipt;

                        var receiptId = receipt.Id;

                        var amount = prepare_receipt_update.AssignmentAmount;

                        prepare_receipt_update.RemainAmount     = amount;
                        prepare_receipt_update.AssignmentAmount = amount;
                        prepare_receipt_update.CompanyId        = header.CompanyId;
                        prepare_receipt_update.UpdateBy         = loginUserId;
                        prepare_receipt_update.UpdateAt         = updateAt;

                        await cancelMatchingQueryProcessor.UpdateReceiptForCancelMatchingAsync(prepare_receipt_update, token);

                        // 入金残ログ洗替え
                        await cancelMatchingQueryProcessor.UpdatePreviousReceiptLogsAsync(header.Id, receiptId, amount, loginUserId, updateAt, token);

                        if (!hasAdvanceReceivedOccured)
                        {
                            continue;
                        }
                        //前受データの削除
                        var maeuke_receipt_flg = details.Exists(x => ((x.AdvanceReceivedOccured == 1) && (x.ReceiptId == receipt.Id)));

                        if (!maeuke_receipt_flg)
                        {
                            continue;
                        }
                        var originalReceiptId = receipt.Id;

                        //前受のテータ取得
                        var maeuke_Receipts = (await cancelMatchingQueryProcessor.GetByOriginalIdAsync(originalReceiptId)).ToList();

                        foreach (var maeuke_receipt in maeuke_Receipts)
                        {
                            int canel_flg = await deleteReceiptQueryProcessor.CancelAdvanceReceivedAsync(maeuke_receipt.Id, token);

                            if (canel_flg != 1)
                            {
                                continue;
                            }
                            // データ同期用
                            deleteReceipts.Add(maeuke_receipt);

                            var originalReceipt = receipts.Find(x => (x.Id == originalReceiptId));
                            originalReceipt.RemainAmount     = maeuke_receipt.ReceiptAmount;
                            originalReceipt.AssignmentAmount = 0; //消込額から減算は不要なので
                            originalReceipt.UpdateBy         = loginUserId;
                            originalReceipt.UpdateAt         = updateAt;

                            await cancelMatchingQueryProcessor.UpdateReceiptForCancelMatchingAsync(originalReceipt, token);
                        }
                    }
                    #endregion

                    #region 8.その他処理

                    #region 消込履歴データ検索・出力済データ(MatchingOutputed)の削除

                    await cancelMatchingQueryProcessor.DeleteMatchingOutputedAsync(header.Id, token);

                    #endregion

                    #region 消込歩引データ(MatchingBillingDiscount)の削除

                    foreach (var matching in details)
                    {
                        await deleteMatchingBillingDiscountQueryProcessor.DeleteByMatchingIdAsync(matching.Id, token);
                    }

                    #endregion

                    #region 消込データ(Matching)の削除

                    var deleteMatchingResult = await cancelMatchingQueryProcessor.DeleteMatchingAsync(header.Id, header.MatchingUpdateAt, token);

                    if (deleteMatchingResult <= 0)
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.MatchingHeaderChanged,
                            ErrorIndex = Array.IndexOf(headersArray, header),
                        });
                    }

                    #endregion

                    #region MatchingHeaderの削除

                    var deleteResult = await deleteMatchingHeaderQueryProcessor.DeleteAsync(header, token);

                    if (deleteResult <= 0)
                    {
                        notifier?.Abort();
                        return(new MatchingResult
                        {
                            MatchingErrorType = MatchingErrorType.MatchingHeaderChanged,
                            ErrorIndex = Array.IndexOf(headersArray, header),
                        });
                    }

                    #endregion

                    #region 相殺データから変換した入金データの削除
                    foreach (long receiptId in nettingReceiptIds)
                    {
                        await deleteReceiptByIdQueryProcessor.DeleteAsync(receiptId, token);
                    }
                    #endregion

                    #endregion

                    notifier?.UpdateState();
                }

                scope.Complete();

                return(new MatchingResult
                {
                    ProcessResult = new ProcessResult {
                        Result = true
                    },
                    Matchings = deleteMatchings,
                    DeleteReceipts = deleteReceipts,
                });
            }
        }
    }
        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
            });
        }