Закажите бесплатную презентацию продуктов РосБизнесСофт прямо сейчас!
«Табличная часть» — это по вложенный объект в другой объект (тип объекта: positions). Обычно табличный части используются в «Справочниках» и «Документах.
Например, табличной частью является список «Номенклатуры» в документах «Счет» и «Отгрузка».

У одного объекта может быть несколько табличных частей. Например, в документе «Ремонт» существуют табличный части «Запчасти» и «Выполненные работы».
Для добавления табличной части необходимо зайти в “Конфигуратор” необходимого Объекта, найти раздел “Табличные части” и нажать кнопку “Добавить”.

Имя табличной частить должно быть уникальным для выбранного Объекта.

После добавления новой «Табличной части» автоматически добавятся поля: “id”, “visible” и “owner”.
Связь между «Табличной частью» и «Объектом» осуществляется по полю-указателю “owner” (в поле хранится ID основного объекта). Удалять эти поля нельзя.
Рассмотрим добавление табличной части на примере справочника “Автопарк” (references.autos). Данный справочник добавлен в базовую конфигурацию в качестве примера для разработчика и не выведен в основной меню. Ссылка на объект: http://my_crm_ru/autos/
Перейдем в справочник “Автопарк” (references.autos). Добавим табличную часть “Техническое обслуживание” (services):

Затем в «Табличную часть» необходимо добавить “Форму редактирования”.

Новая форма должна быть вложена в «Форму редактирования” родительского объекта (в нашем случае autos/edit). Название формы должно быть уникальным в рамках текущего Объекта.

После добавления появится новая форма у «Табличной части».

Для того, чтобы в “Форме редактирования” автомобиля (references.autos) появилась таблица, ее необходимо прописать в «PHP-сценарии» и в «Шаблоне» родительской «Формы редактирования».
Данная табличная часть будет динамической. Изменения в ней будет отображаться на странице без перезагрузки страницы. За вывод динамической табличной части отвечает метод printDataDTable().
Добавим следующий код в «PHP-сценарий» в метод onPlay() основной “Формы редактирования” автомобиля:
$this->printDataDTable($this->structure->services, "services", [
"date",
"number",
"km",
"comment",
"total" => function($obj) {
return Text::money($obj->total);
}
],[
"path" => "/autos/" . $this->structure->id . "/services",
"modalTitle" => Language::getVariable($this->form, 'Services'),
"modalSize" => "tiny",
"modalMulti" => 0,
"modalName" => "services",
"selectAll" => 1,
"calcFields" => ["total"],
"moreActions" => [
[
"onclick" => "functionName(); return false;",
"name" => Language::getVariable($this->form, 'ButtonName'),
"icon" => ['before' => 'fa fa-plus'],
"access" => ACCESS_EDIT,
]);Где:
В “Шаблон” необходимо добавить таблицу, отвечающую за вывод динамической табличной части:
[[CONTAINER | header: $data.services.name | name: services | template: table]]
[[dTable | name: services | id: servicesTable | key: num]]
[[column | table: services | name: num | header: # | type: num]]
[[column | table: services | name: date | tags: nowrap center]]
[[column | table: services | name: number | align: center]]
[[column | table: services | name: km]]
[[column | table: services | name: comment]]
[[column | table: services | name: total]]
[[edit | table: services]]
[[delete | table: services]]
[[table_end]]
[[CONTAINER_END]]Для контейнера необходимо указать параметр “template: table”, чтобы добавилась кнопка добавления нового элемента в табличную часть.

Обращаем внимание, что «плюсик» в контейнере становится активным (можно добавлять элементы в табличную часть) только тогда, когда родительский Объект уже создан и имеет уникальную ссылку вида: http://my_crm_ru/autos/[id].
В блоке dTable параметр «name» (название табличной части) должен быть обязательно равен 2-му параметру из метода printDataDTable().
Параметр «id» у dTable должен быть уникальным для страницы. Обычно его называют: название табличной части и постфикс «Table». Например: servicesTable.
В результате контейнер с табличной частью будет выглядеть так:

Затем необходимо отредактировать “Форму редактирования” нашей новой табличной части.

В сценарии PHP поменять наследование на ModalEdit:
class Edit extends \Kernel\Actions\Forms\ModalEdit
В «PHP-сценарий» в метод onSave() добавим следующий код, чтобы была связь между табличной частью и родительским Объектом (references.autos):
protected function onSave() {
$this->structure->owner = $this->parents[0][0];
}При наследовании от ModalEdit необходимо реализовать метод responseChange() с вызовом результирующего метода response().
В него передается массив, ключ которого равен названию таблицы в dTable в шаблоне основной «Формы редактирования».
Для динамического редактирования также необходимо всегда передать ключи id и act.
В массиве также передаются поля, которые отображены в форме редактирования табличной части, и при нажатии на кнопку «Сохранить» они динамически передаются в новую строку в таблице (без перезагрузки страницы).
protected function responseChange() {
$this->response([
"services" => [
"id" => $this->structure->id,
"act" => $this->formType,
"date" => $this->structure->date->toFormat('d.m.Y'),
"number" => $this->structure->number,
"km" => $this->structure->km,
"comment" => $this->structure->comment,
"total" => Text::money($this->structure->total)
]
]);
}Добавим в «Шаблон» формы редактирования табличной части новый контейнер с полями и подключить форму js:
{BASE append}
[[CONTAINER | h: hide | name: data]]
[[field | name: date]]
[[field | name: number]]
[[field | name: km]]
[[field | name: comment]]
[[field | name: total]]
[[CONTAINER_END]]
{/BASE}
{JS append}
[[js | name: autos/services/edit]]
{/JS}На закладке «Интерфейс (JS-код)» формы редактирования таб. части реализуем следующие условия:
// функция разблокировки
function enabled(id_number){
if(id_number == 0 || !id_number){
modal.find(".actions .btn.ok").addClass("disabled");
} else {
modal.find(".actions .btn.ok").removeClass("disabled");
}
}
// убираем иконку загрузки страницы
var modal;
top.jsApplication.findTop("body").on("readyModal", function(e, selectorModal, modalName){
if(selectorModal == "#EditModal_0"){
modal = jsApplication.getModal(selectorModal);
modal.find(".actions .btn.cancel").removeClass("disabled");
modal.find(".content").removeClass("loading");
}
});
$(function () {
// после сохранения записи очищаем все поля на форме
$("form.ui.form").on("reset",function(){
modal.find(".actions .btn.cancel").removeClass("disabled");
modal.find(".content").removeClass("loading");
});
// при изменении поля number Блокируем либо разблокируем кнопку “Сохранить”
$("#f_number").on("keyup",function(){
var number = $("#f_number").val();
enabled(number);
});
});
Итоговый результат добавления добавления табличной части:
Пользователя нажимает на «плюсик».

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

Пользователь заполняет поля на форме и нажимаем «Сохранить».
После чего форма модальная закрывается и на экране отображается новая (отредактированная) строка в «Табличной части» родительского Объекта. Строка всегда подкрашивается желтым цветом.

Для скрытия столбца при отображении табличной части можно использовать следующий метод:
$this->tables->products->delColumn('total');Где:
Рассмотрим пример «Табличной части» с одновременным выбором «Номенклатуры» на примере модуля “Счет” (documents.orders).
В табличной части «products» создаются 2 поля для номенклатуры:


В шаблоне «Формы редактирования» табличной части «products» указывается скрытым поле «product».
<input type="hidden" name="object_product" id="f_product" value="$data.product.value">
Добавляем фрейм на форму выбора «Номенклатуры».
<div class="m-5">
<iframe class="selectProduct" style="width:100%" height="600" resize-element="#productsTree" frameborder="0" scrolling="yes" id="products" src="/products/select/?noTree=1&pType=$pType$&store=$store$&noLoader=1$appendFilterParents"></iframe>
</div>Важно! В этом случаем мы добавляем «Формы выбора» номенклатуры из объекта «Номенклатуры», а не из табличной части «Номенклатура» документа «Счет».
Обратите внимание на путь формы выбора из фрейма:
/products/select/?noTree=1&pType=$pType$&store=$store$&noLoader=1$appendFilterParents
В «Форму редактирования» табличной части «product» в метод onPlay() добавляем метод для передачи переменных на форму. Необходимо передать «Склад» и «Тип цены» (если они есть в Объекте), чтобы «Форма выбора» Номенклатура показала нам остатки товара на определенном «Складе» и выбранный «Тип цен» (например, оптовые).
$this->page->set([
"BASE.pType" => $this->parents[0]->price_type->id,
"BASE.store" => $this->parents[0]->storehouse->id
]);Где:
В результате при добавления «Номенклатуры» в «Счет» откроется следующая форма.

При выборе «Номенклатуры» в списке добавим следующую логику:
Для этого внесем правки в «Форму редактирования» табличной части «Products» на закладке «Интерфейс (JS-код)»:
// блокировка / разблокировка кнопки "Сохранить"
function enabled(number){
if(number == 0 || !number || parseFloat(minNumber) > number){
modal.find(".actions .btn.ok").addClass("disabled");
} else {
modal.find(".actions .btn.ok").removeClass("disabled");
}
}
// подсчет сумм
function result(a, b){
return (b != 0) ? (a / b).toFixed(2) : 0;
}
// блокировка / разблокировка полей
function enabledFields(isEnabled) {
if(isEnabled == undefined)
isEnabled = false;
/* если режим редактирования */
productId = jsApplication.getString($("#f_product").val());
number = jsApplication.getNumber($("#f_number").val());
if(productId.length > 0 && number > 0) {
isEnabled = true;
}
if(isEnabled) {
$("input, select, textarea").removeAttr("disabled").closest(".field").removeClass("disabled").find(".dropdown").removeClass("disabled");
} else {
$("input, select, textarea").attr("disabled", "disabled").closest(".field").addClass("disabled").find(".dropdown").addClass("disabled");
}
}
var modal;
// после загрузки модального окна выключаем иконку загрузки (loader) и снимаем disabled с кнопки "Закрыть"
top.jsApplication.findTop("body").on("readyModal", function(e, selectorModal, modalName){
if(selectorModal == "#EditModal_0"){
modal = jsApplication.getModal(selectorModal);
enabledFields();
modal.find(".actions .btn.cancel").removeClass("disabled");
modal.find(".content").removeClass("loading");
}
});
$(function () {
// после отправки формы очищаем поля
$("form.ui.form").on("reset",function(){
enabledFields();
modal.find(".actions .btn.cancel").removeClass("disabled");
modal.find(".content").removeClass("loading");
});
// при нажатии на товар из фрейма вставляем данные в шаблон
jsApplication.OnEventTableInFrame("iframe#products", "products", "onSelect", function(e, extra){
$("#f_product").val(extra.id);
$("#f_name").val($(extra.tr).children("td:eq(2)").text());
$("#f_price_nds, #f_total").val(parseFloat($(extra.tr).children("td:eq(3)").text().replace("'", '')));
$("#f_number").val(1);
enabledFields(true);
enabled(extra.id);
});
// при изменении полей number или price_nds пересчитываем total (сумма)
$("#f_number, #f_price_nds").on("keyup",function(){
var number = $("#f_number").val(),
price_nds = $("#f_price_nds").val();
$("#f_total").val(result(number * price_nds, 1));
enabled(number);
});
// при изменении поля total пересчитываем price_nds (цена)
$("#f_total").on("keyup",function(){
var number = $("#f_number").val(),
total = $("#f_total").val();
$("#f_price_nds").val(result(total, number));
enabled(number);
});
});