Работа с Record. Часть четвертая, практическая

Предыдущие части можно найти здесь: «1. Основные команды. Чтение данных без блокировок», «2. Modify», «3. FILTERGROUP. Другие фирмы».

Примеры из практики Navision

Вам необходимо получить остатки определенного товара на определенном складе. Есть два способа сделать это. У обоих способов есть свои за и против.

  1. ПРАВИЛЬНО — (ЗА  — вам не нужно заботиться об индексах. ПРОТИВ — задействование обычных полей для присвоения значений и наложение фильтров на FLOWFILTER’ы).
    CLEAR(recItem);
    recItem."No." := '1000'; // или recItem.GET('1000');
    recItem.SETRANGE("Location Filter",'BLU');
    recItem.CALCFIELDS(Inventory);
    MESSAGE('%1',recItem.Inventory);
  2. ПРАВИЛЬНО (ЗА — нет присвоения значения и всегда используются фильтры, ПРОТИВ — необходимо знать конкретный индекс, используемый для получения значения вычисляемого SIFT-поля):
    recItemLedgerEntry.RESET;
    recItemLedgerEntry.SETCURRENTKEY("Item no.","Location Code");
    recItemLedgerEntry.SETRANGE("Item No.",'1000');
    recItemLedgerEntry.SETRANGE("Location Code",'BLU');
    recItemLedgerEntry.CALCSUMS(Inventory);
    MESSAGE('%1',recItemLedgerEntry.Quantity);

Если вам нужно два экземпляра переменных одной и той же таблицы — можете использовать вместо двух переменных массив. Они работают независимо, как две отдельных переменных. Пара примеров:

recRecordVar1.RESET; => recRecordVar[1].RESET;
CLEAR(recRecordVar1); => CLEAR(recRecordVar[1]);
recRecordVar1.SETRANGE(...) => recRecordVar[1].SETRANGE()

Для определения индекса массива можно использовать переменную, например:

intSomeInteger := 1;
recRecordVar1.RESET; => recRecordVar[intSomeInteger].RESET;

Работу с массивами можно использовать для временных таблиц. Они также работают независимо друг от друга, как и настоящие таблицы. НО ЗДЕСЬ БУДЕТ ВСЕГО ОДНА ВРЕМЕННАЯ ТАБЛИЦА! То есть, при создании переменной с индексом [1] она будет доступна и с индексом [2]. Пример (это начало работы с временной таблицей, поэтому записей там еще нет).

CLEAR(recRecord[1]);
recRecord[1]."No." := '1');
recRecord[1].INSERT(FALSE);
recRecord[2].RESET;
recRecord[2].FINDFIRST;
MESSAGE('%1',recRecord[2]."No."); // здесь будет показано значение записи с индексом [1]!

Вычисление суммы по Общим бизнес группам и Общим товарным группам таблицы 15 G/L Entry

На SQL сделать это легко:

SELECT "Gen_ Bus_ Posting Group","Gen_ Prod_ Posting Group", SUM(Amount)
FROM dbo."CRONUS International Ltd_$G_L Entry"
GROUP BY "Gen_ Bus_ Posting Group","Gen_ Prod_ Posting Group"
ORDER BY "Gen_ Bus_ Posting Group","Gen_ Prod_ Posting Group"

В C/AL мы, к сожалению, не можем использовать инструкции SQL (или придется использовать ADO), поэтому есть другой способ. Я рекомендую всегда использовать его для суммирования в C/AL:

recGLEntry.RESET;
recGLEntry.SETCURRENTKEY(...); // попробуйте включить ключ, который можно использовать
// для быстрого получения фильтрованного набора данных

recGLEntry.SETRANGE(.....); // накладываем фильтры
IF recGLEntry.FINDSET THEN
REPEAT
// "tmpGLEntry" - временная таблица, где мы будем хранить итоги группировки
tmpGLEntry.RESET;

// надо постараться выбрать хороший ключ для фильтров
IF NOT tmpGLEntry.SETCURRENTKEY("Gen. Bus. Posting Group") THEN
IF NOT tmpGLEntry.SETCURRENTKEY("Gen. Prod. Posting Group") THEN ;

// Я накладываю фильтр на записи, по которым хочу сделать группировку
tmpGLEntry.setrange("Gen. Bus. Posting Group",recGLEntry."Gen. Bus. Posting Group");
tmpGLEntry.setrange("Gen. Prod. Posting Group",recGLEntry."Gen. Prod. Posting Group");

IF NOT tmpGLEntry.FINDFIRST THEN BEGIN

// Я не нашел запись, поэтому создал новую.
// Помните, что во время вставки записи вам нужен уникальный первичный ключ

// Это условие выполняется, поскольку каждая считанная мной recGLEntry - уникальна,
// поэтому можно просто делать INSERT "recGLEntry" в мою временную таблицу
tmpGLEntry := recGLEntry;
tmpGLEntry.insert(FALSE)

END ELSE BEGIN
// Я нашел требуемое сочетание, поэтому добавляю поля к суммеtmpGLEntry.Amount += recGLEntry.Amount;
tmpGLEntry.MODIFY(FALSE);
END;
UNTIL recGLEntry.NEXT = 0;

// Теперь у меня во временной таблице записи, которые содержат суммы по всем нужным мне комбинациям.
// Если вам необходима сортировка, понадобится ключ, а свойство MaintainSQLIndex можно отключить

// Еще один вариант - сделать новую таблицу со всеми нужными ключами и полям и использовать ее как
//временную. Вам НЕ НУЖНА лицензия на новую таблице, которую вы используете ТОЛЬКО в качестве временной!
// Теперь вы просто можете читать данные из временной таблицы, и делать с ними все, что угодно

tmpGLEntry.RESET;
FORM.RUNMODAL(0,tmpGLEntry);

Оригинал статьи называется «How to work with record-variables (version 2)?»

Автор:

В области Navision - с 2003 года. Профессиональные интересы: NAV, MS SQL, .NET, BPMN, IT-менеджмент. Предметная область: логистика, финансы, склады, 3PL.

Количество статей, опубликованных автором: 86.

Комментарии (2 комментария)

  1. Владимир

    Такой вопрос, если нам нужно оперативно обновлять суммы в форме.
    То есть у нас есть под таблицей поля с суммами некоторых столбцов, добавленных в SumIndexFields.
    При этом на форму может быть наложен абсолютно любой фильтр. То есть, вторичный ключ у нас не резиновый.
    Как лучше всего просуммировать столбцы?
    Поможет ли на OnAfterGetRecord или каком-то другом триггере прописать что-то такое:
    TempTable.COPY(Table);
    TempTable.CALCSUMS(Amount1,Amount2);
    Amount1Sum := TempTable.Amount1;
    Amount2Sum := TempTable.Amount2

    Если да, то на какой триггер повесить TempTable.DELETEALL, чтобы заново собрать все строки, которые мы суммируем?

    И вообще, как правильнее всего это сделать?

Добавить комментарий