Показаны сообщения с ярлыком Axapta. Показать все сообщения
Показаны сообщения с ярлыком Axapta. Показать все сообщения

четверг, 1 апреля 2010 г.

Обновление декларации по НДС

Компания Майкрософт серьезно взялась за обновление российской функциональности системы Microsoft Dynamics AX. Но пока есть над чем работать. Вот пример.

15 октября 2009 года датируется приказ Минфина России об изменении декларации по НДС. Новая форма должна быть применена для отчетности начиная с 4 квартала 2009 года.

24 декабря 2009 года этот приказ была зарегистрирован в Минюсте.

24 февраля 2010 года ФНС выпустила письмо с объяснениями как заполнять декларацию.

1 апреля 2010 года российский офис Майкрософт подготовил и выпустил на белый свет обновление для DAX 4 и DAX2009 с новой версией декларации по НДС.

Выдающая оперативность.

воскресенье, 23 августа 2009 г.

Отладчик

Система Dynamics AX имеет встроенный отладчик.

Пример. Найдите таблицу PurchTable, метод checkUpdate(). Поставьте брэйкпоинт в строке номер 6, там должно быть написано:

if (this && this.PurchStatus == PurchStatus::Invoiced)

Установка брэйкпоинта Чтобы поставить брэйкпойнт нажмите на желтую ладонь в редакторе кода, или клавишу F9. Строка кода будет выделена бардовым цветом.

Точка останова

Теперь откройте форму заказов на покупку: модуль Расчеты с поставщиками/пункт Заказ на покупку. И вас выбросит в отдельное окно отладчика:

Окно отладчика

Здесь вам доступно море дополнительной информации.

Окно точек останова

Окно точек останова

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

Окно стэка вызовов

Стэк вызовов

Стэк вызова отображает вложенность вызываемых методов до текущей точки останова. Например, судя по рисунку видно, что отправной точкой явился метод active() датасорса PurchTable формы Заказ на покупку. Щелкнув два раза на строке с надписью «Forms\PurchTable\Methods\editHeaderAllowed» вам откроется код метода editHeaderAllowed().

Чем дальше вы будете знакомиться с внутренней архитектурой системы Dynamics AX, тем больше вы будете использовать стэк вызова.

Окно переменных и окно отслеживаемых переменных

Окно переменных Blog0617-watches

Какое из них использовать – дело вкуса. Первое – это список всех доступных переменных. Второе – настраиваемый вами список переменных; здесь можно написать в колонке Name имя отслеживаемой переменной и ее значение будет отображено в колонке Value.

Но вернемся к отладке. Нажмите кнопку Go в верхней панели отладчика, или клавишу F5, чтобы продолжить исполнение кода.

До следующей точки останова

Увидеть список всех поставленных точек останова можно и не заходя в отладчик. Нажмите Shift+F9 и появится такое окно со списком точек:

Список точек останова

Чтобы удалить все точки останова, нажмите кнопку «Удалить все». Тот же эффект дает сочетание клавиш Ctrl+Shift+F9.

Можно отключить режим отладки. Для этого откройте параметры системы: панель меню/меню Сервис/пункт Параметры. Перейдите на вкладку «Разработка». Установите раздел Режим отладки в состояние «Нет».

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

Итог:

Нет программ без ошибок, нет программирования без отладки.

четверг, 4 июня 2009 г.

Таблица: Временная таблица 1

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

На физическом уровне временная таблица – это отдельный файл, создаваемый на жестком диске пользователя или на жестком диске сервера AOS. Так как только один пользователь имеет доступ к конкретной временной таблице, то отпадает необходимость в использовании транзакций (ttsbegin, ttscommit, ключевое слово forupdate).

Основное предназначение временных таблиц — это буфер данных; в него данные выгружаются из таблиц базы данных для последующей обработки.

Для временных таблиц можно также создавать индексы, связи (realtions).

Временная таблица создается двумя путями:

  • Создание новой таблицы в AOT со свойством Temporary равным Yes. Хорошим тоном считается начинать название таких временных таблиц с «Tmp».
  • «Превращение» обычной таблицы во временную с помощью метода setTmp.

Первый вариант не представляет особых сложностей. Рассмотрим второй.

Создайте новый джоб:

static void BLG_UseTemporaryTables(Args _args) { BLG_TestTable1 tbl; ; print "1. что было в таблице до установки как временная"; while select tbl { print tbl.intField1, "-", tbl.strField2; } print "2. Делаем таблицу временной"; tbl.setTmp(); while select tbl { print tbl.intField1, "-", tbl.strField2; } tbl.intField1 = 123; tbl.strField2 = "123"; tbl.insert(); tbl.intField1 = 456; tbl.strField2 = "456"; tbl.insert(); print "3. Содержимое таблицы после ввода данных"; while select tbl { print tbl.intField1, "-", tbl.strField2; } pause; }

Получили следующий результат:

Я предварительно внес несколько строк в таблицу BLG_TestTable1, которые и были отображены вначале.

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

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

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

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

Последнее. Если в процессе работы вам потребовалась временная таблица и речь идет о данных из стандартных таблиц Аксапты, то прежде чем создавать новую временную таблицу посмотрите, существуют ли уже готовые. Готовые временные таблицы ищите по имени — оно начинается на «Tmp».

Итог

Временная таблица является удобным буфером для данных, которые не нужно хранить.

воскресенье, 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 На рисунке выделены две кнопки: левая — сохранение изменений, правая — компиляция кода. Названия различные, суть одна: процессы сохранения и компиляции в Аксапте неотделимы («сохраняя, компилируем», «компилируя, сохраняем»).

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

Итог

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

понедельник, 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 значит: мне нужно только одну первую строку результата запроса и как можно быстрее.