воскресенье, 16 декабря 2007 г.

Юзабилити Интранет-порталов

Недавно прослушал вэбкаст о юзабилит Интранет-сайтов. Итак, краткие выдержки из него.

Есть проблема: информационная разобщенность внутри компании между сотрудниками филиалов и даже между отделами, находящимися в соседних комнатах. Интранет-портал как и служит целям объединения работников, их командной работе.

Интранет — это прежде всего источник информации. Когда он внедряется в компании, то вынужден конкурировать с прочими источниками информации: желтыми самоклеющимися листочками, всезнающим соседом Димой, адресной книгой Outlook и прочими.
Интранет принесет пользу только в том случае, если будет эффективнее других источников информации.

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

Все, что похоже на баннер, будет восприниматься как баннер, а значит — пользователь проигнорирует это. Такой эффект получил название «баннерная слепота». Заболевание свойственно людями, работающим в Интернет больше двух месяцев, то есть всем.

Некоторые компании идут на рекламирование Интранет-портала среди сотрудников. Для этого, например, делают календарики. Ведущий вэбкаста Константин Самойлов посоветовал, чтобы на подобной рекламе находилась действительно ценная информация — горячие телефоны и клавиши, например.

Чаще всего пользователям нужен фильтр по имени, фамилии, почтовому ящику и внутреннему номеру телефона.

Лучше полностью отказаться от ввода пароля при входе в Интранет-портал.

воскресенье, 9 декабря 2007 г.

Форма: создание столбцов на гриде

[id:52]

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

Создайте проект Blog52 и форму BLG_Form52.

Создайте датасорс по имени Address и привяжите его к таблице с этим же именем. На дизайн формы нужно добавить контрол Grid.

Теперь создаем столбцы грида: щелкните правой клавишей на нем и выберите пункт «Создать Control», «ComboBox». Чтобы столбец показывал данные, его нужно привязать к полю таблицы. Для этого у контрола ComboBox в свойстве DataSource выберите источник Address (он единственный на форме), а свойство DataField в значение «type» (это название поля таблицы Address). Этим мы добились, чтобы на гриде появился столбец с типами адресов. Создание столбца на таблице вручную

Теперь создадим еще столбец, но менее трудоемким способом Drag&Drop. Удобно открыть в отдельном окне список полей датасорса Address (как это сделать, смотрите шаг «Форма: this is my first time»). Найдите там поле Name и перетащите его на грид. Появиться новый контрол типа StringEdit с именем «Address_Name», который уже привязан к полю Name датасорса Address.
Вот такая получается форма: Результат

Заметили, что в зависимости от добавляемого контрола, доступны табличные поля только совместимого типа? Выбрали выпадающий список ComboBox, и в свойстве DataField система показывает только поля типа перечисление (Enum). А если бы создавали контрол типа RealEdit (он работает с вещественными числами), то и привязаться можно было бы только к полю типа Real.

Интересная особенность: если на гриде не установлено значение датасорса, но в тоже время на его контролах (столбцах) есть привязка к источнику данных и конкретному полю в нем, то данные на гриде не отобразятся. Другими словами, если вы зайдете в свойства контрола Grid и сделаете пустым значение пункта DataSource, то форма перестанет показывать данные. И это не смотря на то, что в наших столбцах русским по белому сказано, откуда надо брать данные.

Задание в догонку: сделайте так, чтобы форма BLG_Form52 могла разворачиваться на полный экран. Для этого нужно изменить всего два свойства одного контрола (мы уже это делали).

Итог
  • контролы, которые добавляются на грид, превращаются в его столбцы;
  • проще создавать контролы на форме путем перетаскивания полей из датасорса.

Форма: this is my first time, часть 3

[id:57]

Наша первая форма (см. шаг «Форма: this is my first time, часть 1») все еще не приняла товарный вид. Надо заняться ее улучшением. Два дефекта:

  1. Форма не разворачивается на полный экран;
  2. Между гридом и вкладкой, на которой он находится, образовалась унылая пустота.
Проблемные места формы

Чтобы исправить первое, зайдите в свойства дизайна формы (ветка Designs, Design) и установите значение свойства WindowResize равным "Dynamic". Теперь вы можете разворачивать окно. Вот только вкладки не будут заполнять свободное пространство снизу. Установка возможности изменять размер формы

Для исправления второй огрехи нужно поработать с контролом Grid: установите его свойства Width и Height равными «Column width» и «Column height» соответственно. Тем самым вы приказываете гриду занять все возможное пространство, которое имеется на вкладке «Обзор» (ведь он находиться на ней). Это избавило нас от второй проблемы. Но все еще вкладки не изменяют своего размера при разворачивании формы.

Жизненное пространство контрола Grid на форме VeryFirstForm ограничено рамками вкладки «Обзор». Область для набора вкладок Tab ограничено размерами формы, плюс справа отъел себе пространство контрол ButtonGroup. Чтобы сказать Tab-у «Чувствуй себя как дома» установите его свойства Width и Height равными «Column width» и «Column height».

Теперь все будет хорошо.

Итог
  1. Если установить свойство Width контрола равным «Column width», то он займет всю доступную ширину области, где находится;
  2. Если установить свойство Height контрола равным «Column height», то он займет всю доступную высоту области, где находится.

Форма: this is my first time, часть 2

[id:56]

Разберем подробнее, а что было, собственно, сделано на предыдущем шаге. Мы создали форму VeryFirstForm.

Форма в Аксапте — это класс с готовым функционалом, который умеет работать с таблицами базы данных, используя своих помощников — источники данных. Класс формы состоит из трех частей: Составляющие формы

Источник данных, или датасорс, или datasource — тоже класс, посредник между элементами пользовательского интерфейса (форма, гриды, поля ввода …) и таблицами базы данных. Датасорсов может быть много, а может не быть вовсе, но каждый всегда привязан только к одной таблице. Мы можете создать методы на источнике данных и даже на отдельных его полях. Но сейчас это не так важно.
Как вы понимаете, нет никаких «полей датасорса». Есть поля таблицы, которую он «представляет».

Методы формы ничем не отличаются от методов любого другого класса.

Дизайн (ветка Design) — внешний вид формы. Заметили, что в предыдущем шаге мы вообще не заморачивались с расположением контролов? Единственно, что было задано нами, это иерархия контролов. Однако все элементы на форме упорядочились сами по себе. Это называется «технологией IntelliMorph», она думает за вас и выравнивает контролы по своему разумению с учетом ваших подсказок.

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

Можно спорить является ли недостатком Аксапты такое унылое однообразие форм, стандартизованность и унифицированность (как результат работы IntelliMorph). Скажу одно, в своей ежедневной работе только 1/10 процента всех шевелений мозговых извилин приходится на задачи проектирования пользовательского интерфейса. Я более сосредоточен на корректной автоматизации бизнес-процессов моего заказачика. А эстетство и эксперименты с юзабилити не приветствуются в условиях сжатых сроков реализации проекта.

Еще немного про элементы интерфейса. Вы помните, мы создали единственную кнопку closeForm в ветке ButtonGroup дизайна формы? Это не просто кнопка (Button), это CommandButton! А значит к ней можно привязать команду. Мы это и сделали через свойство Command. Нажимая на нее, выполняется команда закрытия текущей формы. А IntelliMorph позаботился о том, чтобы на кнопке отображался текст «Закрыть» (свойство "Text" мы не задавали).

Итог

Пользовательский интерфейс в Аксапте — забота технологии IntelliMorph, а не разработчика.

суббота, 24 ноября 2007 г.

Форма: this is my first time, часть 1

[id:53]

Есть такие счастливчики, которые хотя бы раз в жизни слышали от девушки фразу «ЭТО у меня в первый раз». Хотя, кроме везения, там обязательно присутствует доля терпения и личные качества соблазнителя.

Создание формы 1 Давайте сделаем свою первую форму. Создайте проект Blog53 и группу типа Forms в нем. Щелкните правой кнопкой на ветке Forms, пункт "Создать", "Form".

Форму будут звать "VeryFirstForm".

Разделы формы В дереве проекта у формы отображается три ветки.
Ветвь "Methods" содержит в себе методы формы; изначально там только "ClassDeclaration" (смотри «Система: MorphX, среда разработки»). Вы можете создавать свои методы на форме, равно как и перегружать имеющиеся. В конце концов форма — это класс с некоторым готовым функционалом.
Следующая ветвь "Data Sources" содержит источники данных, то есть таблицы, из которых форма будет брать данные.
И последнее — "Designs" —, где навечно прописана ветвь Design. В Аксапте все свойства формы устанавливаются именно здесь.

Давайте сделаем заголовок формы: щелкните правой кнопкой на Design и выберите пункт «Свойства» (или нажмите Alt+Enter). Справа появится прикрепленное окно свойств. Установите свойство Caption равным «Самая первая форма». Установка свойств формы И сохраните форму: щелчок правой клавишей на форме VeryFirstForm, пункт «Сохранить».

Пока рано глядеть результат, нужно еще прикрепить источник данных. Форма работает с таблицами базы данных через них. Датасорсов (источников данных) может быть много, но здесь ограничимся одним — таблицей альтернативных адресов "Address". Откройте AOT нажатием Ctrl+D и найдите там таблицу "Address". Перетащите ее в ветку "Data Sources" нашей формы VeryFirstForm: Создание датасорса путем перетаскивания таблицы из дерева объектов Дитя дрэг-энд-дропа (drag-and-drop, перетащи и брось) обзавется так же, как и таблица, к которой он привязан. Но вовсе не обязательно, чтобы датасорс назывался также, как и представляемая им таблица.

Следующий шаг — накидывание контролов на форму (элементов пользовательского интерфейса). Типовая форма в Аксапте состоит из набора вкладок "Tab" и справа размещается группа кнопок "ButtonGroup". Делаем.

Джентельменский набор вкладок и набор кнопок на форме Щелкните на ветке "Design" правой клавишей, пункт «Создать Control», "Tab". Появится контрол Tab (набор вкладок) с именем "Tab". Таким же образом сделайте контрол типа ButtonGroup с именем "ButtonGroup".

Форма с набором вкладок Типовая форма Аксапты содержит две вкладки: «Обзор» и «Разное». На первой данные отображаются в табличном виде (на гриде, Grid), а вторая содержит поля только для текущей записи.
Щелкните на контроле "Tab", выберите пункт «Создать Control», "TabPage". Переименуйте созданную вкладку в "TabOverview", ее свойство "Caption" установите равным «Обзор». Точно также создайте вторую вкладку "TabGeneral" со свойством Caption=Разное.
На вкладку TabOverview добавьте контрол типа "Grid" с таким же именем.
В группе кнопок ButtonGroup создайте контрол типа "CommandButton" c именем "closeForm". Контролы CommandButton имеют специальное свойство "Command" (оно располагается в конце списка свойств), установите его в значение «Закрыть». Тем самым вы задаете команду, которая будет выполнена системой во время нажатия на кнопку.

Заключительный этап: отображение данных на форме.
В ветке Data Sources формы для единственного датасорса Address щелкните на разделе Fields правой клавишей, выберите «Открыть в новом окне». Откроется дополнительное окно со всеми полями таблицы Address. Оно нам сейчас пригодится. Окно со списком полей датасорса Как вы понимаете сам датасорс не имеет полей, их нельзя создать на нем. Все, что он имеет, взято из таблицы Address.

Чтобы создать поля на контроле Grid, которые будут показывать данные таблицы Address, снова воспользуемся приемом Drag-and-Drop: выделите поля "Name", "Address", "Phone" в дополнительном окне и перетащите их на контрол Grid. Будут созданы контролы Address_Name, Address_Address, Address_Phone, которые превраться в столбцы на таблице Grid. Добавление столбцов на грид

Наконец, добавим что-нибудь на закладку "Разное". Для этого в дополнительном окне с полями таблицы прокрутите список вниз до конца. Вы увидите элементы с ярлыками в виде желтых папочек; это группы полей (неважно что это конкретно, просто группы полей). Выделите Description и SalesTax, перетащите их на вкладку TabGeneral. Перенос групп полей

Все готово, чтобы увидеть свою собственную форму. Щелкните на форме VeryFirstForm правой клавишей, выберите "Открыть". Вот она: Самая первая форма, вкладка Обзор Самая первая форма, вкладка Обзор

Итог

Alt+Enter — открыть панель свойств объекта.

Слон и муха

или Трудности взаимопонимания

Комнатная муха в длину около пяти-девяти миллиметров. Метраж индийского слона составляет пять-шесть метров в длину и два метра в высоту. Вот такие отличия.

Разные люди смотрят на одни и те же вещи по разному: незакрытый тюбик зубной пасты, регулярные походы по магазинам, хамство в общественном транспорте; все эти вещи сравнимы с мухой для Феди, и превратятся в слоноподобную помеху для Димы или Маши. Все дело в отношении. И все бы ничего, если каждый жил сам по себе.

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

Но важнее всего вовремя заткнуться.

воскресенье, 11 ноября 2007 г.

Данные: перебор while select

[id:55]

Очень часто нужно перебирать данные, возвращаемые запросом select, запись за записью. Этой цели служит конструкция while select. Она отбирает набор записей и позволяет выполнить нужные действия над каждой из них.
Вот пример: static void BLG55_WhileSelect() { Currency currency; setprefix("Валюты"); while select currency where currency.CurrencyCode like "c*" || currency.CurrencyCode == "rur" { info(strfmt('%1 - %2', currency.CurrencyCode, currency.Txt)); } }

Результат перебора записей о валютах Здесь мы читаем список валют в соответствии с условиями. Отобраный запросом набор записей о валютах кэшируется в буфере, связанном с табличной переменной "currency". Затем данные, запись за записью, выдаются в цикле, пока не будет исчерпан буфер. Текущая запись доступна через табличную переменную (здесь, это "currency"). Удобно.

Если не считать ключевое слово "while", то синтаксис этой конструкции идентичен синтаксису "select".

Итог:

выражение "while select" предназначено для обработки записей в цикле.

понедельник, 5 ноября 2007 г.

X++: базисные типы данных

[id:54]

Как это не занудно, но я просто обязан об этом рассказать. Ведь это основательнейшая основа основ X++.
Итак, базисные типы данных или primitive data types:

Тип Ключевое слово Значение по умолчанию
Булев boolean false
Дата date null
Время timeOfDay 0
Перечисление <имя_перечисления> 0 или первый элемент в перечислении
Целое 32-бита int 0
Целое 64-бита int64 0
Вещественное real 0.00
Строка str "" (пустая строка)

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

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

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

Во-вторых, любой из указанных типов приводиться к булеву типу, а приведенные в таблице значения по умолчанию обозначают «Ложь»; все остальное это «Истина». По-простому говоря, вы можете переменную базисного типа вставлять в условный оператор, например вот так: if (my_integer_variable) или while (! my_date)

В-третьих, эти же типы данных используются при работе с базой данных. А как же эта проблема с NULL-значениями? Ведь известно, что в базах данных есть такое понятие, как «Отсутствие данных»/«Колбасы нет. Продавец ушла на склад»/«Извините, у нас учет». И это обозначается замечательным NULL-значением.
Создатели Аксапты отказались от концепции NULL. Каждое поле таблицы всегда содержит значение. А значения по умолчанию теперь интерпретируются системой как «Отсутствие данных».

Тип "Date" решил быть не как все; он всегда был какой-то не такой. Но о нем расскажу позже.

Итог:
  • для базисных типов данных есть значения по умолчанию;
  • для базисных типов данных нет понятия NULL;
  • для базисных типов данных значения по умолчанию играют роль NULL-значений.

суббота, 3 ноября 2007 г.

Система: InfoLog, часть 2. Иерархия c помощью setprefix()

[id:051]

Подсистема Infolog позволяет упорядочивать выводимые сообщения, создавая из них иерархии. Достигается это с помощью глобального метода "setprefix". Попробуем это на примере: static void BLG51_HierarchyOfMessages(Args _args) { Currency currency; ; info("Начало"); setprefix("Валюты"); while select currency { setprefix(currency.CurrencyCode); if (currency.CurrencyCode == "USD") { error("Да это же доллар!"); break; } else info(currency.Txt); } info("Конец"); } Здесь Currency — это таблица со списком валют.

И вот такой результат будет получен:

Дерево сообщений в инфологе Каждый вызов метода setprefix() увеличивает уровень вложенности на один. Каждая закрывающая фигурная скобка уменьшает этот уровень на единицу.

Посмотрите на приведенный рисунок снова. Самый первый вызов "setprefix(«Валюты»)" делает уровень вложенности сообщений равным единице. Затем мы вошли в цикл "while" и вызвали "setprefix(currency.CurrencyCode)". Это увеличило вложенность до двух. И вот уже на втором уровне и отобразилось сообщение «Канадские доллары». Затем интерпретатор упирается в окончание цикла "while", а это значит, что закончилось действие последнего "setprefix()" и уровень уменьшился до одного. Правда не надолго: очередной "setprefix()" для валюты «Китайский юань» снова возвращает нас на второй уровень.
Заметьте, что сообщение «Конец» находиться в ветке «Валюты».

Зачем вообще это нужно?
Нужно, но не жизненно необходимо. Например, недавно я писал модуль импорта данных из Excel. Каждая строка, естественно, предварительно проверяется, и в ней может быть несколько ошибок. Чтобы не писать в каждом сообщении об ошибке номер текущей строки, я использовал setprefix.

Итог:
  • setprefix() создает деревья сообщений.
  • если сообщений на текущем уровне вложенности не было, то не отобразится и текст, указанный в методе setprefix(). Проверьте это, закомментировав в приведенном джобе строку "else" и "info(currency.Txt)".

суббота, 13 октября 2007 г.

X++: исключения, часть 2

[id:50]

Давайте попробуем по сложнее. static void BLG50_TryExceptions2(Args _args) { boolean errorFlag = false; try { if (! errorFlag) throw error('У вас прорвало батарею'); throw info('Котлеты в холодильнике. Не забудь покормить кота'); } catch(Exception::Error) { warning('Исключение ERROR поймано'); errorFlag = true; retry; } catch(Exception::Info) { warning('Исключение INFO поймано'); } info('Конец'); } Новый для вас оператор — retry. Он возвращает точку выполнения программы в начало блока "try". Да, именно в начало "try", а не в точку после строки с генерированным исключением. В нашем случае мы снова вернемся на строчку "if (! errorFlag)".
Чтобы предотвратить зацикливание я ввел переменную "errorFlag"; таким образом, исключение типа error будет сгенерировано только один раз.

Результаты получились неожиданными: Неожиданный результат Как видите, куда-то пропали наши сообщения об ошибке. Виноватым является оператор "retry". Он сбрасывает сообщения в Инфологе, начиная с последнего оператора throw — "throw error ('У вас прорвало батарею')". Нужно как-то заставить Инфолог зафиксировать наши реплики до выполнения "retry". Сделать это можно с помощью глобального метода setprefix. Внесите следующие изменения в блок catch (Exception::Error): catch(Exception::Error) { warning('Исключение ERROR поймано'); errorFlag = true; setprefix(''); retry; } Теперь все будет нормально: Дождались ожидаемого

Итог:

оператор retry возвращает исполнение кода в начало блока try.

четверг, 27 сентября 2007 г.

X++: исключения, часть 1

[id:49]

Механизм исключений в Аксапте тесно связан с подсистемой Infolog. А синтаксис идентичен C++. Попробуем простейшее: static void BLG49_TryExceptions1() { try { throw Exception::Info; } catch(Exception::Error) { print 'Exception::Error'; } print 'End of Job'; pause; } Какой будет результат? Никакой. Причина: блок "catch(Exception::Error)" не будет ловить исключение типа "Exception::Info". А я то раньше наивно предполагал о наличии какой-то иерархии исключений с базовым классом и прочими вещями, как это сделано в .NET!
Но нет. В данном случае мой код не отлавливает исключение "Exception::Info", и оно попадает в лапы Аксапты. Это значит, что до строки "print ’End of Job’" дело не дойдет.

Кстати, с таким же успехом вместо: throw Exception::Info; вы могли бы написать: throw info('Information exception'); Во втором случае, в Infolog сразу попадет ваше сообщение.
Последний вариант приемлем, если хочется сообщить пользователю об ошибке во всех подробностях (которые известны, зачастую, только в том месте, где она возникла).

Итог:

в Аксапте отсутствует иерархия исключений

воскресенье, 23 сентября 2007 г.

X++: открыть документ Word

[id:48]

В систему встроена библиотека вызова некоторых, но не всех, WinAPI-функций. Они доступны через класс Winapi.

Здесь мы попробуем открыть документ Word. Сделай следующий джоб: static void BLG48_ShellExecuteJob(Args _args) {; WINAPI::shellExecute('C:\\temp\\MyBlog.doc'); } Конечно, файл "MyBlog.doc" должен находиться в папке "C:\temp\".
Заметили двойной обратный слэш в строке пути к файлу? Не изменяй своим привычкам, программист С++!

Итог:
  • метод shellExecute() просит операционную систему Windows открыть файл с помощью программы, ассоциированной с расширением файла;
  • удваивайте обратный слэш в строковых константах.

суббота, 15 сентября 2007 г.

Система: компиляция

[id:26]

X++ — объектно-ориентированный язык программирования. Да вы уже сами догадались об этом. Догадались ли вы о том, что это также интерпретируемый язык? Видимо тоже да. Может удивить вас тем, что результатом компиляции является некий байт-код? А знаете ли вы, что сгенерированный байт-код будет во время исполнения интерпретироваться? И вся эта байткодовая возня ради того, чтобы увеличить скорость ваших решений вы тоже знали?

Сохранение изменений объекта – суть тоже самое, что и компиляция. Скомпилировать объект – это значит, что его также сохранили.

Есть одна неприятная новость, и снова глупенький компилятор. Лучше это прочувствовать самому; поэтому создайте проект с двумя классами в нем: Новый проект Код для классов: class BLG26_BaseClass { public int baseMethod(int _i) { return _i + _i; } } class BLG26_ChildClass extends BLG26_BaseClass { public int baseMethod(int _i) { return super(_i) + super(_i); } static void main(Args args) { BLG26_ChildClass cls = new BLG26_ChildClass(); ; info(strfmt('%1', cls.baseMethod(5))); } } Итак, здесь мы создали два класса — папу и дочку. В дочернем классе перекрыли метод "baseMethod()". И, конечно, создали метод main(), чтобы проверить работу написанного кода. Запустил, увидел цифру 20. Пока все нормально, дальше будет хуже.

Поменяем сигнатуру метода baseMethod() в базовом классе: class BLG26_BaseClass { public real baseMethod(real _i) { return _i + _i; } } Компилируйте базовый класс (в дереве проекта щелкните правой кнопкой мыши на классе "BLG26_BaseClass" и во всплывающем меню выберите пункт «Компилировать»). Все в порядке. Откомпилируйте дочерний класс. И снова: все в порядке! Хотя нужно ожидать ошибки. Ведь перегруженный в дочернем классе метод "baseMethod()" по-прежнему работает с целыми числами и вызывает метод "baseMethod()" своего родителя, вполне законно ожидая, что тот тоже работает с целыми числами. А на самом деле папаша уже переметнулся и манипулирует вещественными переменными. Однако компилятор молчит.
Если вы снова выполните метод "main()" дочернего класса, то снова получите цифру 20.

Выход из сложившегося положения — инкрементная компиляция. Для этого щелкните правой кнопкой на родительском классе — "BLG26_BaseClass" —, пункт «Надстройки», «Инкрементная компиляция»: Спасательный круг - инкрементная компиляция Это действие занимает несколько больше времени, чем просто компиляция, потому что система заново генерирует байт-код для текущего класса и всех его дочек. И, о, чудо! Ошибка найдена! Ошибочка вышла Мне было сказано: «ошибка 85, Перегруженная функция имеет неправильный тип возвращаемого значения».

Итог:
  • Аксапта исполняет предварительно созданый байт-код;
  • инкрементная компиляция поможет в трудную минуту.

пятница, 14 сентября 2007 г.

X++: метод strfmt()

[id:47]

Метод strfmt() — первый на очереди, когда хочется выразить свое недовольство действиями пользователя. Это то же самое, что и функция printf() в языке С++. Это тоже самое, что и метод String.Format() в .NET. Да, правильно, этим методом формируют строки. Места для подстановки значений обозначаются парой «процент-число». Поехали: static void BLG47_Ads(Args _args) { Counter cnt; str AdString(str sex, str horoscope, str behavoiur, str partner, str purpose) { return strfmt('#%6, %1, знак зодиака %2, %3, ищет %4 для %5', sex, horoscope, behavoiur, partner, purpose, cnt); } ; cnt++; info(AdString('мужчина', 'близнец', 'без в/п', 'женщину', 'создания семьи')); cnt ++; info(AdString('женщина', 'скорпион', 'курю/выпиваю', 'состоятельного мужчину с квартирой', 'самых серьезных отношений')); } Там, где стоит пара символов "%1" будет подставлено значение переменной "sex", вместо "%2" будет значение переменной "horoscope". И так далее.

Новость первая — тип "Counter" — это расширенный тип данных на базе целого числа. Откройте AOT, ветка "Data Dictionary", "Extended Data Types" и найдите этот тип. Его предназначение быть счетчиком. Использовать его или просто создавать целочисленные переменные является делом вкуса.

Новость вторая — внутри метода можно создать так называемую встроенную функцию. У нас это функция AdString(). Подобные конструкции видны только внутри текущего метода (внутри джоба BLG47_Ads). Заметьте также: то, что для джоба является локальной переменной, то для встроенной функции будет глобальной переменной. На примере видно, как AdString() имеет прямой доступ к переменной cnt, объявленной в джобе.

И третье — порядок символов подстановки для глобального метода strfmt() произвольный. В данном примере видно, что подстановка "%6" идет первой, хоть и берет значение последней переменной (переменной cnt).

Итог:

знакомится с девушками лучше в реальности. Однако уж лучше через объявления делать попытки, чем завязывать служебный роман. Служебный роман — это очень, очень нехорошо.

X++: логическое И, ИЛИ

[id:35]

Попробуем проверить поведение X++ в ситуациях с логическими условиями. Создайте джоб: static void BLG35_CheckAND() { boolean blnTest = false; if (blnTest && checkFailed('Недостижимый участок кода')) { info('Test passed'); } info('End of job'); }

В условии IF искусственно делаем так, чтобы компилятор споткнулся на первом же условии — переменной blnTest. Следующую часть проверять бессмысленно, так как условие уже не выполнилось. И здесь X++ ведет себя логично: он не будет выполнять метод checkFailed(), и сообщение «Недостижимый участок кода» мы не увидим.

Точно такая же ситуация с логическим ИЛИ: static void BLG35_CheckOR() { boolean blnTest = true; if (blnTest || checkFailed('Недостижимый участок кода')) { info('Test passed'); } info('End of job'); }

Итог:

логические И/ИЛИ работают вполне логично

воскресенье, 9 сентября 2007 г.

Система: как бы закрыть окно

[id:039]

Периодически возникает ситуация, когда я оставляю окно проекта развернутым и закрываю систему. Затем открываю Аксапту снова и получаю: Как закрыть окно проекта?

Я не вижу кнопок «Закрыть», «Восстановить» для окна проекта. Один выход: меню «Окно», пункт «Каскад». Это баг.

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

воскресенье, 2 сентября 2007 г.

X++: точка с запятой

[id:46]

Вспоминаю свой первый день работы с Аксаптой, первые примеры готового кода, с которыми разбирался. Как тогда мне резала глаза точка с запятой, безжизненно и одиноко висящая без всяких на то причин!

Вот, например, код метода одного из классов системы: public Common parmBuffer(Common _buffer = buffer) {; buffer = _buffer; return buffer; } Точка с запятой после открывающей скобки метода — это вообще что такое???

Начнем с того, что в X++ объявления переменных должны располагаться строго в начале метода, как в старые добрые времена языка Си. Дополнительно, сделайте скидку на то, что анализатор кода Аксапты глуповат, либо прикидывается таковым. Одинокая точка с запятой является наследственной, как у человека копчик; она подсказывает компилятору место, где заканчивается область определения переменных и начинается код метода.

До сих пор не смог понять, когда нужно ставить такую точку с запятой, а когда — нет. Не ставлю из принципа. Но если компилятор ругается, а вины своей я не вижу, вот тогда и ставлю этот «копчик», авось поможет (и помогает в 30% случаев).

Итог:
  • переменные можно объявить только в начале метода;
  • может стоит все-таки ставить точку с запятой после объявления переменных, ведь это же не сделка с совестью?

суббота, 1 сентября 2007 г.

X++: самый первый джоб

[id:45]

"Job", он же «джоб», он же «задание» — это отдельно стоящий кусочек кода; он независим от всех, как африканские племена независимы от системы центрального отопления. Похож на функцию, которая не пренадлежит ни одному классу. Предназначен для решения локальных задач разработчика (попробовать какой-то алгоритм, массово исправить данные, запорченные ошибкой в коде).
Далее я буду использовать термин «джоб».

Откройте дерево проектов и создайте новый проект "Blog45", и группу в нем (смотрите рисунок). У группы свойство "ProjectGroupType" должно быть равно "Jobs". Создание нового джоба Для создания нового джоба нажмите на правую кнопку мышки на узле "Jobs" и выберите пункт «Создать», "Job". Откроется редактор X++ с объявленным джобом. Переименуйте его в BLG45_Job и напишите код на рисунке ниже: Тело джоба BLG45_Job На рисунке выделены две кнопки: левая — сохранение изменений, правая — компиляция кода. Названия различные, суть одна: процессы сохранения и компиляции в Аксапте неотделимы («сохраняя, компилируем», «компилируя, сохраняем»).

В результате увидите белое окно с приветственным сообщением: Результат работы джоба

Итог

Джоб — это для программистов.

пятница, 31 августа 2007 г.

Таблица штрафов ГИБДД, 2007

Не знаю, когда мне это понадобится. И понадобится ли вообще. А вдруггггы-гы-гы.

Таблица штрафов ГИБДД, 2007

понедельник, 27 августа 2007 г.

X++: SELECT 1

[id:042]

Логично было ожидать, что Аксапта имеет упрощенный механизм работы с данными из СУБД. В X++ встроен диалект языка SQL.

Пробуем, создай джоб: static void BLG42_SelectQuery(Args _args) { EmplTable emplTable; select firstonly emplTable; print emplTable.Name, ' - ', emplTable.BirthDate; pause; }

Вот такой результат я получил: Ваш первый запрос в Аксапте Проанализируем.
Во-первых, объявляем табличную переменную emplTable для таблицы EmplTable. Таким образом создается курсор, который пока ссылаться в никуда.
Во-вторых, выбираем первую попавшуюся запись из таблицы EmplTable с помощью оператора "select", выборку производим не через саму таблицу EmplTable, а с помощью табличной переменной emplTable; в нее же возвращается результат. Таковы правила игры. Заметим, что ключевое слово "from" писать не нужно.
В-третьих, получаем значения полей записи, на которой сейчас стоит табличная переменная emplTable.]
В-четвертых, ключевое слово «firstonly» говорит АОС-у запросить у базы данных только первую запись. Ускорение в данном случае достигается за счет сокращения объема данных, передаваемых по сети из базы данных. В общем, ключевое слово «firstonly» на уровне языка запросов T-SQL превращается в «TOP 1».

Итог
  • табличная переменная пуста, пока не установишь ее на конкретную запись;
  • запрос к базе данных выполняется с помощью оператора select;
  • строка с запросом select также должна заканчиваться на точку с запятой, ведь это все еще X++;
  • ключевое слово firstonly значит: мне нужно только одну первую строку результата запроса и как можно быстрее.

X++: аксессоры 2

[id:041]

Реализация аксессора "parmMyMoney" в предыдущем уроке может вызвать ваши справедливые возражения, ведь там происходит постоянное переприсвоение значений полю "myMoney", даже если не нужно ничего изменять. Это называется "overhead" или «непроизводительные затраты».

Однако прилагать дополнительные усилия стоит, когда изменяемое поле имеет большой размер. К примеру, контейнер; контейнер — это наш размерчик! Он может быть очень большим. В случае контейнеров стоит напрячь мозги: public container parmMyBunchOfMoney(container _myBunchOfMoney = conNull()) { if (!prmIsDefault(_myBunchOfMoney) { myBunchOfMoney = _myBunchOfMoney; } return myBunchOfMoney; } connull() возвращает пустой контейнер, здесь он используется как значение по умолчанию. Глобальный метод prmIsDefault() возвращает ноль, если параметр _myBunchOfMoney содержит что-то новенькое; и выдаст единицу, если параметр хранит значение по умолчанию.
Суть приведенного кода — изменить содержимое контейнера только тогда, когда мы прислали что-то новое.

Итог

  • запомнить шаблон аксессора parmMyBunchOfMoney
  • глобальный метод prmIsDefault определяет, содержит ли параметр метода значение по умолчанию.

X++: аксессоры 1

[id:040]

Раз уж поля класса не доступны извне, то нужно писать методы доступа к ним. Эти методы называются аксесорами, или accessor methods. Обычно они очень простые, словно ситцевые трусы.

Есть устоявшийся стандарт именования таких методов:

  • название метода должно совпадать с именем поля, для которого он является аксесором. Разница только в префиксе;
  • метод должен начинаться на префикс «set», если он используется только для установки значения поля класса;
  • метод должен начинаться на префикс «get», если он используется только для получения значения поля класса;
  • метод должен начинаться на префикс «parm», если он используется и для установки и для получения значения поля класса.

Пробуем, создай класс: class BLG40_AccessorMethods { AmountMST myMoney; public void setMyMoney(AmountMST _newValue) { myMoney = _newValue; } public AmountMST getMyMoney() { return myMoney; } public AmountMST parmMyMoney(AmountMST _newValue) { myMoney = _newValue; return myMoney; } } Расширеный тип данных AmountMST относиться к семейству вещественных чисел, его используют для хранения денежных сумм.
Примечателен метод parmMyMoney, его код является типовым для аксесоров получения/изменения значений полей. Это все теже бабушкины секреты (best practices).

Ну, и протестируем . Создай статический метод main для класса: static void main(Args _args) { BLG40_AccessorMethods cls = new BLG40_AccessorMethods(); print cls.getMyMoney(); cls.setMyMoney(45.3); print cls.getMyMoney(); print cls.parmMyMoney(100.33); pause; } Проверка работы аксесоров Заметьте, что первое обращение через метод "getMyMoney" идет к неинициализированому значению поля "myMoney". Но система сделала это за нас, для целых чисел ноль — это значение по умолчанию.

Итог

  • запомнить шаблон аксессора parmMyMoney, ибо в будущем будем делать подобное постоянное
  • целочисленные, и не только, переменные автоматически инициализируются при создании

пятница, 17 августа 2007 г.

Система: InfoLog, часть 1

[id:036]

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

Есть несколько глобальных методов, которые выводят сообщения в InfoLog. Пробуем. Создайте джоб: static void BLG36_Job(Args _args) {; info('А есть закурить?'); warning('Ты че такой дерзкий?'); checkFailed('Ты по понятиям не рубишь'); error('Ты по понятиям не рубишь. Ответь за пацана'); }

Выполни джоб, увидишь типовой набор фраз гопника: Вывод сообщений в Infolog

Примечателен метод "checkFailed", визуально он не отличается от "warning". Но есть особенность: метод всегда возвращает "false", что крайне удобно.
Например, есть метод "checkSomething", проверяющий некоторые условия. Он возвратит "true", если все в порядке, иначе — "false". В случае невыполнения условий проверки, нужно также выдать сообщение пользователю: boolean checkSomething() { if (I_Am_A_Moscow_Citizen) return true; else return checkFailed( 'Понаехали! Москва не резиновая'); } Здесь проверяется булево поле "I_Am_A_Moscow_Citizen"; и, если оно равно "false", метод "checkSomething" возращает "false" и выводит сообщение пользователю одним махом.

Итог:
  • InfoLog — объект для вывода сообщений пользователю;
  • глобальные методы для вывода сообщений: info, warning, checkFailed, error.
  • info — простое информационное сообщение для пользователя.
  • warning, checkFailed — предупреждающее сообщение, означает «Случилась какая-то нехорошая вещь, но жить можно».
  • error — сообщение об ошибке, означает «Все плохо, я не могу так дальше работать, это не выносимо».

понедельник, 13 августа 2007 г.

Быт: течет кран в туалете

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

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

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

Бляха-муха — ругательное слово; используется в значении «твою мать», «черт побери»

Говорю по-русски, но понт дороже

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

Тем не менее, люди ввинчивают английские словечки там, где нет в этом необходимости. Для Москвы эта болезнь приняла хронический характер.

Примеры:

  • Здесь сидят наши сэйлзы (английское слово «Sales». «Продажные менеджеры тута»)
  • Наша работа — постоянный челендж (английское слово «Challenge». «У нас пашут все и без остановок»)
  • Давайте устроим глобальный илнэс (английское слово «Illness». «Товарищи, предлагаю тупо заболеть все разом»)
  • Это легче скопипастить (английская фраза «Copy — Paste». «Жми Ctrl+C, затем Ctrl+V и хватит корчить из себя стахановца»)
  • Я уже отправил вам месадж (английское слово «Message». «Предпочитаю посылать людей на хрен в письменной форме»)

вторник, 31 июля 2007 г.

Система: MorphX, среда разработки

[id:034]

Полное имя — MorphX Development Suite.
Так называется среда разработки в Axapta, или по ненашему — Integrated Development Environment (IDE).

Все, что связано с проектированием форм и отчетов, редактированием компилированием и отладкой кода, все это — MorphX.

Выбери класс AddressSelectForm_PurchLine IDE интегрирована с самой системой. Что представляет из себя редактор X++ кода? Откройте AOT и два раза щелкните на имени класса "AddressSelectForm_PurchLine".

Увидите окно редактора X++: Окно редактора кода Вот он какой северный олень. В панели слева отображается список всех методов класса «AddressSelectForm_PurchLine»; содержимое выделенного метода показывается справа. У любого класса присутствует строка "classDeclaration" в левой панели. Это не метод; в нем выводится объявление класса (как называется класс, от кого наследуется) и объявление его полей. Переходить по отображенным методам редактора можно с помощью комбинации Ctrl+Tab и Ctrl+Shift+Tab.

По началу такое представление было очень непривычным. Привык видеть все методы внутри класса, как в .NET. Но куда денешься?! Замечу на будущее: я буду приводить примеры кода классов в манере .NET, не вынося тело каждого метода за границы класса.

Итог

  • нужно привыкнуть к редактору кода
  • Ctrl+Tab — для перехода по методам на левой панели редактора.

четверг, 26 июля 2007 г.

Пожиратель огурцов

Рабочие места на моей работе сделаны на американский манер — перегородки, отделяющие столы, и это все — в большой комнате.

За перегородкой сидит парень деревенской наружности, 185 сантиметров по вертикали и... ЖРЕТ ОГУРЦЫ в течение последних СТА МИНУТ!!! Хруст начал добивать. Под конец огуречного марафона возникло стойкое ощущение, что я объелся огурцов, во рту появился специфический привкус огурцовой кожуры, темно-зеленой такой.

1 кг огурцов дают организму 150 килокалорий. За два часа можно перемолоть не менее килограмма. Таким образом, ртом он добыл 150 килокалорий и набил свой желудок.
Коню-соседу нужно около 3000 килокалорий в день, чтобы жить. Если грубо поделить эти три тысячи на 24 часа, что есть в нашем распоряжении каждый день, получается - 125 килокалорий в час необходимо засовывать в организм. За два часа - 250 килокалорий, которые нужно как-то восполнить.
Потери энергии: 250 (килокалорий, которые нужны нашему коню за прошелшие два часа) - 150 (килокалорий, что он выжал из огурцов за теже два часа) = 100 килокалорий

Хочет похудеть!
Бегай, жиробас!
Ах, бег тебе противопоказан?
И ты бегал раньше, но не помогло?

Я бегал по утрам - ни одного жирного не видел, все вокруг были поджарые. Какие еще отговорки будут?

среда, 25 июля 2007 г.

X++: наследование 1

[id:029]

Наследовать класс можно только от одного класса.

Создаем класс BLG29_ClassBase с конструктором: class BLG29_ClassBase { int iMyVar; void new() { iMyVar = 123; print 'constructor of base class'; } }

Создаем дочерний класс BLG29_ClassChild, туда же помести метод main(). Внимание на слово "extends": class BLG29_ClassChild extends BLG29_ClassBase { // конструктор класса void new() { super(); print 'constructor of inherited class'; } int getMyVar() { return iMyVar; } static void main(Args _args) { BLG29_ClassChild cls; cls = new BLG29_ClassChild(); print cls.getMyVar(); pause; } }

У меня появилось: Результат. Родительский класс тоже отработал

Слово "super" в конструкторе класса BLG29_ClassChild вызывает конструктор родительского класса BLG29_ClassBase.
Конструктор класса BLG29_ClassBase не запускается автоматически, попробуй закомментировать строку с вызовом super() в конструкторе класса BLG29_ClassChild.
Получишь: Результат отключения вызова родительского конструктора

Итог
  • наследование задается с помощью слова «extends»;
  • наследовать можно только от одного класса;
  • модификаторы доступа (public, protected, private) при наследовании не задаются (как в .NET);
  • доступ к перегруженным методам базового класса из дочернего — через волшебное слово "super";

X++: абстрактный класс

[id:032]

Создаем абстрактный класс и метод в нем (внимание на ключевое слово "abstract"): abstract class BLG32_AbstractClass { int iMyVar; int MyMethod() { return iMyVar + 100; } }

Еще класс с методом main(): class BLG32_Class { static void main(Args _args) { BLG32_AbstractClass cls; cls = new BLG32_AbstractClass(); } }

Что получил? Ошибка компиляции. На то он и абстрактный, что нельзя создать объект этого класса.
Исправляем ошибку. Для этого сделаем класс BLG32_Class наследником от BLG32_AbstractClass и будет обращаться к реальному классу, а не какой-то там абстракции: class BLG32_Class extends BLG32_AbstractClass { static void main(Args _args) { BLG32_AbstractClass cls; cls = new BLG32_Class(); print cls.MyMethod(); pause; } }

Я увидел цифру 100. А ведь мы даже не переопределяли метод MyMethod() в классе BLG32_Class! А все потому, что MyMethod() вполне реальный метод, несмотря на то, что класс, где он размещен, абстрактный.
Экспериментируем дальше, сделай метод MyMethod() абстрактным: abstract class BLG32_AbstractClass { int iMyVar; abstract int MyMethod() { // Абстрактный метод должен быть пустым, иначе - заругають! //return iMyVar + 100; } }

Заметь, я отчистил тело метода.
А в классе BLG32_Class я переопределил метод: class BLG32_Class extends BLG32_AbstractClass { int MyMethod() { return iMyVar + 1000; } static void main(Args _args) { BLG32_AbstractClass cls; cls = new BLG32_Class(); print cls.MyMethod(); pause; } }

Результат: цифра 1000.

Итак:

  • в неабстрактный метод абстрактного класса можно что-нибудь написать
  • тело абстрактного метода должно быть пустым (как у интерфейса)
  • в отличие от наследования интерфейса, при наследовании от абстрактного класса начинает работать полезный пункт «Перекрыть метод». Попробуй:
    Переименуй метод MyMethod() во что-нибудь другое. Щелкни правой клавишей в окне проекта на классе BLG32_Class, пункт «Перекрыть метод» и тебе откроется список методов, доступных для перегрузки. Не правда ли, это хорошая возможность поберечь свои пальцы?

X++: объявление интерфейса

[id:027]

Интерфейс создается как и класс, в той же ветке — "Classes"; отличие в ключевом слове "interface".

Пишем. Создаем интерфейс "BLG27_MyInterface" и метод в нем: interface BLG27_MyInterface { void methodToImplement() { } }

Создайте класс "BLG27_Class" (внимание на слово "implements") и метод main() в нем: class BLG27_Class implements BLG27_MyInterface { static void main(Args _args) { BLG27_Class cls = new BLG27_Class(); cls.methodToImplement(); pause; } }

Компилируем и получаем ошибку «забыл реализовать метод methodToImplement()». Исправляем этот недочет, создайте метод на классе BLG27_Class: void methodToImplement() { print 'methodToImplement'; }

Запускайте. Увидите строку «methodToImplement».
Сохрани "BLG27_MyInterface", он еще понадобится.

Итог:

два ключевых слова для работы с интерфейсами — interface и implements.

X++: метод main()

[id:025]

Метод main() — это точка входа для класса. Если знаком с методом main() из мира C#, то здесь все точно также. Хотя он создается в рамках конкретного класса, метод нужно рассматривать, как нечто обособленное.

Выглядит следующим образом: static void main(Args _args) { // Здесь ваш код }
main() — статический метод. Обязательно присутствует параметр типа Args, в нем передаются возможные параметры. Параметры именно возможные, но не обязательные.

Попрактикуемся. Создайте класс: public class BLG25_Class { }

Чтобы запустить класс, нужно его открыть: щелкните правой кнопкой на классе в дереве проекта, пункт «Открыть». И ничего не произойдет Открываем класс

Создайте метод main() в классе BLG25_Class: public class BLG25_Class { static void main(Args _args) { ; info('Hello, world!'); } }
Теперь снова откройте класс. Появиться сообщение, замозолившее программисту глаза.

Итог

main — статический метод, играет роль точки входа на классе. Нужен, прежде всего, для целей тестирования.

X++: поля класса

[id:24]

Здесь и далее использую термин «поле класса» в том же значении, как это было в С++: это переменные, объявленные в классе (но не в его методах).

Пробуем создать поле.
Создаем класс BLG24_ClassBase и в нем объявляем поле iMyVar типа int и конструктор инициализации поля: class BLG24_ClassBase { int iMyVar; void new() { iMyVar = 123; } }

Создаем еще один класс "BLG24_ClassChild" и в нем определяем статический метод main(): class BLG24_ClassChild { static void main(Args _args) { BLG24_ClassBase cls; cls = new BLG24_ClassBase(); print cls.iMyVar; pause; } }

Компилируйте. Не получилось? Значит делаем вывод: доступ к полям класса BLG24_ClassBase «из вне» не возможен. Возьмите на заметку.

Делаем класс BLG24_ClassChild дочерним от BLG24_ClassBase с помощью ключевого слова "extends". Добавляем метод getMyVar(), чтобы получить значение поля и переписываем имеющийся метод main(): class BLG24_ClassChild extends BLG24_ClassBase { int getMyVar() { return iMyVar; } static void main(Args _args) { BLG24_ClassChild cls; cls = new BLG24_ClassChild(); print cls.getMyVar(); pause; } }

Запустите метод main(). Результат: Результат - получили значение поля базового класса

О наследовании классов расскажу позже.

Итог

поля класса видят только методы данного класса и его дочки. Всем остальным доступ закрыт

вторник, 24 июля 2007 г.

X++: объявление строковой переменной

[id:023]

Чтобы объявить строковую переменную используется зарезервированное слово "str". По-умолчанию у строки неограниченная длина (сам я не проверял это утверждение). Но можно задать максимальную длину.

Пробуем. Создайте джоб: static void BLG23_Job(Args _args) { str strValue1 = "string"; str 3 strValue2 = "string"; print strValue1 + ' ' + strValue2; pause; }
Первая переменная "strValue1" является безразмерной строкой. Вторая "strValue2" может вместить максимум три символа. В результате мне показали строчку «string str».

Наблюдательный человек заметил, что строки я заключил сначала в двойные (при объявлении переменных), а потом — в одинарные кавычки (чтобы вставить пробел при конкатенации строк). А компилятор не ругается.
В Аксапте приемлемы оба типа. Давайте использовать одинарные кавычки; а двойные оставим для другого случая.

Итог:
  • строковая переменная создается с помощью ключевого слова "str";
  • можно указать максимальный размер строковой переменной;
  • строковые константы заключайте в одинарные кавычки.

Общее: создание проекта 2

[id:022]

Обобщая сказаное, проект — инструмент группировки объектов (форм, отчетов, запросов, таблиц и т.п.), инструмент повышения удобства разработки в системе. Где бы ты и что бы ты не создал, все эти объекты появятся в АОТ. В проекте ты видишь только ссылки на объекты. Верно и другое: если перетаскиваешь какой-либо объект из AOT в проект, то это вовсе не «перемещение», а создание ссылки на объект в проекте. Пробуем.

Создай новую форму в текущем проекте (щелкай правой кнопкой на проекте, пункт «Создать», строка «Form»). Появилась новая форма «Form1».

Создаем форму

И красной вертикальной полосой
Будут помечены объекты,
Что изменил, но не запомнил ты.
JayS, сборник «Криворукие стихи, замаскированные под хайку»

Созданая форма Нажми «Сохранить все» (на предыдущем рисунке отмечено красным овалом), либо Ctrl+Shift+S. Теперь открой AOT, и увидишь созданную форму.

Двойное удаление Вернись к окну проекта, на форме «Form1» щелкни правой клавишей и в контекстном меню заметишь две строки-близняшки:

Как прикажете исчезнуть, сэр? Что конкретно делает каждая из них можно понять только из текста в строке состояния, когда наводишь указатель мыши. Одна — это команда «Исключить выбранный объект из проекта» — т.е. из проекта удаляется ссылка на выбранный объект, но он остается жить в AOT. Другая — открывает диалог, где выбираешь: исключить из состава коммунистической партии Советского Союза, либо расстрелять, как троцкиста и выродка белогвардейской контры:

Удали полностью, из системы эту форму.

Может статься так, что ты создал объект, сохранил его, даже набросал парочку методов и скомпилировал. Но вдруг «Service has been terminated» и Axapta самостоятельно закрылась. Ты снова запускаешь ее, открываешь проект и не наблюдаешь свои объекты. Торопиться не нужно. Прежде всего посмотри в AOT.

Система: авторизация пользователей

[id:021]

Система Axapta 4.0 не спрашивает имя пользователя и пароль при запуске: все данные берутся из домена Active Directory. То есть, поддерживаться только режим Windows-аутентификации.

В версии 3 Axapta позволяла вводить имя пользователя и пароль, не запрашивая информации у домена.

Для любопытных товарищей: параметры всех пользователей хранятся в таблице UserInfo на сервере базы данных. Поля, которые могут представлять интерес: ID, SID, NETWORKDOMAIN, NETWORKALIAS.

SID — security identificator, или идентификатор доступа. Чтобы узнать SID пользователя вам поможет программа "SID&User".

С кого спрашивать

Если у тебя возникли вопросы при чтении моих уроков по Axapta, пиши — jay_temp@pochta.ru

Признаю за собой неприязнь к вэб-чатам, форумам и тому подобному. Комментарии в блоге очень напоминают эти самые форумы.
Возник вопрос — подумай, сформулируй его, и напиши мне письмо. Я отвечу. Мало того, если по твоим письмам станет очевидно, что какой-то урок слишком непонятен — он будет переписан.
Если автор письма в заголовке пишет: «Срочно нужна помощь!!!» или «Не могу понять» или «И снова этот DataSource…», — это очевидный сигнал, что человек не сильно напрягал свое серое вещество при написании. Он просто почувствовал внутреннюю потребность задать вопрос, а что ему конкретно непонятно — пусть это выяснится в процессе переписки с адресатом. Так дело не пойдет

понедельник, 23 июля 2007 г.

Axapta 4.0, по шагам

Изучение Microsoft Dynamics AX 4 с самого начала

Статьи ориентированы на программистов C# и С++ прежде всего.
Я предполагаю, что вы знаете базовые конструкции этих языков (for, while, switch, try, catch и так далее). Вы уже создавали SQL-запросы, знаете типы соединений наборов записей.

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

  1. Microsoft Dynamics AX
  2. AOT - Application Object Tree
  3. Система: архитектура 1
  4. Система: архитектура 2
  5. Система: слои 1
  6. Система: слои 2
  7. Система: слои 3
  8. Общее: создание проекта 1
  9. Общее: создание проекта 2
  10. Общее: создание группы элементов
  11. X++: самый первый джоб
  12. X++: case insensitive, скажи нет IntelliSense-у
  13. Система: MorphX, среда разработки
  14. X++: базисные типы данных
  15. X++: объявление строковой переменной
  16. X++: точка с запятой
  17. X++: метод strfmt()
  18. Система: InfoLog, часть 1
  19. Система: InfoLog, часть 2. Иерархия c помощью setprefix()
  20. Система: таблица, табличная переменная
  21. X++: SELECT 1
  22. Данные: перебор с помощью while select
  23. X++: метод main()
  24. X++: наследование 1
  25. Система: компиляция
  26. X++: поля класса
  27. Форма: this is my first time, часть 1
  28. Форма: this is my first time, часть 2
  29. Форма: this is my first time, часть 3
  30. Форма: создание столбцов на гриде
  31. X++: аксессоры 1
  32. X++: аксессоры 2
  33. X++: объявление интерфейса
  34. X++: абстрактный класс
  35. Терминология: номенклатура, ассортимент
  36. Система: макросы 1
  37. Система: макросы 2
  38. Система: макросы 3
  39. Терминология: компания
  40. X++: логическое И
  41. X++: исключения, часть 1
  42. X++: исключения, часть 2
  43. Система: авторизация пользователей
  44. X++: открыть документ Word
  45. Система: выполнение команд при запуске 1
  46. Система: выполнение команд при запуске 2
  47. Система: выполнение команд при запуске 3
  48. Система: как бы закрыть окно
  49. Форма: запуск с параметрами

Система: слои 3

[id:020]

Слой "USR" доступен для создания своего кода, родненького, сразу и навсегда… естественно, если куплена лицензия на разработку в Axapta.

Во время соотворения Аксапты было задумано, что компания-внедренец системы будет писать свои модификации "CUS" и "VAR" слои. Но на практике, чтобы перейти на другой, кроме "USR", слой, нужно иметь лицензионный ключ.

Платить за лицензии люди не любят. Поэтому внедренцы пишут модификации для конкретных клиентов в слой "USR". И, когда накапливаются наработки в конкретной отрасли, внедренец становится достаточно самоуверен и амбициозен, покупается лицензия и готовое решение опускается в слой "VAR" или "BUS" (насколько наглости хватит). Далее все как обычно: пресс-релиз, удары в грудь, красочные маркетинговые материалы про наше уникальное решение, которое без базара и без аналогов войдет в анналы.

Чтобы изменить текущий слой, зайдите в утилиту конфигурации клиента (описано в другом уроке) и на вкладке "Developer" выберите нужный слой. НО: для любого слоя, кроме USR/USP тебе придется ввести лицензионный код. А слой "SYS" вообще привилегия Майкрософт. Настройка текущего слоя