Контрольное задание#

Вариант 1#

Регистр движения книг#

Создание класса-журнала#

  • Создайте директорию regmov и в нем класс Lbr_RegisterMov - Регистр движения книг с типом journal и атрибутами:

name

attribute-type

caption

Дополнительно

idBook

Long

Книга

Ссылается на класс Lbr_Book

gidSrcDoc

Varchar

Документ источник

Атрибут переменной ссылочности (type="refAnyObject"), ссылается на класс Lbr_Document

gidDet

Varchar

Позиция источник

Атрибут переменной ссылочности

nDirection

Number

Направление

idLibrarian

Long

Библиотекарь

Ссылочный на объект, ссылается на класс Bs_Person

idPerson

Long

Читатель

Ссылочный на объект, Ссылается на класс Bs_Person

nQty

Number

Кол-во

dReg

Date

Дата операции

Редактор даты

idDepOwner

Long

Организация

Ссылочный на объект, Ссылается на класс Bs_DepOwner

  • Запустите кодогенерацию по данному классу

  • Добавьте orm класса в all.xml и запустите генератор таблиц для него

  • Добавьте в Lbr_MainMenuAvi пункт Регистр движения книг под Тесты и отладка, в котором пропишите открытие умол. списка для Lbr_RegisterMovAvi

Отражение в регистр и удаление из него#

В Lbr_RegisterMovApi напишите логику для отражения и удаления из регистра:

  • Для универсальности и изоляции логики регистра объявите case class (для удобства можно объявить перед class Lbr_RegisterMovApi, например назвать objReg) с полями из таблицы ниже

  • Создайте метод отражения в регистр по списку объектов данного case class (например wrtByObj(apObj: List[objReg]). В нем напишите обход (например через foreach) по списку объектов apObj и создание записей в регистре по данным из них, при установке количества, можно записывать сразу с учетом nDirection, чтобы в дальнейшем при вычислении остатков не писать nQty*nDirection.

  • Напишите метод удаления из регистра по полю gidSrcDoc, на вход будет gid документа (например deleteBySrcDoc(gipSrcDoc: NGid)). Вместо запросов можно использовать транзакционный индекс. Руководство разработчика: Взаимодействие с базой данных # Транзакционный индекс.

В Api документов:

  • Напишите метод отражения в регистр по позициям документа, на вход будет rop документа (например wrtToReg(rop: ApiRop)). Для этого создайте переменную val avObj = ArrayBuffer[objReg]() и при обходе по позициям документа (через refreshByParent) заполните avObj объектами из case class с данными по таблице ниже. После обхода позиций вызовите метод отражения в регистр по данным заполненного списка Lbr_RegisterMovApi().wrtByObj(avObj.toList)

  • На переход состояния в >=300 и из < 300, после вызова метода с проверками, пропишите вызов метода отражения в регистр, на обратный переход пропишите вызов метода удаления из регистра.

Таблица с полями для case class и откуда надо брать для них значения:

Имя поля в регистре

Тип

Значение

idBook

NLong

idBook из позиции

gidSrcDoc

NGid

gid документа

gidDet

NGid

gid позиции

nDirection

NNumber

1 для приходной накладной и возврата книг, -1 для выдачи книг

idLibrarian

NLong

idLibrarian из документа

idPerson

NLong

idPerson из документа, если есть такое поле

nQty

NNumber

Количество в позиции, если атрибута с количеством нет, то берем 1

dReg

NDate

Дата документа

idDepOwner

NLong

idDepOwner из документа

Метод проверки на отрицательные остатки#

В Lbr_RegisterMovApi напишите метод проверки на наличие отрицательных остатков для регистра, в разрезе idBook и idDepOwner. Для этого:

  • Добавьте еще один case class с атрибутами разреза (например case class objRegVal(idBook: NLong, idDepOwner: NLong)) уже внутри Lbr_RegisterMovApi

  • Добавьте новый метод, на вход которого будет список из данного case class (например validateReg(apObjVal: List[objRegVal]))

  • Для упрощения написания проверки, можно использовать for (rv <- new ASelect с запросом по регистру, с группировкой по разрезам и условием на отрицательное количество. Для передачи в запрос данных из параметра apObjVal, создайте массивы с типом LongPgArray для idBook и idDepOwner (например val idavBook = LongPgArray(apObjVal.map(_.idBook))) и в запрос пропишите в конструкции with трансформацию массивов в таблицу со столбцами. Далее уже можно обращаться к data как к обычной таблице.

with data as(
      select unnest(${idavBook}) idBook
             ,unnest(${idavDepOwner}) idDepOwner
    )
  • Внутри for соберите полученные данные запроса с отрицательными остатками в формате книга ${HL книги} - остаток ${получившееся количество} (организация ${HL организации})" в переменную (например val avError = ArrayBuffer[NString]()) объявленную до for и уже в конце если она не пустая, то пропишите вызов ошибки в формате Действие невозможно, так как в регистре образовались отрицательные остатки: ${собранные в avError данные}.

  • Если проверка будет осуществляться через запрос, то обязательно перед for пропишите session.flush(), чтобы в БД были актуальные данные.

  • В методе wrtByObj и deleteBySrcDoc допишите сбор данных разрезов в переменную со списком case class (например val avObjRegVal = ArrayBuffer[objRegVal]()) и в конце допишите вызов метода проверки validateReg(avObjRegVal.toList)

Отчет по остаткам#

Шапка#

Создайте отдельную avi для отчета по остаткам с полями:

  • ISBN книги

  • Наименование книги

  • Остаток на начало периода

  • Возврат/поступление за период

  • Выдача за период

  • Изменение за период

  • Остаток на конец периода

Особенности реализации:

  1. Данные для отчета берем по регистру Lbr_RegisterMov

  2. Берем данные до конца периода и фильтруем их по организации.

  3. Для расчета количество на начало периода суммируем только те данные, дата которых меньше начала периода.

  4. Для расчета изменений в текущем периоде суммируем только те данные, дата которых входит в наш период.

Примечание

Для того чтобы исключить данные из суммирования можно воспользоваться следующей конструкцией: sum(case when r.dRegDate <= :flt_dBegin then <расчет количества> end)

Фильтр с подстановкой значений по умолчанию из главной выборки приложения:

  • Период с
    редактор даты.

  • по
    редактор даты

  • Организация
    выпадающий список

Расположите фильтры в одну строчку (принцип размещения атрибутов на панели фильтров аналогичен размещению в карточке. Если будете делать через разметку hBox, vBox, то используйте тег layout внутри тега filter)

Примечание

В этом задании нельзя использовать макрос фильтра в тексте запроса, т.к. нам нужны данные до начала периода, чтобы посчитать количество на начало. Следует обращаться к переменным фильтра напрямую в запросе, например, :flt_dBegin

Перечень атрибутов выборки главного приложения можно посмотреть через debugger. Для получения значения используйте метод getVar, передав в него имя нужного атрибута с префиксом super$

Детализация#

Создайте и подключите детализацию к мастер-выборке с расшифровкой по документам:

  • HL документа (для удобства заголовок можно взять из Lbr_Document)

  • Дата

  • Количество с учетом направления

Примечание

  1. Данные отчета берем по регистру Lbr_RegisterMov

  2. Фильтруем их по периоду, книге и организации

  3. Группируем по документу, считаем количество

Добавьте операцию открытия документа в карточке с помощью метода открытия по gid Btk_CommonOperLib().cardEdit

Вариант 2#

Регистр движения книг#

Создание класса-журнала#

  • Создайте директорию regmov и в нем класс Lbr_RegisterMov - Регистр движения книг с типом journal и атрибутами:

name

attribute-type

caption

Дополнительно

idBook

Long

Книга

Ссылается на класс Lbr_Book

gidSrcDoc

Varchar

Документ источник

Атрибут переменной ссылочности (type="refAnyObject"), ссылается на класс Lbr_Document

gidDet

Varchar

Позиция источник

Атрибут переменной ссылочности

nDirection

Number

Направление

idLibrarian

Long

Библиотекарь

Ссылочный на объект, ссылается на класс Bs_Person

idPerson

Long

Читатель

Ссылочный на объект, Ссылается на класс Bs_Person

nQty

Number

Кол-во

dReg

Date

Дата операции

Редактор даты

idDepOwner

Long

Организация

Ссылочный на объект, Ссылается на класс Bs_DepOwner

  • Запустите кодогенерацию по данному классу

  • Добавьте orm класса в all.xml и запустите генератор таблиц для него

  • Добавьте в Lbr_MainMenuAvi пункт Регистр движения книг под Тесты и отладка, в котором пропишите открытие умол. списка для Lbr_RegisterMovAvi

Отражение в регистр и удаление из него#

В Lbr_RegisterMovApi напишите логику для отражения и удаления из регистра:

  • Для универсальности и изоляции логики регистра объявите case class (для удобства можно объявить перед class Lbr_RegisterMovApi, например назвать objReg) с полями из таблицы ниже

  • Создайте метод отражения в регистр по списку объектов данного case class (например wrtByObj(apObj: List[objReg]). В нем напишите обход (например через foreach) по списку объектов apObj и создание записей в регистре по данным из них, при установке количества, можно записывать сразу с учетом nDirection, чтобы в дальнейшем при вычислении остатков не писать nQty*nDirection.

  • Напишите метод удаления из регистра по полю gidSrcDoc, на вход будет gid документа (например deleteBySrcDoc(gipSrcDoc: NGid)). Вместо запросов можно использовать транзакционный индекс. Руководство разработчика: Взаимодействие с базой данных # Транзакционный индекс.

В Api документов:

  • Напишите метод отражения в регистр по позициям документа, на вход будет rop документа (например wrtToReg(rop: ApiRop)). Для этого создайте переменную val avObj = ArrayBuffer[objReg]() и при обходе по позициям документа (через refreshByParent) заполните avObj объектами из case class с данными по таблице ниже. После обхода позиций вызовите метод отражения в регистр по данным заполненного списка Lbr_RegisterMovApi().wrtByObj(avObj.toList)

  • На переход состояния в >=300 и из < 300, после вызова метода с проверками, пропишите вызов метода отражения в регистр, на обратный переход пропишите вызов метода удаления из регистра.

Таблица с полями для case class и откуда надо брать для них значения:

Имя поля в регистре

Тип

Значение

idBook

NLong

idBook из позиции

gidSrcDoc

NGid

gid документа

gidDet

NGid

gid позиции

nDirection

NNumber

1 для приходной накладной и возврата книг, -1 для выдачи книг

idLibrarian

NLong

idLibrarian из документа

idPerson

NLong

idPerson из документа, если есть такое поле

nQty

NNumber

Количество в позиции, если атрибута с количеством нет, то берем 1

dReg

NDate

Дата документа

idDepOwner

NLong

idDepOwner из документа

Метод проверки на отрицательные остатки#

В Lbr_RegisterMovApi напишите метод проверки на наличие отрицательных остатков для регистра, в разрезе idBook и idDepOwner. Для этого:

  • Добавьте еще один case class с атрибутами разреза (например case class objRegVal(idBook: NLong, idDepOwner: NLong)) уже внутри Lbr_RegisterMovApi

  • Добавьте новый метод, на вход которого будет список из данного case class (например validateReg(apObjVal: List[objRegVal]))

  • Для упрощения написания проверки, можно использовать for (rv <- new ASelect с запросом по регистру, с группировкой по разрезам и условием на отрицательное количество. Для передачи в запрос данных из параметра apObjVal, создайте массивы с типом LongPgArray для idBook и idDepOwner (например val idavBook = LongPgArray(apObjVal.map(_.idBook))) и в запрос пропишите в конструкции with трансформацию массивов в таблицу со столбцами. Далее уже можно обращаться к data как к обычной таблице.

with data as(
      select unnest(${idavBook}) idBook
             ,unnest(${idavDepOwner}) idDepOwner
    )
  • Внутри for соберите полученные данные запроса с отрицательными остатками в формате книга ${HL книги} - остаток ${получившееся количество} (организация ${HL организации})" в переменную (например val avError = ArrayBuffer[NString]()) объявленную до for и уже в конце если она не пустая, то пропишите вызов ошибки в формате Действие невозможно, так как в регистре образовались отрицательные остатки: ${собранные в avError данные}.

  • Если проверка будет осуществляться через запрос, то обязательно перед for пропишите session.flush(), чтобы в БД были актуальные данные.

  • В методе wrtByObj и deleteBySrcDoc допишите сбор данных разрезов в переменную со списком case class (например val avObjRegVal = ArrayBuffer[objRegVal]()) и в конце допишите вызов метода проверки validateReg(avObjRegVal.toList)

Отчет по остаткам#

Главная форма#

Создайте отдельную avi для отчета по остаткам с полями:

  • ISBN книги

  • Наименование книги

  • HL документа

  • Дата документа

  • Номер документа

  • Количество с учетом направления

Особенности реализации:

  1. Данные для отчета берем по регистру Lbr_RegisterMov

  2. Поля, которые относятся к документу можно взять из миксина Lbr_Document

  3. Фильтруем их по периоду, организации и документу

  4. Рабочий период определяется от даты в фильтре +- 30 дн.

  5. Группируем по документу, считаем количество

Фильтры:

  • Дата
    Текущая дата.

  • Организация
    выпадающий список. Значение берется из главной выборки приложения

  • Документ Фильтр с тремя точками. Позволяет открыть список объектов миксина Lbr_Document.

Фильтры дата и Организация расположите в одной строчке. Фильтр Документ в отдельной строке (принцип размещения атрибутов на панели фильтров аналогичен размещению в карточке. Если будете делать через разметку hBox, vBox, то используйте тег layout внутри тега filter)

Примечание

Перечень атрибутов выборки главного приложения можно посмотреть через debugger. Для получения значения используйте метод getVar, передав в него имя нужного атрибута с префиксом super$

Текущую дату можно получить через NDate.now()

Добавьте операцию открытия документа в карточке с помощью метода открытия по gid Btk_CommonOperLib().cardEdit

Форма с остатками#

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

Для этого необходимо:

  1. Создать еще одно отображение в вышей выборке, которая будет отображать информацию с остатками

  2. Из операции главной формы необходимо открывать форму с остатками, передав туда параметры о текущем выбранном периоде, организации, и документе в выделенной строке.

  3. В форме-расшифровке реализуйте вывод следующих полей:

  • ISBN книги

  • Наименование книги

  • Остаток на начало периода

  • Возврат/поступление за период

  • Выдача за период

  • Изменение за период

  • Остаток на конец периода

Особенности реализации:

  1. Данные для отчета берем по регистру Lbr_RegisterMov

  2. Берем данные до конца периода и фильтруем их по организации (они переданы через параметры в форму).

  3. Берем только книги, по которым было движение в переданном документе.

  4. Рабочий период определяется от переданной даты +- 30 дн.

  5. Для расчета количество на начало периода суммируем только те данные, дата которых меньше начала периода.

  6. Для расчета изменений в текущем периоде суммируем только те данные, дата которых входит в наш период.

Примечание

Следует обращаться к переданным параметрам напрямую в запросе, например, :dDate_dz

Для того чтобы исключить данные из суммирования можно воспользоваться следующей конструкцией: sum(case when r.dRegDate <= :dDate_dz - interval '30 days' then <расчет количества> end)