Закажите бесплатную презентацию продуктов РосБизнесСофт прямо сейчас!
«Табличная часть» — это по вложенный объект в другой объект (тип объекта: 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); }); });
Итоговый результат добавления добавления табличной части:
Пользователя нажимает на «плюсик».
Открывается модальная «Форма редактирования» табличной части «Техническое обслуживание» с полями.
Пользователь заполняет поля на форме и нажимаем «Сохранить».
После чего форма модальная закрывается и на экране отображается новая (отредактированная) строка в «Табличной части» родительского Объекта. Строка всегда подкрашивается желтым цветом.
Рассмотрим пример «Табличной части» с одновременным выбором «Номенклатуры» на примере модуля “Счет” (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); }); });