Запрос
Помимо модели еще одним источником информации от БД может быть sql-запрос. Его результат можно вывести как в грид (grid.dataSource = массив данных от запроса), так и в лукапе (источником данных для списка указывается не модель, а запрос).
Создадим новую форму, в ней выведем в информацию по всем товарам из всех прайсов, собранную с помощью sql-запроса. Так же добавим условие в виде лукапа с товарами, но он будет построен не на модели, а на запросе.
Создайте в каталоге forms файлы PriceListAnalytForm.xml
| <Form writeRoles='super' title='Прайс листы - Аналитическая форма'>
<ToolBar>
<Tool id="toolBarItemExit" caption="Закрыть" image="current-theme/images/leftNavBar/close.png" clicked='toolBarItemExit_clicked'/>
</ToolBar>
<Layout type='bootstrap' >
<Group id='gr1' editorWidth='12' showBorder='true' >
</Group>
<Group caption='Результат' id='gr2' editorWidth='12' >
<Grid caption='Товары' id='Grid1' bind='Detail' editorWidth='12' selectionMode='multiplecellsadvanced' height='400px' toolbarVisible='false' >
<Column text='Дата документа' datafield='DocumentDate' cellsformat='dd.MM.yyyy' width='10%' >
<DateTimeInput />
</Column>
<ColumnGroup name="group1" text='Товар'>
<Column text='Группа' datafield='MaterialGroup' width='15%' >
<TextBox />
</Column>
<Column text='Наименование' datafield='Material' width='50%' >
<TextBox />
</Column>
</ColumnGroup>
<Column text='Цена' datafield='Price' width='15%' cellsformat='D2' >
<NumberInput />
</Column>
</Grid>
</Group>
</Layout>
</Form>
|
и файл PriceListAnalytForm.js
| import { Form } from 'ecosoft-lexema8'; // импорт компонентов из библиотеки ecosoft-lexema8
import template from './PriceListAnalytForm.xml'; // импорт макета документа
export default class PriceListAnalytForm extends Form {
constructor(...args) {
super(...args);
}
// макет документа
static get template() {
return template;
}
toolBarItemExit_clicked() {
this.close();
}
}
|
, и зарегистрируем ее в системе (units.json)
| {
"caption": "Прайс лист - Аналитическая форма",
"key": "PriceListAnalytForm",
"form": "./forms/PriceListAnalytForm"
}
|
Результат:


Список для условия добавим позже.
Теперь создадим в Лексеме запрос. Он состоят как минимум из 2х файлов:
JSON файл, оканчивающийся на .query.json. Он нужен для регистрации в Лексеме запроса, описания входных параметров и прав доступа.
SQL файл. Он содержит сам sql-запрос.
Все запросы должны располагаться в каталоге queries. Создайте в нём два файла:
файл GetPriceLists.sql
| select p.DocumentDate, p.VCode
, Material = m.Name
, MaterialGroup = mg.Name
, d.Price
from dbo.PriceListDetail d
inner join dbo.PriceList p on p.VCode = d.PCode
inner join dbo.Material m on m.VCode = d.Material
left join dbo.MaterialGroup mg on mg.VCode = m.MaterialGroup
|
и файл GetPriceLists.query.json
| {
"select": {
"text": "./GetPriceLists.sql",
"parameters": []
},
"parameters": {
},
"rights": {
"create": [],
"read": ["super"],
"update": [],
"delete": []
}
}
|
Запрос и Grid
Вернемся к нашей форме. Выполнение запроса сделаем по кнопке. Для этого добавим её в тулбар и пропишем реализацию.
| import { Form, QuerySource } from 'ecosoft-lexema8'; // импорт компонентов из библиотеки ecosoft-lexema8
import template from './PriceListAnalytForm.xml'; // импорт макета документа
import GetPriceLists_query from '../queries/GetPriceLists.query.json';
export default class PriceListAnalytForm extends Form {
…
toolBarItemSearch_clicked() {
new QuerySource(GetPriceLists_query).load()
.done(result => {
this.controls.Grid1.dataSource = ko.mapping.fromJS(result);
})
}
}
|
Запуск запроса выполняется с помощью объекта QuerySource, входящего в состав Лексемы. Для этого к его экземпляру (который принимает один параметр - ссылка на запрос) применить команду load. Эта команда может принимать параметры. Результатом станет объект типа Deffered, который вернёт результат запроса.
Далее этот результат вставляется в грид.
Результат:
Чтобы лучше увидеть результат запроса создайте несколько прайс листов на разную дату. В моем случае запрос вернул следующий результат:

Запрос и Lookup
Добавим на форму фильтр - товар, и сделаем так, чтобы запрос возвращал информацию только по выбранному товару.
В добавим на форму список (lookup) “Товар”, но построен он будет не на модели Товар, а на запросе. Такой подход часто применяется, так как ускорит получение списка, ведь не надо загружать множество свойств модели, которые тут по сути и не используются.
Создадим сперва запрос, он будет крайне простым:
Material.sql
| select VCode, Name
from dbo.Material
|
Material.query.json
| {
"select": {
"text": "./Material.sql",
"parameters": []
},
"parameters": {},
"rights": {
"create": [],
"read": ["super"],
"update": [],
"delete": []
}
}
|
Добавьте в макет формы ( в группу gr1):
| <Lookup caption='Товар' id='LookupMaterial' dataSource='../queries/Material.query.json' showValueMember='false' captionWidth='1' editorWidth='2' width='100%' />
|
Получим:

Теперь его надо передать параметром в запрос GetPriceLists:
| toolBarItemSearch_clicked() {
const qParam = {
Material: (this.controls.LookupMaterial.value || {}).VCode || 0
}
new QuerySource(GetPriceLists_query).load(qParam)
.done(result => {
this.controls.Grid1.dataSource = ko.mapping.fromJS(result);
})
}
|
Добавим объявление этого параметра в самом запросе:
GetPriceLists.query.json
| {
"select": {
"text": "./GetPriceLists.sql",
"parameters": ["Material"]
},
"parameters": {
"Material": "bigint"
},
"rights": {
"create": [],
"read": ["super"],
"update": [],
"delete": []
}
}
|
Запрос:
GetPriceLists.sql
| select p.DocumentDate, p.VCode
, Material = m.Name
, MaterialGroup = mg.Name
, d.Price
from dbo.PriceListDetail d
inner join dbo.PriceList p on p.VCode = d.PCode
inner join dbo.Material m on m.VCode = d.Material
left join dbo.MaterialGroup mg on mg.VCode = m.MaterialGroup
where d.Material = @Material
|
Результат:

Немного усложним условие в sql-запросе, чтобы в случае, если товар не выбран, выводились все товары.
| ….
where (@Material > 0 and d.Material = @Material)
or (@Material = 0)
|
Синхронизация
В проекте Лексемы можно хранить необходимые для проекта sql объекты (функции, процедуры, представления) и через Обновление БД обновлять их в БД. Все они хранятся в каталоге проекта sqlObjects.
Для того, чтобы это сделать, сперва создадим такой sql объект. Перенесём содержимое запроса GetPriceLists.sql в функцию, а в теле запроса будем только её вызыввать.
Создание sql функции:
| SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.GetPriceLists
(
@Material bigint
)
RETURNS TABLE
AS
RETURN
(
select p.DocumentDate, p.VCode
, Material = m.Name
, MaterialGroup = mg.Name
, d.Price
from dbo.PriceListDetail d
inner join dbo.PriceList p on p.VCode = d.PCode
inner join dbo.Material m on m.VCode = d.Material
left join dbo.MaterialGroup mg on mg.VCode = m.MaterialGroup
where (@Material > 0 and d.Material = @Material)
or (@Material = 0)
)
|
Теперь ее можно увидеть в дереве sql объектов MSSMS:

В запросе GetPriceLists.sql заменим выборку на вызов этой функции:
| select * from dbo.GetPriceLists(@Material)
|
Теперь с помощью еще одного инструмента разработчика (помимо Обновления БД) - Синхронизации перенесем эту функцию в наш проект.
Перейдите по адресу http://localhost:3337/dev#/view/synchronize
На открывшейся форме нажмите “Загрузить”.

В списке должна появиться наша функция. Выберите строку с ней (галочка слева) и нажмите “Выполнить”. В результате код создания функции должен появиться в вашем проекте в каталоге sqlQbjects.

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