Контрольное задание#
Вариант 1#
Регистр движения книг#
Создание класса-журнала#
Создайте директорию
regmovи в нем классLbr_RegisterMov-Регистр движения книгс типомjournalи атрибутами:
name |
attribute-type |
caption |
Дополнительно |
|---|---|---|---|
idBook |
Long |
Книга |
Ссылается на класс Lbr_Book |
gidSrcDoc |
Varchar |
Документ источник |
Атрибут переменной ссылочности ( |
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 книги
Наименование книги
Остаток на начало периода
Возврат/поступление за период
Выдача за период
Изменение за период
Остаток на конец периода
Особенности реализации:
Данные для отчета берем по регистру
Lbr_RegisterMovБерем данные до конца периода и фильтруем их по организации.
Для расчета количество на начало периода суммируем только те данные, дата которых меньше начала периода.
Для расчета изменений в текущем периоде суммируем только те данные, дата которых входит в наш период.
Примечание
Для того чтобы исключить данные из суммирования можно воспользоваться следующей конструкцией:
sum(case when r.dRegDate <= :flt_dBegin then <расчет количества> end)
Фильтр с подстановкой значений по умолчанию из главной выборки приложения:
Период с
редактор даты.по
редактор датыОрганизация
выпадающий список
Расположите фильтры в одну строчку (принцип размещения атрибутов на панели фильтров аналогичен размещению в карточке. Если будете делать через разметку hBox, vBox, то используйте тег layout внутри тега filter)
Примечание
В этом задании нельзя использовать макрос фильтра в тексте запроса, т.к. нам нужны данные до начала периода, чтобы посчитать количество на начало. Следует обращаться к переменным фильтра напрямую в запросе, например, :flt_dBegin
Перечень атрибутов выборки главного приложения можно посмотреть через debugger. Для получения значения используйте метод getVar, передав в него имя нужного атрибута с префиксом super$
Детализация#
Создайте и подключите детализацию к мастер-выборке с расшифровкой по документам:
HL документа(для удобства заголовок можно взять изLbr_Document)ДатаКоличество с учетом направления
Примечание
Данные отчета берем по регистру
Lbr_RegisterMovФильтруем их по периоду, книге и организации
Группируем по документу, считаем количество
Добавьте операцию открытия документа в карточке с помощью метода открытия по gid Btk_CommonOperLib().cardEdit
Вариант 2#
Регистр движения книг#
Создание класса-журнала#
Создайте директорию
regmovи в нем классLbr_RegisterMov-Регистр движения книгс типомjournalи атрибутами:
name |
attribute-type |
caption |
Дополнительно |
|---|---|---|---|
idBook |
Long |
Книга |
Ссылается на класс Lbr_Book |
gidSrcDoc |
Varchar |
Документ источник |
Атрибут переменной ссылочности ( |
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 документаДата документаНомер документаКоличество с учетом направления
Особенности реализации:
Данные для отчета берем по регистру
Lbr_RegisterMovПоля, которые относятся к документу можно взять из миксина
Lbr_DocumentФильтруем их по периоду, организации и документу
Рабочий период определяется от даты в фильтре +- 30 дн.
Группируем по документу, считаем количество
Фильтры:
Дата
Текущая дата.Организация
выпадающий список. Значение берется из главной выборки приложенияДокументФильтр с тремя точками. Позволяет открыть список объектов миксинаLbr_Document.
Фильтры дата и Организация расположите в одной строчке.
Фильтр Документ в отдельной строке
(принцип размещения атрибутов на панели фильтров аналогичен размещению в карточке. Если будете делать через разметку hBox, vBox, то используйте тег layout внутри тега filter)
Примечание
Перечень атрибутов выборки главного приложения можно посмотреть через debugger. Для получения значения используйте метод getVar, передав в него имя нужного атрибута с префиксом super$
Текущую дату можно получить через NDate.now()
Добавьте операцию открытия документа в карточке с помощью метода открытия по gid Btk_CommonOperLib().cardEdit
Форма с остатками#
В главной форме добавьте операцию на тулбар, которая будет по выделенной строке регистра выводить информацию об остатках по книгам, по которым было движение в указанном периоде.
Для этого необходимо:
Создать еще одно отображение в вышей выборке, которая будет отображать информацию с остатками
Из операции главной формы необходимо открывать форму с остатками, передав туда параметры о текущем выбранном периоде, организации, и документе в выделенной строке.
В форме-расшифровке реализуйте вывод следующих полей:
ISBN книгиНаименование книгиОстаток на начало периодаВозврат/поступление за периодВыдача за периодИзменение за периодОстаток на конец периода
Особенности реализации:
Данные для отчета берем по регистру
Lbr_RegisterMovБерем данные до конца периода и фильтруем их по организации (они переданы через параметры в форму).
Берем только книги, по которым было движение в переданном документе.
Рабочий период определяется от переданной даты +- 30 дн.
Для расчета количество на начало периода суммируем только те данные, дата которых меньше начала периода.
Для расчета изменений в текущем периоде суммируем только те данные, дата которых входит в наш период.
Примечание
Следует обращаться к переданным параметрам напрямую в запросе, например, :dDate_dz
Для того чтобы исключить данные из суммирования можно воспользоваться следующей конструкцией:
sum(case when r.dRegDate <= :dDate_dz - interval '30 days' then <расчет количества> end)