/// <summary> /// Попытаться оплатить накладную реализации из аванса (т.е. неразнесенных остатков платежных документов по сделке и команде). /// После разнесения может оставаться неоплаченный остаток у накладной или неразнесенные остатки платежных документов. /// Если накладная не имеет положительного остатка, расшифровки распределения оплаты не будут созданы. /// При полной оплате накладной реализации происходит установка признака того, что накладная полностью оплачена /// </summary> /// <param name="saleWaybill">Накладная реализации, на которую выполняется разнесение платежных документов</param> public void PaySaleWaybillOnAccept(SaleWaybill saleWaybill, DateTime currentDate) { // если сумма в ОЦ по накладной реализации = 0, выставляем признак полной оплаты и выходим if (saleWaybill.Is <ExpenditureWaybill>() && saleWaybill.As <ExpenditureWaybill>().SalePriceSum == 0) { saleWaybill.IsFullyPaid = true; return; } // При проводке данной накладной неоплаченный остаток равен сумме накладной в ОЦ var debtRemainder = saleWaybill.As <ExpenditureWaybill>().SalePriceSum; // получение информации о неразнесенных частях платежных документов var dealPaymentUndistributedPartsInfo = GetTotalDealPaymentUndistributedPartsInfo(saleWaybill.Deal, saleWaybill.Team); // разносим неоплаченные остатки на накладную PaySaleWaybill(dealPaymentUndistributedPartsInfo, saleWaybill, debtRemainder, debtRemainder, currentDate); }
/// <summary> /// Общая сумма всех возвратов по накладной реализации /// </summary> /// <param name="saleWaybill">Накладная реализации</param> /// <returns>Сумма</returns> public decimal GetTotalReservedByReturnSumForSaleWaybill(SaleWaybill saleWaybill) { if (saleWaybill.Is <ExpenditureWaybill>()) { ExpenditureWaybill expenditureWaybill = saleWaybill.As <ExpenditureWaybill>(); return(expenditureWaybillIndicatorService.GetTotalReservedByReturnSumForSaleWaybill(expenditureWaybill)); } throw new Exception("Неизвестный тип накладной реализации."); }
/// <summary> /// Расчет неоплаченного остатка по накладной реализации /// </summary> /// <param name="saleWaybill">Накладная реализации</param> /// <returns>Неоплаченный остаток</returns> public decimal CalculateDebtRemainder(SaleWaybill saleWaybill) { if (saleWaybill.Is <ExpenditureWaybill>()) { ExpenditureWaybill expenditureWaybill = saleWaybill.As <ExpenditureWaybill>(); return(expenditureWaybillIndicatorService.CalculateDebtRemainder(expenditureWaybill)); } throw new Exception("Неизвестный тип накладной реализации."); }
/// <summary> /// Расчет суммы накладной реализации с учетом всех сделанных по ней возвратов /// </summary> /// <param name="sale"></param> /// <returns></returns> public decimal CalculateSaleWaybillCostWithReturns(SaleWaybill sale, ReturnFromClientWaybill returnFromClientWaybillToExclude = null) { // Сумма всех возвратов по накладной реализации var returnedRow = returnFromClientWaybillRepository.Query <ReturnFromClientWaybillRow>(); // TODO рефакторить, ну кто так через OneOf пишет? Надо выбрать в RetWaybillRow SaleWaybillRow, а в нем - SaleWaybill.Id !!! - Олег returnedRow.OneOf(x => x.SaleWaybillRow.Id, sale.Rows.Select(x => x.Id)) .Restriction <ReturnFromClientWaybill>(x => x.ReturnFromClientWaybill) .Where(x => x.State == ReturnFromClientWaybillState.Receipted); if (returnFromClientWaybillToExclude != null) { returnedRow.Where(x => x.ReturnFromClientWaybill.Id != returnFromClientWaybillToExclude.Id); } var returnedSum = returnedRow.ToList <ReturnFromClientWaybillRow>() .Sum(x => x.SalePrice.Value * x.ReturnCount); // Отпускная цена уже должна быть установлена (иметь Value) // Вычисляем сумму накладной реализации decimal saleSum = sale.As <ExpenditureWaybill>().SalePriceSum; return(saleSum - returnedSum); }
/// <summary> /// Разнести неразнесенные части платежных документов на накладную реализации. /// После разнесения у накладной реализации может оставаться неоплаченный остаток. /// Если до разнесения накладная реализации не имеет положительного неоплаченного остатка, разнесение платежного документа не будет создано. /// При полной оплате накладной реализации происходит установка признака того, что накладная реализации полностью оплачена. /// </summary> /// <param name="dealPaymentUndistributedPartsInfo">Список неразнесенных частей платежных документов</param> /// <param name="saleWaybill">Накладная реализации (единственная), на которую выполняется разнесение платежных документов</param> /// <param name="debtRemainder">Текущий неоплаченный остаток по накладной</param> /// <param name="sumToDistribute">Сумма к разнесению</param> /// <param name="currentDate">Дата операции</param> private void PaySaleWaybill(List <DealPaymentUndistributedPartInfo> dealPaymentUndistributedPartsInfo, SaleWaybill saleWaybill, decimal debtRemainder, decimal sumToDistribute, DateTime currentDate) { // разносим неразнесенные части оплат foreach (var distributionPartInfo in dealPaymentUndistributedPartsInfo.OrderBy(x => x.AppearenceDate)) { var dealPaymentDocument = distributionPartInfo.DealPaymentDocument; // cумма создаваемого разнесения платежного документа decimal sum = Math.Min(distributionPartInfo.Sum, sumToDistribute); // вычитаем разносимую сумму distributionPartInfo.Sum -= sum; sumToDistribute -= sum; // дата создаваемого разнесения var distributionDate = DateTimeUtils.GetMaxDate(distributionPartInfo.AppearenceDate, saleWaybill.AcceptanceDate.Value); ValidationUtils.Assert(dealPaymentDocument.Is <DealPaymentFromClient>() || dealPaymentDocument.Is <DealCreditInitialBalanceCorrection>(), "Платежный документ имеет недопустимый тип."); ValidationUtils.Assert(sum > 0, "Сумма для разнесения должна быть положительной."); if (saleWaybill.Is <ExpenditureWaybill>()) // Если появятся еще типы накладных реализации, вместо "накладная реализации товаров" выводить их названия { ExpenditureWaybill expenditureWaybill = saleWaybill.As <ExpenditureWaybill>(); ValidationUtils.Assert(expenditureWaybill.IsAccepted, String.Format("Невозможно разнести платежный документ на накладную реализации товаров {0} со статусом «{1}».", expenditureWaybill.Name, expenditureWaybill.State.GetDisplayName())); ValidationUtils.Assert(sum <= debtRemainder, String.Format("Сумма для разнесения ({0} р.) превышает неоплаченный остаток накладной реализации товаров {1} ({2} р.)", sum.ForDisplay(ValueDisplayType.Money), expenditureWaybill.Name, debtRemainder.ForDisplay(ValueDisplayType.Money))); ValidationUtils.Assert(dealPaymentDocument.Team == saleWaybill.Team, String.Format("Невозможно оплатить накладную реализации «{0}», т.к. она относится к другой команде.", saleWaybill.Name)); // формируем разнесение var dealPaymentDocumentDistributionToSaleWaybill = new DealPaymentDocumentDistributionToSaleWaybill(dealPaymentDocument, expenditureWaybill, sum, distributionDate, currentDate) { SourceDistributionToReturnFromClientWaybill = distributionPartInfo.DealPaymentDocumentDistributionToReturnFromClientWaybill }; dealPaymentDocument.AddDealPaymentDocumentDistribution(dealPaymentDocumentDistributionToSaleWaybill); debtRemainder -= sum; // Если накладная реализации товаров полностью оплачена, ставим ей признак полной оплаты expenditureWaybill.IsFullyPaid = debtRemainder <= 0M; } // и выходим if (saleWaybill.IsFullyPaid) { break; } } // удаляем полностью разнесенные части var undistributedPartsToDelete = dealPaymentUndistributedPartsInfo.Where(x => x.Sum == 0).ToList(); foreach (var item in undistributedPartsToDelete) { dealPaymentUndistributedPartsInfo.Remove(item); } }