|
Конкретные вопросы по lsFusion. Часть 2. | ☑ | ||
---|---|---|---|---|
0
CrushBy
14.10.19
✎
21:08
|
В этой ветке только вопросы по lsFusion, без оценок и срача, что в сторону lsFusion, что в сторону 1С.
Для сравнения и троллинга есть отдельная ветка : OFF: lsFusion vs 1C. Раунд 3 |
|||
449
CrushBy
26.10.19
✎
08:37
|
(448) Покажу на примере.
Можно написать двумя способами : NEWSESSION { f(a) <- g(a); APPLY; } и второй способ (именно обернуть код в транзакцию) : APPLY { f(a) <- g(a); } В первом случае f(a) <- g(a) ничего в базу писать не будет, а запишет изменения во временную таблицу. При APPLY пойдет UPDATE в базу исходя из данных временной таблицы. Во втором случае, начнется транзакция, а затем сразу пойдет UPDATE f ... NEWSESSION нужен в основном для взаимодействия с пользователем (вы же не можете начать транзакцию и дать пользователю что-то выбрать - это убийство базы). На самом деле, открытие любой формы из меню равносильно : NEWSESSION { SHOW form DOCKED; } Внутри любой формы есть кнопки Сохранить и ОК (сохранить и закрыть). Они по сути и сделают APPLY, если пользователь это нажмет. |
|||
450
НиколаевГ
26.10.19
✎
09:41
|
(449) И в каких случаях какой способ применять?
|
|||
451
CrushBy
26.10.19
✎
10:09
|
(450) Если нужно с пользователем в действии взаимодействовать, то первый. Иначе второй.
|
|||
452
CrushBy
26.10.19
✎
10:11
|
(451) Плюс первый предпочтительнее, если не хотите растягивать транзакцию, когда в действии есть сложные вычисления, которые можно сделать вне транзакции.
|
|||
453
Bro
26.10.19
✎
11:09
|
Про сессии этот раздел:
https://habr.com/ru/company/lsfusion/blog/458376/#session Тут все зависит на самом деле хотите чтобы верхняя и открываемая форма вместе свои изменения применяли (отменяли) или по отдельности. |
|||
454
Ещё1
26.10.19
✎
12:42
|
А, ну да, в Фузине транзакция кратковременная, происходит внутри блока в команде APPLY. Чтобы базу не блокировать надолго. Иначе не работало бы одновременное редактирование одного документа разными пользователями. Хотя стоило бы всё же предупреждать пользователя при открытии документа, что его уже редактирует Марьванна с бухгалтерии.
|
|||
455
Bro
26.10.19
✎
13:04
|
(454) Вообще я писал, что лучше не так. Лучше как в гуглдокс, показывать, какие пользователи редактируют, рамочкой показывать, где сейчас фокус у этих пользователей и что недавно изменилось. Но это уже лучшее враг хорошего. Принципиальный момент, что платформа принципиально устойчива к одновременному редактированию.
|
|||
456
НиколаевГ
26.10.19
✎
14:04
|
(455) И в 1С моно сделать так-же, если что.
|
|||
457
Bro
26.10.19
✎
15:45
|
(456) Понятно что и на ассемблере можно так же сделать. Вопрос в том, что в lsFusion для этого делать ничего не надо, там одновременное редактирование "в крови" (по умолчанию).
|
|||
458
Bro
26.10.19
✎
15:47
|
(454) Транзакция в принципе должна быть короткой. По сотне причин. Строго говоря если в транзакции есть любой интерактив или ожидание чего либо еще это уже неправильно.
|
|||
459
Ещё1
26.10.19
✎
22:13
|
Вопрос по настройке планировщика программно. При запуске сервера приложений выполняется такой код (упрощённо):
onStarted() + { NEWSESSION { NEW task = UserScheduledTask { name(task) <- 'Импорт из 1С'; //... NEW td = UserScheduledTaskDetail { active(td) <- TRUE; //... order(td) <- 1; NEW action = Action { caption(action) <- 'importFiles'; canonicalName(action) <- 'import.importFiles[]'; action(td) <- action; } scheduledTask(td) <- task; } APPLY; } } Я вижу, что код точно выполняется, ошибок никаких не выдаёт. Однако задание в планировщике не появляется. В чём здесь проблема? |
|||
460
Bro
26.10.19
✎
22:27
|
(459)
Странно выполняю в lsfusion.org/try у меня появляется задание: REQUIRE Scheduler; showHelloWorld 'Показать сообщение' () { MESSAGE 'Hello world!'; } FORM formHelloWorld 'Форма' PROPERTIES 'Сообщение' = 'Hello world!'; ; NAVIGATOR { NEW showHelloWorld; NEW formHelloWorld; } onStarted() + { NEW d=UserScheduledTask { name(d) <- 'Импорт из 1С'; } } И с NEWSESSION APPLY тоже. А попробуйте просто в интерпретаторе показать форму OBJECTS d=UserScheduledTask PROPERTIES (d) VALUE,name посмотреть создались или нет. То есть без фильтров и всего остального. |
|||
461
Ещё1
26.10.19
✎
22:59
|
Вы по-другому всё-таки делаете.
Если я отключаю создание NEW action то задание нормально создаётся, и в нём появляется 1 строка задания, но с пустым действием. И это понятно, т.к. action я не заполняю. Я уже пробовал так: action(td) <- actionCanonicalName('import.importFiles[]'); и так: action(td) <- propertyCanonicalName('import.importFiles[]'); и даже так: action(td) <- GROUP AGGR ActionOrProperty a WHERE a IS ActionOrProperty BY canonicalName(a); Последнее не скомпилировалось. Когда вручную выбираю это действие из списка, то в столбце "Класс действия" появляется ListAction. Но я не смог найти этот класс в LSF-файлах платформы, чтобы создавать экземпляр именно этого класса. |
|||
462
Ещё1
26.10.19
✎
23:08
|
В метаданных - Действие нужное мне действие есть в группе root - private:
https://paste.pics/a5adf06571625ca30c289eb7bfec901e Но как его выцепить? |
|||
463
Ещё1
27.10.19
✎
01:44
|
OK добавил задание в планировщик пока вручную. Вроде всё прописалось, сохранилось. Планировщик запущен. Но задание не запускается. На закладке "Лог" - пустой. Вручную кнопкой с навигатора задание запускается и отрабатывает. Непонятно...
https://paste.pics/761e772096273f4cad5b97b6a878f672 |
|||
464
Ещё1
27.10.19
✎
01:47
|
Нажимаю "Выполнить задание" в строке задания справа - всё срабатывает отлично.
|
|||
465
Злопчинский
27.10.19
✎
02:56
|
(449) так как в итоге блокировку поставить для других пользователей как в (414) описано - по второму варианту из (449)?
|
|||
466
Злопчинский
27.10.19
✎
02:57
|
а главное меню - верхняя часть - поддается настройке в пользовательском режиме (показать толькт значки, только тест, текст-значки)..? или зашито жестко ? или опять надо "написать одну строку?"
|
|||
467
Bro
27.10.19
✎
08:32
|
(462) У вас Import с маленькой буквы. Язык case sensitive, canonical name'ы тоже. По идее это правильно:
action(td) <- actionCanonicalName('Import.importFiles[]'); С NEW правда непонятно почему не сработало, я проверю чуть позже |
|||
468
Bro
27.10.19
✎
08:34
|
(463) А попробуйте Выполнять при старте, или дату начала проставить. Если это поможет то конечно криво, пофиксим.
|
|||
469
Bro
27.10.19
✎
08:37
|
(465) В модуле Authentication есть уже готовые действия lock, unlock (можете кстати глянуть как они реализованы, если что можно просто их скопировать и сделать более хитрую логику)
lock(object); TRY { IF lockResult() THEN { ... } ELSE MESSAGE 'Locked by user : ' + locked(object); } FINALLY { unlock(object); } |
|||
470
Bro
27.10.19
✎
08:47
|
(466) Смотрите есть вещи которые делает разработчик, есть которые пользователь. Общая идеология - пользователь делает совсем базовые вещи, связанные в основном с особенностями его компьютера (иногда бизнес-процесса, но все равно базовые). То есть акцент на бизнес-приложения, а не "телеграмы и вконтакте". Возможно пользовательскую настройку будем расширять, но не хочется дублировать механизмы. Все же возможности пользователя ограничены его желанием / способностями, а размазывать логику приложения - это создавать проблемы с поддержкой и доработкой. Пока основной фокус lsFusion это не массовые коробки (где можно все решить деньгами), а нишевые коробки и проекты с высокой степенью кастомизации.
|
|||
471
Bro
27.10.19
✎
10:08
|
(461) GROUP AGGR не сработал потому как он неявно создает ограничение, и просто где попало его использовать нельзя, только при объявлении свойства вроде как, так же как и DATA. В таких случаях обычно GROUP MAX, или GROUP NAGGR (без создания ограничения, но что запись одна на совести разработчика)
|
|||
472
Ещё1
27.10.19
✎
11:59
|
(467) Точно, не доглядел. Поменял букву, теперь нормально записывается простой командой:
action(td) <- actionCanonicalName('Import.importFiles[]'); (468) Да, установка запуска при старте помогла. (471) Я потом был переписал эту строку так: action(td) <- [GROUP AGGR Action a WHERE a IS Action BY canonicalName(a)]('import.importFiles[]'); Но естественно она тоже не работала, потому что 'import' с маленькой буквы. OK на данный момент настройка шедулера в приложении работает как планировалось. |
|||
473
Ещё1
27.10.19
✎
12:03
|
Можно ли настроить уровень логирования в планировщике, напр. только ошибки?
Или полностью отключить лог, когда всё отлажено и работает. |
|||
474
Bro
27.10.19
✎
12:17
|
(473) EXTEND FORM scheduler FILTERS error(o), ну или точно не помню что просто наведите на колонке там в tooltip показывается свойство. Ну или FILTERGROUP можете добавить с DEFAULT, чтобы по умолчанию включены только ошибки, в при выключении все.
А что значит полностью отключить лог? Ну пишется и пишется, или что вы имеете ввиду? |
|||
475
Ещё1
27.10.19
✎
12:20
|
(474) Фильтр там уже реализован, под логом птичка "Ошибки или сообщения". Я думал, вообще можно отключить логирование, если оно не нужно. Но на закладке "Настройки планировщика" есть только "Количество потоков".
|
|||
476
Bro
27.10.19
✎
12:29
|
(475) Туда все сообщения из задания пишутся, но непонятно зачем его вообще отключать
|
|||
477
Ещё1
27.10.19
✎
13:27
|
В форме заказа, хочу сделать чтобы при добавлении новой строки OrderDetail по кнопке ( PROPERTIES(od) NEW ) или по клавише Insert, сразу же появлялось окошко выбора номенклатуры ( FORM skusForm ) для этой строки. Возможно ли?
Сейчас при нажатии Insert внизу в списке появляется новая пустая строка, надо щёлкнуть по ячейке Код товара или Товар, чтобы открылся список номенклатуры для подбора. |
|||
478
Ещё1
27.10.19
✎
13:38
|
Можно ли открыть форму заказа в режиме "Только чтение" при указанных условиях (напр., статус заказа - отправлен). Я могу это сделать, добавив на все свойства READONLYIF, а на конпки NEW и DELETE - условие SHOWIF. Но может есть простой способ, который сразу накладывает на все свойства объекта условие READONLY.
|
|||
479
Bro
27.10.19
✎
13:47
|
(477) Action сделайте и повесьте на форму (но да CHANGEKEY и картинку придется прописать)
addDetail(Order o) { DIALOG LIST Sku INPUT s DO NEW od = OrderDetail { sku(od) <- s; order(od) <- o; } } // или (s-имя объекта на skusForm) // DIALOG skusForm OBJECTS s INPUT DO... |
|||
480
Bro
27.10.19
✎
13:48
|
(478) При SHOW / DIALOG есть опция READONLY, можно при показе проверить и показать либо с этой опцией либо без. Ну а так да, READONLYIF
|
|||
481
Михаил Иванович
27.10.19
✎
14:05
|
разговоры сами с собой ведете?
(479) врешь! |
|||
482
Михаил Иванович
27.10.19
✎
14:05
|
(480) Опять врешь!
|
|||
483
Ещё1
27.10.19
✎
14:31
|
(480) Это работает, но требуется небольшая доработка. При открытии формы в режиме READONLY надо дополнительно убирать кнопки NEW, DELETE.
|
|||
484
Bro
27.10.19
✎
14:37
|
(483) А они разве не убираются? Может там они в NEWSESSION?
|
|||
485
Ещё1
27.10.19
✎
14:47
|
(484) Нет, не убираются, и не NEWSESSION. Сейчас я добавил вручную SHOWIF, чтобы убирать кнопки когда не нужны:
PROPERTIES(od) SHOWIF NOT readOnly(o) NEW, DELETE |
|||
486
Bro
27.10.19
✎
14:54
|
(485) не, в понедельник глянем пофиксим. но очень странно так как на тот же механизм политика безопасности read only завязана, и если бы появились эти кнопки нам бы давно сказали.
|
|||
487
Ещё1
27.10.19
✎
14:56
|
(479) Не хотелось бы лезть в макет формы, чтобы поднастроить поведение стандартных действий. А что если как-то так сделать:
WHEN LOCAL SET(OrderDetail od) AND NOT sku(od) DO { DIALOG LIST SKU INPUT s DO { sku(od) <- s; } } Но в этом коде что-то не то с синтаксисом, пишет "Data:158:32 single parameter could not be a parameter of a session operator" |
|||
488
Ещё1
27.10.19
✎
15:07
|
(487) Переделал так, компилируется:
WHEN LOCAL SET(od IS OrderDetail) AND NOT sku(od) DO { DIALOG LIST SKU INPUT s DO { sku(od) <- s; } } Но окно выбора не появляется, и на сервере вызывает StackOverflowError. |
|||
489
Bro
27.10.19
✎
15:15
|
(488) вообще локальные события предметной области асинхронные, и в них интерактивные блокирующие вызовы мы никогда не вызывали. Хотя теоретически не вижу почему они не должны работать, а можете стэк в слэк скинуть?
|
|||
490
Ещё1
27.10.19
✎
15:22
|
(489) Я попробовал пошагово оттрассировать обработку этого события. Тут получается зацикливание, т.е. последовательно исполняется DIALOG LIST SKU, затем вызывается WHEN LOCAL SET, потом снова DIALOG LIST SKU и т.д.
Может как-то нужно подправить условие в WHEN, чтобы избежать этого? |
|||
491
Bro
27.10.19
✎
15:27
|
(490) Ну в общем да логично, она при открытии формы в той же сессии опять вызывает обработку локальных событий. Можно теоретически NEWSESSION сделать, но тогда придется sku через NESTED возвращать (так как в новой сессии не будет виден sku, можно конечно еще NESTEDSESSION попробовать, но это уже совсем жестко):
selectedSku = DATA LOCAL NESTED Sku (); WHEN LOCAL SET(od IS OrderDetail) AND NOT sku(od) DO { NEWSESSION DIALOG LIST SKU INPUT s DO selectedSku() <- s; sku(od) <- s; } Ну и не уверен, что это заработает. Хотя если нет, то интересно почему. Но я бы все же вернулся к более кошерному варианту с явным действием Action. |
|||
492
Bro
27.10.19
✎
15:27
|
(491) * не видно свойство sku в смысле и даже новая строка не будет видна.
|
|||
493
Ещё1
27.10.19
✎
15:35
|
Я решил эту проблему через добавление локального флажка, отслеживающего повторный заход в событие.
inDialog = DATA LOCAL BOOLEAN (); WHEN LOCAL SET(od IS OrderDetail) AND NOT sku(od) AND NOT inDialog() DO { inDialog() <- TRUE; DIALOG LIST SKU INPUT s DO { sku(od) <- s; } inDialog() <- NULL; } Костыльно, но работает. |
|||
494
Bro
27.10.19
✎
15:46
|
(493) Ниразу так не делали. Хотя с другой стороны, ситуации когда диалог вызывается в асинхронных операциях в платформе бывают, так что не вижу почему не должно работать. Тут бы конечно "связь" бы затормозить и потыкать на добавить очень быстро.
А с NEWSESSION заработает? |
|||
495
Ещё1
27.10.19
✎
16:13
|
(494) С новой сессией получилось так:
WHEN LOCAL SET(od IS OrderDetail) AND NOT sku(od) DO { LOCAL newSKU = SKU(); NEWSESSION NESTED LOCAL { DIALOG LIST SKU INPUT s DO { newSKU() <- s; } } sku(od) <- newSKU(); } Выглядит уже покрасивше. Пришлось использовать LOCAL newSKU для промежуточного хранения, потому что если сразу писать в sku(od) <- s; то присвоенное значение не сохраняется. |
|||
496
Bro
27.10.19
✎
18:48
|
(495) да я это и писал выше. Что надо через NESTED LOCAL делать.
Кстати в таких случаях в самом LOCAL лучше указывать NESTED. Он тогда автоматически передастся в NEWSESSION. Ну или в nested указать конкретно к свойство. В любом случае мы так никогда не делали, даже интересно какие подводные камни при этом могут быть. |
|||
497
Ещё1
27.10.19
✎
19:03
|
(496) OK переделал на LOCAL NESTED newSKU = SKU();
Так тоже работает, минус 1 служебное слово. OK |
|||
498
CrushBy
27.10.19
✎
20:55
|
(495) Есть только один момент. В том же ERP мы ни разу так не делали (в локальном событии вызов диалоговой формы). Так что точной корректности гарантировать не могу.
|
|||
499
Ещё1
28.10.19
✎
23:34
|
(354) > VALUE(o) - описывается здесь https://documentation.lsfusion.org/pages/viewpage.action?pageId=1573071
А при обратном импорте из JSON в базу, я могу прочитать ранее записанный идентификатор объекта как число LONG, а как по нему получить сам объект известного класса (скажем Order)? |
|||
500
Ещё1
28.10.19
✎
23:46
|
Допустим:
impID = DATA LOCAL LONG (); // Сюда импортируется ID объекта из JSON LOCAL order = Order (); // Сюда хочу поместить ссылку на сам объект с ID = impID() Пробую разные варианты: order() <- Order(impID()); // Приведение типа не работает VALUE(order()) <- impID(); // Тоже не работает order() <- [GROUP AGGR Order o WHERE o IS Order BY VALUE(o)](impID()); // тоже не берёт |
|||
501
Ещё1
29.10.19
✎
13:27
|
Правильное решение получения объекта по внутреннему идентификатору:
order() <- GROUP MAX Order o IF o IS Order AND LONG(o) == impID(); |
|||
502
Bro
29.10.19
✎
14:06
|
(501) Да есть этот фокус, с тем что указание класса, это еще не означает IS. Хотя строго говоря в данном случаем можно класс не указывать и просто писать GROUP MAX o IF o IS Order AND LONG(o) = impID();
|
|||
503
Ещё1
29.10.19
✎
14:16
|
(502) Ага, untyped implicit parameter declaration. С другой стороны, если lsFusion может вывести тип и параметры переменной из записи запроса, то почему нет.
|
|||
504
Bro
29.10.19
✎
14:39
|
(503) Это не warning. Это подсветка чтобы случайно новые параметры не заводили. lsFusion выводит классы параметры и значений свойств и при неявной ситуации. Просто обычно не рекомендуется не указывать классы параметров, если идет обращение к другим свойством, потому как ambigious рискуете получить, когда появится еще одно свойство с таким именем.
|
|||
505
Bro
29.10.19
✎
14:40
|
* при неявной типизации
|
|||
506
Ещё1
29.10.19
✎
16:24
|
(504) Так а в случае ambigious будет предупреждение? Если да - то не проблема.
|
|||
507
Ещё1
29.10.19
✎
16:36
|
Обновляется ли форма в lsFusion автоматически при изменениях в базе? На примере: в web-клиенте открыта форма со списком заказов, на сервере приложений фоновая задача отрабатывает пришедший от 1С пакет, который обновляет статус заказов. Но у пользователя в Web-клиенте статусы сами не обновляются, надо жать formRefresh. Это так задумано, или недоработка (моя или платформы)?
|
|||
508
Злопчинский
29.10.19
✎
16:40
|
(507) имхо это проблематично.
обновлений в базе столько может быть, что ядро должно постоянно диспетчеризировать и рассылать "уведомления" об обновлении. или клиент (с таймаутом? - это херня полная) |
|||
509
Bro
29.10.19
✎
16:52
|
(507) Да проблематично, по сути надо следить как-то за видимым окном и что именно в нем произошли изменения. Оверхед будет очень большой скорее всего в таком случае.
Для реализации таких вещей обычно AUTOREFRESH используют: https://documentation.lsfusion.org/pages/viewpage.action?pageId=3670145 |
|||
510
Ещё1
29.10.19
✎
17:26
|
(509) AUTOREFRESH отлично, полностью устраивает.
|
|||
511
Злопчинский
29.10.19
✎
17:34
|
(509) при авторефреше - сохранится набитое в текстовых поляпрочих полях которые не изменялись другой тсороной? курсор в поле вврда текста останетяс на той же строке?
|
|||
512
Bro
29.10.19
✎
17:47
|
(511) Естественно.
|
|||
513
Ещё1
29.10.19
✎
20:48
|
Зря вы сделали логины пользователей чувствительными к регистру. Он должен иметь возможность ввести Vasya, vasya или VASYA (Caps Lock "случайно" включился), и система его примет. Меньше разгневанных звонков в службу поддержки. Вот пробелы в начале и конце логина отбрасываете - это правильно.
|
|||
514
Ещё1
30.10.19
✎
11:37
|
Такой код:
NEW ur = UserRole { sid(ur) <- 'admin'; //... mainRole(customUser('admin')) <- ur; } Он компилируется, но в IDEA подчёркивает красным customUser('admin') и пишет Ambiguous reference: mainRole(?) and mainRole(?) match. Я уточняю тип параметра: mainRole(customUser('admin') AS CustomUser) <- ur; Но подчёркивание остаётся. Пробовал приведение к типу: mainRole(CustomUser(customUser('admin'))) <- ur; Но так не компилируется. Как избавиться от этого предупреждения? Можно и так оставить, но я люблю когда код чистый, без предупреждений компилятора. |
|||
515
_DAle_
30.10.19
✎
12:08
|
(514) Подскажите, пожалуйста, если нажать ctrl+b на mainRole в исходном коде, то показываются ссылки на исходные файлы в двух разных .jar файлах, один из которых *sources.jar?
|
|||
516
Ещё1
30.10.19
✎
14:15
|
(515) Вот так: https://paste.pics/da5a90ef9f9a38699a499f77a695e45b
|
|||
517
_DAle_
30.10.19
✎
14:40
|
(516) Избавиться всегда можно с помощью явного указания сигнатуры, в данном случае mainRole[User], но ситуация ненормальная, нужно смотреть.
|
|||
518
Ещё1
30.10.19
✎
14:45
|
Подстановка mainRole[CustomUser](customUser('admin')) <- ur; не убирает подчёркивание.
|
|||
519
Ещё1
30.10.19
✎
14:49
|
https://paste.pics/81cbf7cc50a7b6a2939a2b7dc5e949b5
Меня смущают "?" в предупреждении об ошибке. Такое ощущение что плагин не видит истинных типов параметров в определениях mainRole. |
|||
520
_DAle_
30.10.19
✎
15:15
|
(518) Да, но я не зря написал, mainRole[User], вы же в DATA свойство хотите записать.
(519) Да, похоже, почему-то не видит, надо разбираться. Могу пока посоветовать вызвать File | Invalidate caches / restart, пускай индексы перестроит для начала. |
|||
521
_DAle_
30.10.19
✎
15:33
|
(520) Воспроизвел баг, проявляется, когда системные модули, в том числе и Security.lsf, находятся внутри jar файла, как, видимо, и у вас сейчас. Будем разбираться.
|
|||
522
Злопчинский
31.10.19
✎
00:24
|
Где по типовой методе в фузине предполагается хранить картинки товаров (для веба и для десктопа).
Как от рисовать хранимую картинку на форме? Типа идём по товарам по списку - меняется картинка. |
|||
523
_DAle_
31.10.19
✎
12:24
|
(522) Я, конечно, не очень знаю типовые подходы. Но в исходниках вижу, например, вот такое применение:
image 'Изображение' = DATA IMAGEFILE (Article, Color); То есть изображения хранятся в базе. Реализуется это с помощью свойства, которое возвращает объект класса IMAGEFILE. В данном случае выбор картинки зависит от артикула и цвета. Потом нужно будет добавить это свойство на форму, обычно в отдельную панель. |
|||
524
Bro
31.10.19
✎
12:30
|
(522)
image = DATA IMAGEFILE (Sku); FORM skus OBJECTS s=Sku PROPERTIES (s) image PANEL ; Когда будете ходить по товарам картинка будет сама обновляться. Хранятся прямо в базе. |
|||
525
Ник080808
31.10.19
✎
12:33
|
(524) если нужно прикреплять несколько картинок к товару? к строке документа?
|
|||
526
CrushBy
31.10.19
✎
12:43
|
(525) Несколько картинок :
CLASS Image; sku = DATA Sku (Image) NONULL DELETE; file = DATA IMAGEFILE (Image); Прикрепление к строке документа : image = DATA IMAGEFILE (DocumentLine); |
|||
527
Ник080808
31.10.19
✎
12:47
|
(526) по какому ключу связывается картинка и строка? по номеру строки?
|
|||
528
CrushBy
31.10.19
✎
12:52
|
(527) Смотрите, IMAGEFILE - это просто примитивный тип. Вроде строки. В базе данных она хранится как byte array (то есть просто бинарным файлом). Соответственно просто в строке появляется новое поле. Как правило, мы складываем их в отдельную таблицу, чтобы не раздувать основную таблицу со строками :
TABLE documentLineImage(DocumentLine); image = DATA IMAGEFILE (DocumentLine) TABLE documentLineImage; |
|||
529
Ещё1
31.10.19
✎
13:15
|
Непонятно, как показать в карточке товара все его картинки. Например, автомобиль, снятый с разных ракурсов. Тут можно разную логику придумать, например по-умолчанию выводится 1-я прикреплённая картинка. Щёлкая по картинке - открывается новое окно с галереей картинок по товару.
|
|||
530
Bro
31.10.19
✎
15:02
|
(529) Можно либо в таблице сделать и увеличить размер ряда, либо что логичнее объектами-в-колонки:
image = DATA IMAGEFILE (Car, LONG); FORM carWithPhotos OBJECTS c=Car, l=LONG FILTERS image(c, l) // фильтруем только номера с картинками PROPERTIES image(c,l) COLUMNS (l) HEADER 'Фото ' + l ; По идее должно сработать |
|||
531
Ещё1
31.10.19
✎
15:08
|
(530) Т.е. в таблицы можно не только текст выводить, но и картинки?
|
|||
532
Злопчинский
31.10.19
✎
16:34
|
(470) я согласен, но настройки интерфейса - зачем на них разработчика тащить. если у меня на ноуте по вертикали 800 пикселей экран - мне бы верхнее меню вообще значки убоать и оставить только текст.
. аналогично - левая панель перечень справочников\документов - хорошо бы разные пиктограммы иметь и убрать текстт - я по горизонтали сэкономлю... |
|||
533
Злопчинский
31.10.19
✎
16:37
|
(531) не, в таблицы картинкт колонками выводить это в отчет хорошо. а на экране - как показать ленту картинок для текущей строки таблицы\списка?понятно что в ленте может быть переменное колво картинок для разных строк списка.
|
|||
534
Bro
31.10.19
✎
16:38
|
(531) ну все можно везде выводить. Строго говоря, с точки зрения реализации свойство в панели это таблица из одного ряда, одной колонки, без заголовка. Вопрос что у разных типов разные рендереры. У картинок это отображение картинки.
|
|||
535
Bro
31.10.19
✎
16:39
|
(533) для этого объекты-в-колонки использовать. То есть по сути кросс-таблицы, просто в панели эта таблица из одного ряда.
|
|||
536
Ещё1
01.11.19
✎
01:42
|
Воспользовался предложением CrushBy в соседней ветке ( OFF: lsFusion vs 1C. Раунд 7 (37) ) и переделал обновление справочника товаров, как приведено в примере №3 https://documentation.lsfusion.org/pages/viewpage.action?pageId=46367614
И сравнил по скорости с первоначальным решением "в лоб". В качестве теста делал 3 загрузки каталога: сначала 1-ю половину, потом 2-ю, а потом целый каталог товаров. Действительно, загрузка ускорилась практически ровно в 2 раза. |
|||
537
CrushBy
02.11.19
✎
11:17
|
К сожалению, оптимизация получилась не полной. В общем случае FOR может скомпилироваться в один SQL запрос (а не выполняться на сервере приложений), если в нем нет IF'ов, других FOR'ов и строки записи внутри его не зависят друг от друга. У Вас же получается, что внутри пробега создаются группы, производители и прочие. Для оптимизации нужно вынести создание справочников за пределы FOR'а.
Например, с импортом групп снаружи FOR будет другой цикл по созданию несуществующих групп : FOR [GROUP MAX INTEGER i BY groupName(i)](ISTRING[100] name) AND NOT groupByName(name) NEW g = Group DO { name(g) <- name; } GROUP MAX тут используется потому, что идет импорт из денормализованной таблицы, и одна группа в строках встречается несколько раз. Соответственно, создавать группу нужно один раз. Потом уже при заполнении свойств sku там останется только group(u) <- groupByName(groupName(i)); так как все группы уже созданы в предыдущем FOR'е. Это все приведет к тому, что все пойдет одним запросом и импорт должен ускориться во много раз. |
|||
538
Bro
02.11.19
✎
12:23
|
(537) IF'и и FOR'ы тоже отлично компилируются в "один запрос" (ELSE'ы только пока не компилируются). Главное чтобы зависимостей читающих свойств от свойств в которые пишется не было. Хотя там можно INLINE в FOR поставить и тогда :
FOR f(a) INLINE DO g(a) <- h(g(a)); Скомпилируется в WHERE (то есть один запрос): g(a) <- h(g(a)) WHERE f(a); принудительно. Но нужно понимать, что при этом может поменяться логика (собственно поэтому платформа этого и не делает). INLINE пока единственная (или одна из 2-3) опция которая не описана в документации, так как для этого нужно подробнее описывать как все работает. Это мы конечно тоже сделаем, но чуть позже. |
|||
539
Ещё1
02.11.19
✎
18:10
|
Я собирался вынести обновление вспомогательных справочников отдельно. Но почему-то показалось, что несколько пробегов типа FOR groupName(INTEGER i) ... по загруженным данным будут хуже 1 пробега FOR id(SKU u) == skuID(INTEGER i), пусть с проверками внутри. А надо было проверить, разница очень заметная. Если до изменений тест пробегался за 16145 мс и 14379 мс (1-я и последующие итерации), то после выноса обновлений справочников - 2688 мс и 1743 мс соответственно. Выигрыш в 6-8 раз. Получается импорт стал быстрее в 12-16 раз по сравнению с решением "в лоб".
Может, немного сбил синтаксис локальных свойств, что вместо groupName = DATA LOCAL ISTRING[100] (INTEGER); нужно писать LOCAL groupName = ISTRING[100] (INTEGER); Я протестировал сейчас 4 варианта: с циклами типа FOR groupName(INTEGER i)..., циклы FOR [GROUP MAX INTEGER i BY groupName(i)](name), и те же циклы с добавлением INLINE. По скорости они все примерно одинаковы. Может только FOR groupName(INTEGER i)... без INLINE чуть медленнее, но нужны значительно большие тестовые справочники, чтобы утверждать точно. С INLINE мне не совсем понятно. Есть ли небольшое ускорение, или и без INLINE компилятор нормально справился в данном случае. Небольшое наблюдение. В операторе [GROUP MAX INTEGER i BY groupName(i)](ISTRING[100] name) Я бы предпочёл не указывать тип параметра ISTRING[100]. И я вижу, язык это позволяет. Причина: при изменении размерности или типа исходного свойства (например на STRING[200]) при явном указании типа в (ISTRING[100] name) придётся вручную отыскать все такие использования и откорректировать. |
|||
540
tty12
02.11.19
✎
18:46
|
(538) (537)
Может уже спрашивали, но я ответа не видел... И все с начала читать лень. Конкретный, простой вопрос. Вы зарубу (задачи в ней) сделали? На котрую Вы подписались! Где решение? Под ТЗ решение, а не под оду... У вас есть ответ? PS: Я вижу один и тот же ответ - 1. "Три сточки дописать." - это всегда!!! 2. "Джун косячит..." - хотя Краш клялся за ним следить, "как за собой" - его цитата. 3. "Краш оду пишет..." - нахер она кому нужна тут???? Речь не про оду... 4. А надо ли продолжать? Фузинаторы - Вы дурачки? |
|||
541
CrushBy
03.11.19
✎
13:45
|
(539) Я тут немного понял вот что, делать вот так вроде как нельзя :
FOR groupName(INTEGER i) AND NOT groupByName(groupName(i)) NEW g = Group DO { name(g) <- groupName(i) } Поскольку, если это не будет скомпилировано в один запрос, то сначала вычисляется выражение, а затем идет по нему итерирование уже на сервере приложений. Так как таблица денормализованна, то создастся несколько групп с одинаковым именем и сохранение не пройдет по CONSTRAINT. Поэтому в моем примере шел GROUP MAX. В целом важно понимать, что нужно стремиться, чтобы все компилировалось в SQL запрос, так как выполнение на сервере БД значительно быстрее чем round trip между сервером приложений и сервером БД. [Может, немного сбил синтаксис локальных свойств, что вместо groupName = DATA LOCAL ISTRING[100] (INTEGER); нужно писать LOCAL groupName = ISTRING[100] (INTEGER);] Эти два объявления с точки зрения выполнения идентичны. Разница в том, что второе объявление видно только в пределах действия, а первое - в пределах модуля и зависимых от него модулей. [С INLINE мне не совсем понятно. Есть ли небольшое ускорение, или и без INLINE компилятор нормально справился в данном случае.] Итерирование на большом объеме в любом случае медленнее, если с INLINE они одинаковы, то или INLINE не сработал и запрос не скомпилировался, или наоборот, исходный скомпилировался. Вообще, с точки зрения выполнения это хорошо видно по "бегунку" в GUI. При итерировании будет идти бегунок, когда выполняется SQL запрос, то никакого бегунка быть не может, так как SQL запрос не возвращает данные о статусе своего выполнения в данный момент. [Я бы предпочёл не указывать тип параметра ISTRING[100]]. Да, там можно не указывать. Платформа должна в таком случае вывести тип сама (если получится). |
|||
542
Salimbek
08.11.19
✎
16:58
|
Тут Конкретные вопросы по lsFusion
Это: // можно сделать хитрее, i2 закинуть в колонки выдаст матрицу 10x10 в ней будут значения REQUIRE Utils; // Моя добавка, т.к. iterate в utils FORM a OBJECTS i1=LONG PROPERTIES VALUE(i1) OBJECTS i2=LONG PROPERTIES a(i1,i2) COLUMNS (i2) HEADER (i2) FILTERS iterate(i1,1,10), iterate(i2,1,10) ; run() { a(1,2) <- 5; a(4,2) <- 7; a(1,3) <- 9; SHOW a; } Пробую выполнить код на lsfusion.org/try - открывается пустое приложение, SHOW a не срабатывает? Ну ок, запускаю IDEA, копирую туда этот код, в ответ: ERROR StartLogger - Exception while starting logics instance: [error]: Main:11:5 single parameter is forbidden in this context Subsequent errors (if any) could not be found. Как я понял - это оно на HEADER (i2) ругается, но как и где поправить? |
|||
543
Ёпрст
08.11.19
✎
18:12
|
(542) тестиь платформу ? Я чегой-то подзабил, хотя и развернул и демку с ерп даже запустил, лень
|
|||
544
Bro
08.11.19
✎
18:32
|
(542) Вообще сейчас не у компьютера, но по идее можно написать HEADER (i2+1-1) :) То есть ей не нравится в этом месте параметр, там да косяк был в этом месте. На самом деле в 2.1 это уже пофикшено ЕМНИП и даже уже релиз ее был. На сайте ссылки новые на нее не добавили. Хотя на download.lsfusion.org уже есть.
|
|||
545
CrushBy
08.11.19
✎
18:51
|
(542) Если речь идет о запуске в Try, то вот модифицированный код, который работает :
REQUIRE Utils; // Моя добавка, т.к. iterate в utils a = DATA LONG (LONG, LONG); FORM a OBJECTS i1=LONG FILTERS iterate(i1,1,10) OBJECTS i2=LONG PROPERTIES a(i1,i2) COLUMNS (i1) HEADER (i1) FILTERS iterate(i2,1,10) ; NAVIGATOR { NEW a; } |
|||
546
Bro
08.11.19
✎
19:13
|
(542) а понял теперь. На Try уже свежая версия 3.beta.0 (поэтому на HEADERS (i2) не ругается), а в инсталляторе вы 2.0 ставили где да была такая проблема.
А не запускается потому как run() в режиме платформы ничего не значит, это вы просто объявили действие с именем run. Оно играет роль в режиме СУБД (а точнее оператора EVAL или HTTP вызова eval например, который и выполняет EVAL) - там выполняется именно действие с именем run. https://documentation.lsfusion.org/pages/viewpage.action?pageId=4915350 https://documentation.lsfusion.org/pages/viewpage.action?pageId=51216539 |
|||
547
Salimbek
12.11.19
✎
14:52
|
(546) И как обновиться с 2.0 до "3.beta.0" или чего там у вас самое новое?
|
|||
548
Bro
12.11.19
✎
15:13
|
(547) Последние snapshot версии вот тут (файлы с постфиксом assembly, это jar файлы все-в-одном вместе с зависимостимя ):
Сервер: https://repo.lsfusion.org/nexus/service/rest/repository/browse/public/lsfusion/platform/server/3.beta.0-SNAPSHOT/ Десктоп-клиент: https://repo.lsfusion.org/nexus/service/rest/repository/browse/public/lsfusion/platform/desktop-client/3.beta.0-SNAPSHOT/ Веб-клиент: https://repo.lsfusion.org/nexus/service/rest/repository/browse/public/lsfusion/platform/web-client/3.beta.0-SNAPSHOT/ Инструкции тут в разделе Обновление: https://documentation.lsfusion.org/pages/viewpage.action?pageId=57738078 Пока 3.beta.0 не выпускали, но в ближайшее время выпустим, тогда просто в download.lsfusion.org появится. |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |