пятница, 27 апреля 2012 г.

Еще один большой привет программистам МТС!

Какие НЕ нужно писать программы


Меня уже удивляли программисты, работающие на МТС, но сегодня коллекция моих удивлений пополнилась еще одним экспонатом.

При попытке изменить набор услуг с помощью Интернет-помощника, а точнее - отключить услугу "БИТ 2011", мне было выдано сообщение:

"Запрос не выполнен. На номере телефона имеются незавершенные операции..."



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

Начинаю долгий поиск по меню, отыскивая возможность посмотреть, чем я так провинился перед МТС.

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


Обратите внимание: программа сообщает мне о невыполненном переходе на тариф "MAXI", показывая справа одновременно, что моему номеру сопоставлен именно этот тариф.

И еще очень "хорошо", что случилось это совсем недавно - 25 октября прошлого 2011 года, т.е. 5 месяцев назад!!!

Молодцы, коллеги из МТС! Так держать!!!

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

четверг, 26 апреля 2012 г.

Урок 41. Графическое представление информации

В этом уроке мне хочется начать тему, которой я не касался ранее, а именно: рассказать о графическом представлении информации.

Любой бухгалтер скажет: "Мне это зачем? Мне Журнал хозяйственных операций дай и Главную книгу".

Но, во-первых, мы пишем не бухгалтерскую, а очень очень похожую программу.
Во-вторых, у нас уже есть нечто, отдаленно напоминающее ЖХО. Не хватает отчета в удобной форме - сделаем. У нас есть замечательная оборотка, из которой только ленивый не построит Главную книгу.
В-третьих, графическое представление информации удобно в тех случаях, когда нужно быстро проанализировать огромный массив данных, чтобы принять какое-то конструктивное решение.
В-четвертых, интереснее рассказывать о чем-то, о чем еще не рассказывал...

Надеюсь, что я привел убедительные доводы в поддержку своих же намерений :-)

среда, 25 апреля 2012 г.

Урок 40. Кодификаторы

Создавать первые базы данных приходилось в условиях полного отсутствия литературы. Я уже не говорю про переведенные на русский язык книги... Вообще не было никаких. Я помню, как писал запрос в представительство Майкрософта: посодействуйте в получении руководства по MS Access...

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

И вывел я для себя вот такую формулу много лет назад:

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

среда, 18 апреля 2012 г.

Немного юмора...


Сидят два админа, грусные тупо смотрят в монитор,

Заходит третий

-Чего такие грусные?

-Да вчера пиво пили… и пароли меняли…


***




Ушел сисадмин в отпуск. Через неделю на мобилу звонит начальник отдела:

- Андрей извини, что побеспокоили. Срочно надо в компе информацию найти. Скажи пожалуйста пароль...

- Как вы меня все з@@бали. Без пробелов и с маленькой буквы.


***




Рaзговор в чaте:

- Помогите, уменяпробелнерaботaет!

Ответ:

- Нaстоящему_прогрaммисту_пробел_не_нужен!


***

четверг, 12 апреля 2012 г.

Сам нашел опечатку в заголовке 33 урока

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


Приношу свои извинения.




среда, 11 апреля 2012 г.

Урок 39. "Товары", сопутствующие Insert

Наверное, те, кто пытается следить за ходом моих публикаций, уже имеют более ни менее работоспособную программу "Расходы", но иногда натыкаются на разные неожиданные неприятности, не позволяющие полноценно использовать ее в качестве инструмента учета Например, прошел месяц март, наступил апрель, Вы пытаетесь добавить новую строчку в список мемориальных ордеров, но...

при попытке сохранения пустой строчки происходит ошибка...


написали "Апрель", но не ввели номер месяца - другая...


Если пользователь - Вы сам, то беды большой нет, поскольку Вы знаете в чем тут дело и как поправить. Но я стараюсь как можно чаще давать понять моему читателю, что пугать неподготовленного пользователя служебными сообщениями есть моветон. Как плохой манерой является и исполнение маневров, подобных тем, что исполняют в МТС.

Чтобы избежать этих мелких неприятностей, нужно в момент добавления записи в соответствующую таблицу, присвоить полям стартовые значения.

Найдите на форме компонент ADOTABLEMOS и напишите обработчик свойства AfterInsert:

procedure TMainFrm.ADOTableMOsAfterInsert(DataSet: TDataSet);
var
  year, month, day: Word;
  monthName:array[1..12] of string[8];       // Объявление одномерного массива фиксированного размера
begin

  // Присвоение начальных значений элементам массива
  monthName[1]:='Январь';
  monthName[2]:='Февраль';
  monthName[3]:='Март';
  monthName[4]:='Апрель';
  monthName[5]:='Май';
  monthName[6]:='Июнь';
  monthName[7]:='Июль';
  monthName[8]:='Август';
  monthName[9]:='Сентябрь';
  monthName[10]:='Октябрь';
  monthName[11]:='Ноябрь';
  monthName[12]:='Декабрь';

  // Декодирование текущей даты  Date() функцией  DecodeDate(). Результат - в переменных year, month, day
  DecodeDate(Date(), year, month, day);

  // Добавление значений по умолчанию в список мемориальных ордеров
  ADOTableMOs.FieldByName('Mes').Value:= IntToStr(month);
  ADOTableMOs.FieldByName('Name').Value:= monthName[month];  // Значение из массива по номеру месяца

end;


Теперь при добавлении новой записи программа поможет оператору:


Вы можете смело продолжить работу, а я попутно хочу заметить
1. Массив monthName можно объявить не в этой процедуре, а выше - на уровне модуля, сделав его не локальным, а глобальным по отношению к данному модулю, если его значения  необходимы в других процедурах. Тогда объявление массива можно было бы совместить с присвоением его элементам значений:

monthName: array[1..12] of String[8]=
('Январь','Февраль','Март',и т.д. до,'Декабрь');



2. Алгоритм "угадывания" названия месяца по его номеру можно вынести в DLL в виде отдельной функции, так как он может понадобится не только в этой программе, но и в других.

3. На основе этого несложного алгоритма  можно сделать компонент, на входе которого будет номер месяца, а на выходе - его название.

Все хорошо, пока пользователь не стер одно из подставленных данной процедурой значений. 
В такой ситуации ошибки повторятся. 

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



Помните, я говорил в уроке 38, что обработчик ошибок нужен при выполнении Post? Давайте поправим ситуацию, пока не поздно.

Займемся этим оператором. А точнее - событием, предшествующим сохранению введенных в Grid значений в источнике, под сеткой подложенной таблице. Событие это носит название BeforePost. Именно в этот момент, перед выполнением операции сохранения данных, уместно сделать проверку на предмет: "Подходят ли нам введенные значения?"

Для этого напишем вот такой обработчик события BeforePost

procedure TMainFrm.ADOTableMOsBeforePost(DataSet: TDataSet);

  function TheTestMos(): Boolean;
  var
    MyStr: String;
    p: boolean;
  begin

    p:= True;            // Признак успешности проверки

    MyStr:= ADOTableMos.FieldByName('Name').AsString;
    if (MyStr='')
    then
    begin
      p:=False;
      MyMessenger.TitleString:='Ошибка';
      MyMessenger.MessageString:='Пустые значения в названии не допустимы';
      MyMessenger.Buttons:=[mbOK];
      MyMessenger.ShowMessage;
      abort;            // Не показывать пользователю стандартное сообщение об ошибке
    end;

    Result:=p;
  end;

begin

  If not TheTestMos
  then
    ADOTableMOs.Cancel;
end;


Попутно замечу, что выделив алгоритм проверки в отдельную функцию TheTestMos, я не преследовал никакой иной цели, кроме иллюстрации такой возможности. Такой прием был бы оправдан, если бы вызов функции повторялся в теле процедуры более одного раза.

Но это - так... лирика. Смоделировав некорректный ввод пользователя, мы получим на экран сообщение:


Первая часть проверки (попытка ввести мемориальный ордер без названия) успешно сработала. 

Аналогично нужно сделать проверку введенного значения в поле Mes.

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

Для этого понадобится запрос TADOQuery, который необходимо разместить на форме. Я назову его TempQuery.


И дополню функцию проверки: 


    // 1-я проверка на отсутствие названия мемориального ордера
    MyStr:= ADOTableMos.FieldByName('Name').AsString;
    if (MyStr='')
    then
    begin
      p:=False;
      MyMessenger.TitleString:='Ошибка';
      MyMessenger.MessageString:='Пустые значения в названии не допустимы';
      MyMessenger.Buttons:=[mbOK];
      MyMessenger.ShowMessage;
      abort;            // Не показывать пользователю стандартное сообщение об ошибке
    end;

    // 2-я проверка на отсутствие номера месяца
    MyStr:= ADOTableMos.FieldByName('Mes').AsString;
    if (MyStr='')
    then
    begin
      p:=False;
      MyMessenger.TitleString:='Ошибка';
      MyMessenger.MessageString:='Пустые значения в поле Месяц не допустимы';
      MyMessenger.Buttons:=[mbOK];
      MyMessenger.ShowMessage;
      abort;            // Не показывать пользователю стандартное сообщение об ошибке
    end;

    // 3-я проверка на ввод повторной записи
    TempQuery.Active:=False;
    TempQuery.SQL.Clear;
    MyStr:='SELECT * FROM MOs WHERE (Name='''+MyStr+''') AND (Mes='+ADOTableMos.FieldByName('Mes').AsString+')';
    TempQuery.SQL.Add(MyStr);
    TempQuery.Active:=True;
    If not TempQuery.Eof
    Then
    begin
      p:=False;
      MyMessenger.TitleString:='Ошибка';
      MyMessenger.MessageString:='Запись с такими значениями уже имеется в базе данных...';
      MyMessenger.Buttons:=[mbOK];
      MyMessenger.ShowMessage;
      Abort;
    end;
    TempQuery.Active:=False;


Запустив программу, после нажатия кнопки "Добавить" мемориальный ордер, ничего не меняя нажму "Сохранить" и опять увижу два апреля. Почему?

Вот тут я готов начать разговор о средствах отладки.
Простейшее из имеющегося богатого набора - это точка останова. Щелкните мышкой на сером поле в строке №967 ( TempQuery.SQL.Add(MyStr);). Появившаяся красная точка - точка, где программа остановится в процессе выполнения.

Если теперь навести курсор на переменную MyStr, то в хинте будет выведено значение, которое эта переменная имеет на данный момент:


В название месяца из той же переменной MyStr подставляется номер (4), полученный ею на этапе предыдущей проверки. Выход прост: переставьте местами 1-ю и 2-ю проверку:



Вот теперь, если оператор будет пытаться сделать некорректные действия, программа постарается его направить в мирное русло:


PS Точку останова нужно всегда ставить на следующей строчке, чтобы оператор присвоения значения (:=) уже был выполнен.

PPS Можно ли было вместо запроса использовать Locate? 





пятница, 6 апреля 2012 г.

четверг, 5 апреля 2012 г.

Приятно, черт возьми

Возникла необходимость яндекс погуглить на тему маски ввода, набираю в строке поиска:

delphi Ehlib Grid EditMask

и натыкаюсь на собственную публикацию в третьей позиции...


Читать не стал :-) нет там ответа на мой вопрос :-)