netros wrote:

Речь идёт именно о таблице табель: мне не хочется создавать 31 поле, по количеству дней.

Кроме ID, в табеле всего два поля:

  • id_person  - ссылка на сотрудника

  • date_ - дата выхода на работу

А вот заполнять данные в табличку на экране для привычного визуального представления (календарь или табельный лист) придётся скриптом, а для печатной формы сделать временную таблицу.

netros wrote:

для табелирования народа.
Т.е. просто проставить в какую смену кто когда вышел
А вытащить - просто пройтись циклом по строке

Рекомендую создать 2 таблицы: работники и табель выхода на работу. Всякие "оптимизации" с запихиванием данных в одно поле на пользу не пойдут, а все вычисления лучше делать не скриптами, а SQL-запросами.

Экспертные системы подобного типа делаются на основе бинарного дерева: пользователю по очереди задаются вопросы, отвечая на которые "да" или "нет" программа перемещается по дереву вариантов. Когда достигнут конечный узел, программа сообщает свое решение. Пользователь либо соглашается, либо отрицает решение системы. В этом случае система предлагает ввести дополнительный вопрос, чтобы добавить ещё одно ветвление в дереве и новый ответ (или два новых ответа).
Для реализации подобной системы понадобится следующая структура дерева вопросов

  • ID - идентификатор (создаётся автоматически)

  • Node_text - текст вопроса или текст ответа

  • ID_positive - идентификатор узла для перехода при положительном ответе

  • ID_negative - идентификатор узла для перехода при отрицательном ответе

Сам алгоритм реализуется скриптом, из элементов интерфейса понадобится Label (для отображение текущего вопроса или ответа) и две кнопки (Да/Нет).

netros wrote:

проще говоря месячный календарь в одно поле, или для каждого числа всё-таки придётся создавать своё поле?

Запихать можно, но будет ли удобно вытаскивать? smile
Уточните вашу бизнес-задачу. Для чего нужен календарь?

Схема структуры предприятия понятна, но непонятно, зачем её реализовывать smile Чтобы определить, кто кому подчиняется и у кого сколько подчиненных? Тогда - да, это нужно.

Что мы имеет на схеме? Иерархический список должностей, в котором некоторые должности представлены группами (названием отдела), которые не должны выбираться в качестве должности сотрудника а служат только для организации иерархической структуры. На практике это дерево расширяется на уровне отделов конкретными должностями: монтажник, штукатур, менеджер, бухгалтер и т.д. Тогда получаем структуру:

  • ID - идентификатор (поле создаётся автоматом)

  • Parent_ID - родительский элемент

  • Name - название должности или отдела

  • isGroup - признак отдела

При добавлении сотрудника ему присваивается должность, отдел назначать нельзя, но его можно определить запросом.

Илья Б. wrote:

...как защитить script.dcu от просмотра пользователем?

Защитить файл script.dcu от чтения нельзя, но можно замаскировать текст логина/пароля (или другие конфиденциальные данные). Напишите простую функцию преобразования строки (перестановки и/или замены символов), чтобы в исходнике не хранились данные для подключения в явном виде.

Попробуйте сделать так:

  • Разместить PageControl в верхней части формы, уменьшив его размер так, чтобы отображались только закладки.

  • Ниже разместить TableGrid

  • Добавить скрипт генерации закладок

  • Добавить скрипт фильтрации при нажатии на закладку

Вот пример:

 procedure CreateTabMenu;
var
  Menu: TDataSet;
  TabSheet: TTabSheet;
  s: string;
begin
  // типы отчётов
  frmMain.pgcFinStatType.Pages[0].Free;
  s := 'select * from fin_stat_type order by id;';
  SQLQuery(s,Menu);
  while not Menu.Eof do
  begin
    TabSheet := TTabSheet.Create(frmMain);
    TabSheet.Caption :=  Menu.FieldByName('name').asString;
    TabSheet.PageControl := frmMain.pgcFinStatType;
    Menu.next;
  end;
  frmMain_pgcFinStatType_OnChange (frmMain.pgcFinStatType);
end;

Фильтрация данных настроена как функция "[ПОИСК]" в кнопке frmMain.btnFinStatUpd

procedure frmMain_pgcFinStatType_OnChange (Sender: TObject);
begin
  // установить фильтр по типу статистики
  frmMain.cmbFinStatType.dbItemID := frmMain.pgcFinStatType.ActivePageIndex+1;
  frmMain.btnFinStatUpd.Click;
end;

Пример сделан с допущением, что ID в таблице fin_stat_type идут последовательно: 1,2,3..   Если это условие не соблюдается (например, если записи удаляются), то можно записывать ID записи в свойство  TabSheet.tag, а затем передавать его в фильтр.

wertyby wrote:

Дмитрий никак не могу понять как активировать вертикальный скролл на форме по вертикали. По горизонтали включается но он не нужен. а вертикальный никак?

Предполагаю, что речь идет не о скроле формы, а о скроле записей таблицы при наведении мыши и кручении колесика. Для себя решил это тем, что в событии OnMouseEnter для таблицы передаю ей фокус:

procedure Form1_TableGrid1_OnMouseEnter (Sender: TObject);
begin
  Form1.TableGrid1.SetFocus;
end;

Как выяснилось, это не совсем удобно, особенно если на этой же форме есть поля ввода данных (например, фильтр).

Хотелось бы услышать лучшее решение.

max1779signal wrote:

...но мне надо узнать этот id еще до сохранения.

Предполагаю, что реализованный в MVDB механизм формирования ID записей обеспечивает целостность данных и нормальную работу СУБД, а Ваша прикладная задача, возможно, имеет другое, более безопасное решение.

Если не секрет, для чего может понадобиться ID недобавленной в базу записи?

netros wrote:

     UpdateDatabase('таблица');

В каких случаях необходимо использовать эту команду?

1,436

(4 replies, posted in Russian)

Bullet3203 wrote:

...При нажатии кнопки отправить, у получателя было уведомление что ему пришло сообщение и запрос на передачу файлов. Сам файл автоматом сохранялся в базе, но после получение его пользователем он бы с базы удалялся. Возможно ли такое?)

Можно по таймеру (например, раз в минуту или чаще) проверять, не появилась ли новая запись в базе. А после выгрузки (сохранения) файла -  удалять запись.

Скорее всего, вопрос имеет отношение к системе защиты программы. На форуме есть куча примеров, которые используют метки времени для ограничения функциональности демо-версий, но всё это легко обходится переустановкой времени на локальной машине. Если же время/дату запрашивать по сети, то надёжность этого элемента защиты будет выше.

mcsimm wrote:

Может через систему это как-то делать, например, через службу времени Windows :
w32tm /stripchart /computer:time.windows.com /samples:1 /dataonly > temp.txt,

Кстати, неплохой вариант cool
Непонятно только, как запустить выполнение этой строки из приложения MyVisualDatabase...

1,438

(4 replies, posted in Russian)

Bullet3203 wrote:

Здравствуйте! Возможно ли реализовать передачу файлов конкретному пользователю? (работа в локальной сети)

Несколько экстравагантное решение, но для небольших файлов вполне применимое: записывайте файлы в базу данных; с помощью системы прав доступа настройте ограничения доступа к записям для обеспечения приватности.

http://f6.s.qip.ru/pgTu2DfP.png

В общем случае нужно добавить обработчик на событие PageControl.onChange . Дальнейшие действия зависят от структуры данных. Если ваша база нормализованная, то типы продукции - это справочник, а в  таблице с продукцией хранится ссылка на него. Тогда обработчик может быть таким:

procedure frmMain_pgcFinStatType_OnChange (Sender: TObject);
begin
  // установить фильтр по типу 
  frmMain.cmbFinStatType.dbItemID := frmMain.pgcFinStatType.ActivePageIndex+1;
  // обновить таблицу 
  frmMain.btnFinStatUpd.Click;
end;

В этом примере устанавливаем ID равным номеру страницы + 1, то есть не по названию, а по ID. А затем нажимаем кнопку, в которой реализован метод фильтрации данных.

1,440

(33 replies, posted in Russian)

MessageDlg выдаёт ошибку:

http://f2.s.qip.ru/SwgAVREX.jpg

begin
  if MessageDlg('Произвести загрузку из текстовых файлов?',mtWarning,[mbOK,mbCancel],0)=mrOK then
  begin
    ShowMessage('!');
  end;
  ShowMessage('!!!');
end.

Сохранение имён визуальных компонент при копировании их через буфер обмена. Сейчас при вставке имена компонентов теряются (устанавливаются по умолчанию), а так как скрипты привязываются к конкретным названиям, то при переносе формы в другой проект много времени теряется на переименование компонентов.

DriveSoft wrote:

В вашем случае необходимо подключиться к MySQL с помощью скрипта, таким образом вы сможете сделать свой диалог подключения к БД, а также выполнить необходимые SQL запросы, чтобы проверить наличие БД на сервере и при необходимости создать ее...

Я подключаюсь к БД с помощью скрипта. Однако, не хотелось бы дублировать информацию о структуре базы в скриптах - это действительно неудобно, так как при изменении структуры нужно будет каждый раз править эти самые скрипты.

Либо мне придётся "изобретать велосипед"  - свой скрипт, который из tables.ini будет создавать аналогичную структуру в базе MySQL. Но мне хочется использовать проверенное, рабочее решение.

DriveSoft wrote:
SQLExecute('CREATE DATABASE IF NOT EXISTS `DBName`;');

Этот скрипт создаёт базу в случае её отсутствия. Но мне нужно только определить её наличие или отсутствие. Ушёл читать описание команд SQL для управления БД...

DriveSoft wrote:

...могу попробовать добавить функцию в скрипт, которая бы создавала структуру без необходимости использовать SQL.

Да, это было бы самым простым (для разработчика прикладной системы) решением smile 


P.S.
Уважаемые разработчики приложений платформы MyVisualDatabase!
А как вы создаёте дистрибутивы приложений для MySQL?

Как с помощью скрипта:

1) определить, что нужная база отсутствует на сервере
2) создать нужную базу
3) создать структуру таблиц, нужных для работы приложения
4) загрузить в таблицы данные (этот пункт, в принципе, понятен, внесён для полноты картины)

Или как отобразить это окошко, в котором реализованы пункты 1-3 и которое есть в среде разработки:

http://f3.s.qip.ru/SwgAVREo.jpg

Всё это очень нужно для настройки приложения у конечного пользователя, который не купил среду разработки и исходники программы.


Конечная цель - создание дистрибутива пользовательского приложения. Хотелось бы обойтись только средствами MyVisualDatabase и пока бесплатной Inno Setup, как описано здесь: http://myvisualdatabase.com/forum/viewt … 249#p19249

1,444

(4 replies, posted in Russian)

http://myvisualdatabase.com/forum/viewt … 754#p22754

mcsimm wrote:

Накидал в редакторе и Пейнте примерный вид такой настройки

Пожалуй, добавлю настроек smile

...действительно, файл не загружается  sad

http://f1.s.qip.ru/SwgAVREk.jpg

Мне видится это так: после создания стандартного sqlite.db средствами MyVisual Database его можно переименовать в MyFile.db средствами операционной системы. А затем использовать по мере необходимости.


Подключение:

SQLExecute('ATTACH DATABASE ''d:\testDB.db'' as ''TEST''');

Использование

SELECT TEST.person.firstname, TEST.person.lastname FROM TEST.person

но при этом ваш проект должен использовать SQLite в качестве основной СУБД


Подсмотрено здесь: http://myvisualdatabase.com/forum/viewtopic.php?id=1804

jonibek wrote:

Для чего нужна рекурсия и что даст данный код, объясните чайнику.

Рекурсия - это приём в программировании, когда функция или процедура вызывает саму себя.


function load(var A:integer):integer;
var
  i: integer;
begin
  i := A;
  if A<5 then
    result := load(A+1) + i
  else
    result := A;
end;


У меня была гипотеза, что FastScript некорректно работает с рекурсивными функциями или процедурами. Данный код опроверг эту гипотезу, и уважаемый iacovlogica так же подтвердил, что с рекурсией всё в порядке. Никакой другой пользы от данного кода нет smile

А вот использование в рекурсивной процедуре функции SQLQuery() приводит к фатальной ошибке, о чем, собственно и весь этот топик smile

1,448

(2 replies, posted in Russian)

Конечно, возможно smile  Например, так:
http://f2.s.qip.ru/SwgAVRCU.jpg

iacovlogica wrote:

...Глобально объявить массив датасет...

Интересный вариант, но, скорей всего результат будет таким же smile
Прошлым вечером я поставил кучу экспериментов и в результате выяснил, что любое сочетание SQLQuery() и рекурсивного вызова функций или процедур фатально. Не знаю конкретно, в чём там дело, скорей всего в особенностях работы FastScript.

Если гора не идёт к Магомету, то... меняем структуру данных:

http://f2.s.qip.ru/SwgAVRCJ.png

Теперь можно обойтись без рекурсии, пожертвовав удобностью настройки меню. Структура меню определяется строковым полем LEVEL (Уровень). Обычно это поле заполняется на триггерах БД на основе id_parent и order_num, но можно и ручками внести smile

http://f4.s.qip.ru/SwgAVRCL.png

Код получился сравнительно простой, загружает до 10 уровней вложенности (на практике больше трёх уровней вложенности в меню не встречал)

// загрузка за один присест
procedure LoadMenu;
var
  Results: TDataSet;
  // до 10 уровней вложенности меню!
  ParentList: array[0..9] of TMenuItem;
  miChild: TMenuItem;
  sForm: string;
  iCurLevel: integer;
  index: integer;
  s: string;
begin
  S :='SELECT main_menu.name, main_menu.form, main_menu.level FROM main_menu '+
    'left join menu_roles on menu_roles.id_main_menu = main_menu.id '+
    'WHERE (menu_roles.id_roles='+inttostr(RoleID)+') order by level';
  SQLQuery(S,Results);
  while not Results.Eof do
  begin
    sForm := Results.FieldByName('form').AsString;
    iCurLevel := length(Results.FieldByName('level').AsString)-1;
    miChild := TMenuItem.Create (MainMenu);
    if iCurLevel = 0 then
    begin
      MainMenu.Items.Add(miChild);
    end
    else
    begin
      TMenuItem(ParentList[iCurLevel-1]).Add(miChild)
    end;
    ParentList[iCurLevel] := miChild;
    //
    miChild.Caption := Results.FieldByName('name').AsString;
    // к пункту меню привязана либо форма, либо вложенные пункты меню
    if sForm<>'' then
    begin
      miChild.OnClick := @MenuItemClick;
      index := FormList.IndexOf(sForm);
      if index>=0 then
        miChild.Tag := index
      else
      begin
        ShowMessage('Wrong form name: '+sForm)
      end
    end;
    Results.Next;
  end;
end;

Результат радует:

http://f5.s.qip.ru/SwgAVRCK.png