Acumatica - Двухстороннее обновление цены за единицу до процента валовой прибыли и обратно - PullRequest
0 голосов
/ 08 апреля 2019

Невозможно настроить цену за единицу, обновляя процент валовой прибыли и обратно.

Я успешно создал новое поле для строки заказа на продажу, чтобы отобразить процент валовой прибыли, но теперь пользователь хочет настроить проценти обновить цену за единицу.Все мои попытки до сих пор вызывали бесконечный цикл.

Атрибут добавлен в поле [UnitPrice] в ЦАП:

[PXFormula(typeof(Switch<Case<Where<SOLineExt.usrGPPercent, Equal<decimal0>>, decimal0>, Mult<SOLineExt.usrGPPercent, SOLine.curyUnitCost>>))]

[UsrGPPercent] атрибуты поля:

[PXDBDecimal]
[PXUIField(DisplayName="GPPercent", Visible = false)]
[PXFormula(typeof(Switch<Case<Where<SOLine.curyLineAmt, Equal<decimal0>>, decimal0>, Div<SOLineExt.usrTotalProfit, SOLine.curyLineAmt>>))]
[PXDefault(TypeCode.Decimal, "0.0")]

[UsrGPPct] Атрибуты поля:

[PXUIField(DisplayName = "GP %", Enabled = true)] 
[PXFormula(typeof(Mult<SOLineExt.usrGPPercent, decimal100>))]
[PXDefault(TypeCode.Decimal, "0.0")]

Все вышеперечисленное отлично работает и обновляет GP%, как и ожидалось.

Попытка # 1 , добавила следующий атрибут в [UsrGCP] (я понимаю, что математика неполная, просто пытаюсь проверить концепцию на этом этапе).

[PXFormula(typeof(Switch<Case<Where<SOLine.curyLineAmt, Equal<decimal0>>, decimal0>, Div<SOLineExt.usrTotalProfit, SOLine.curyLineAmt>>))]

Попытка # 2 : обработчик FieldUpdated:

protected void SOLine_UsrGPPct_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
  if(InvokeBaseHandler != null)
    InvokeBaseHandler(cache, e);
  var row = (SOLine)e.Row;

  PX.Objects.SO.SOLineExt soLineExt = PXCache<SOLine>.GetExtension<PX.Objects.SO.SOLineExt>(row);

  if (row.OrderType == "SO")
  {
      if (soLineExt.UsrGPPct > 0)
      {
        row.CuryUnitPrice = row.CuryUnitCost + (soLineExt.UsrGPPct * row.CuryUnitCost);
      }
  }

}

Оба метода, очевидно, привели к бесконечному циклу (угадывание, поскольку отладчик был запущен и IIS пришлось сбросить).Цель состоит в том, чтобы обновлять только один раз, когда пользователь обновляет любое из полей, и игнорировать обновления, сделанные системой.Есть идеи?

Основываясь на ответе HB_Acumatica, я обновил код выше:

protected void SOLine_UsrGPPct_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
  if(InvokeBaseHandler != null)
    InvokeBaseHandler(cache, e);
  var row = (SOLine)e.Row;

  PX.Objects.SO.SOLineExt soLineExt = PXCache<SOLine>.GetExtension<PX.Objects.SO.SOLineExt>(row);

  if (e.ExternalCall)
  {
      if (soLineExt.UsrGPPct > 0)
      {
        if (row.OrderType == "SO")
        {
          decimal NewUnitPrice;
          decimal GPPercent = soLineExt.UsrGPPct ?? 0;
          decimal UnitCost = row.CuryUnitCost ?? 0;
          NewUnitPrice = UnitCost + ((GPPercent  / (decimal)100) * UnitCost);
          row.CuryUnitPrice = NewUnitPrice;
          row.UnitPrice = NewUnitPrice;
        }
      }
  }
}

Это почти работает.На экране ничего не происходит, но при сохранении цена за единицу действительно обновляется и сохраняется правильно.Почти там, есть какой-то шаг обновления, который я пропускаю?

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

Большое спасибо HB_ACUMATICA!

Финальный код опубликован ниже для тех, кто пытается сделать что-то подобное. Также убрано.

Два поля добавлены в ЦАП (и экран SalesOrder):

UsrGPPercent

Атрибуты:

[PXDBDecimal()]
[PXUIField(DisplayName = "GP %", Enabled = true, Visible = true)] 
[PXFormula(typeof(Mult<Switch<Case<Where<SOLine.curyExtCost, Equal<decimal0>>, decimal0>, Div<SOLineExt.usrTotalProfit, SOLine.curyExtCost>>, decimal100>))]
[PXDefault(TypeCode.Decimal, "0.0")]

Расширение кода:

    #region Event Handlers
    protected void SOLine_UsrGPPercent_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
    {
      if(InvokeBaseHandler != null)
        InvokeBaseHandler(cache, e);
      var row = (SOLine)e.Row;

      PX.Objects.SO.SOLineExt soLineExt = PXCache<SOLine>.GetExtension<PX.Objects.SO.SOLineExt>(row);

      if (e.ExternalCall)
      {
          if (soLineExt.UsrGPPercent > 0)
          {
            if (row.OrderType == "SO")
            {
              decimal NewUnitPrice;
              decimal GPPercent = soLineExt.UsrGPPercent ?? 0;
              decimal UnitCost = row.CuryUnitCost ?? 0;
              decimal QtyOrdered = row.OrderQty ?? 0;
              NewUnitPrice = (UnitCost + ((GPPercent  / (decimal)100) * UnitCost));

              soLineExt.UsrTotalProfit = (NewUnitPrice * QtyOrdered) - (UnitCost * QtyOrdered);
              row.CuryUnitPrice = NewUnitPrice;
              row.UnitPrice = NewUnitPrice;
            }
          }
      }
    }
    #endregion

UsrTotalProfit

Атрибуты:

[PXDBCurrency(typeof(SOLine.curyInfoID), typeof(SOLineExt.usrTotalProfit))] 
[PXUIField(DisplayName = "Total Profit", Enabled = false)]               
[PXFormula(typeof(Sub<SOLine.curyLineAmt, SOLine.curyExtCost>))]    
[PXDefault(TypeCode.Decimal, "0.0")]
1 голос
/ 08 апреля 2019

Обычно это исправляется с помощью e.ExternalCall в поле обновленного обработчика событий для предотвращения рекурсии:

protected void SOLine_UsrGPPct_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
   if (e.ExternalCall)
   {
        // Make sure it gets in here only once when value change in UI
   }
}

Свойство ExternalCall (PXRowUpdatingEventArgs)

Получает значение, указывающее, если оно равно true, что обновление объекта DAC было инициировано из пользовательского интерфейса или через API веб-службы


Кроме того, другой случай, когда дваАтрибуты полей являются взаимозависимыми и нуждаются друг в друге для вычисления их значения.У вас не может быть поля 1 с формулой, которая зависит от поля 2, а также с полем 1, которое также имеет формулу, ссылающуюся на поле 2.

При такой настройке при запросе поля 1 он сначала попытаетсяоцените зависимое поле 2 в формуле поля 1, и так как поле 2 также зависит от поля 1, оно запустит бесконечный цикл рекурсии.

Нет выхода из этого, который я знаю, вам придется повторноспроектируйте поля ЦАП, чтобы ни одно из них не было взаимозависимым.Или просто пропустите атрибуты DAC и выполните вычисления в событиях RowSelected (у этого есть некоторые недостатки, потому что для работы требуется граф).


РЕДАКТИРОВАТЬ:

О значениях полей, не изменяющихся в пользовательском интерфейсе.Это может произойти, когда некоторым полям требуется запуск некоторых событий при изменении их значения (обычно это событие FieldUpdated).

Вместо присвоения таких значений, которые не вызывают событие:

row.Field = value;

Вместо этого вы можете использовать SetValueExt, который вызывает такие события, как FieldUpdated:

sender.SetValueExt<DAC.field>(row, value);

Отправитель должен быть кэшем типа DAC, в вашем случае это должно быть иначе:

Base.Caches[typeof(DAC)].SetValueExt<DAC.field>(row, value);

Обратите внимание, чтополе, имеющее атрибут PXFormula, в какой-то момент получит его значение заново, поэтому вам не следует вручную устанавливать значение таких полей.

Если у вас все еще есть такой атрибут в поле UnitPrice, вам не следуетвручную обновите значение поля:

[PXFormula(typeof(Switch<Case<Where<SOLineExt.usrGPPercent, Equal<decimal0>>, decimal0>, Mult<SOLineExt.usrGPPercent, SOLine.curyUnitCost>>))]

Может быть, все, что вам нужно сделать, - это принудительно обновить формулу при изменении UsrGPPercent.В этом случае вместо установки значения UnitPrice попробуйте использовать метод RaiseFieldDefaulting, чтобы пересчитать формулу: объект-пустышка;

sender.RaiseFieldDefaulting<SOLine.unitPrice>(e.Row, out dummy);
...