82719

Проектирования и внедрения интеграционного решения между биллинговыми системами и информационной системой Microsoft Dynamics Axapta 4

Дипломная

Информатика, кибернетика и программирование

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

Русский

2015-03-02

2.6 MB

5 чел.

АННОТАЦИЯ

Пояснительная записка содержит 152 листа, 37 рисунков, 15 таблиц.

В данной работе представлено описание проектирования и внедрения интеграционного решения между биллинговыми системами и информационной системой Microsoft Dynamics Axapta 4.

В первой главе данной работы проведен краткий анализ биллинговых систем работающих на предприятиях телекоммуникационной отрасли в нашей стране в настоящее время. Даны характеристики основным понятиям биллинга.

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

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

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

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

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

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

Восьмой раздел посвящен экологичности и безопасности проекта.

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


СОДЕРЖАНИЕ

ВВедение 6

1. АНАЛИЗ БИЛЛИНГОВЫХ СИСТЕМ 8

1.1. Особенности биллинговых систем 8

1.2. Структура и функции биллинговых систем 11

2. ИНТЕГРАЦИЯ СИСТЕМ 14

2.1. Задачи и особенности интергации систем 14

2.2. Способы интеграции приложений 15

2.2.1. Удаленный вызов процедур 15

2.2.2. Интеграция данных 19

2.2.3. Хранилища данных, оперативная аналитическая обработка данных 21

3. ПОСТАНОВКА ЗАДАЧИ 26

4. РАЗРАБОТКА СТРУКТУРЫ ПЕРЕДАВАЕМЫХ ДАННЫХ 27

5. РАЗРАБОТКА АЛГОРИТМА ПЕРЕДАЧИ ДАННЫХ 43

5.1. Структура классов интерфейса 44

5.2. Алгоритм импорта счетов из ПБД в MDAX 4 47

5.3. Алгоритм импорта справочников из ПБД в MDAX 4 55

5.4. Алгоритм экспорта справочников и операций из MDAX 4 в ПБД 57

6. ТЕСТИРОВАНИЕ И ОТЛАДКА ИНТЕРФЕЙСА 60

7. ОРГАНИЗАЦИОННО-ЭКОНОМИЧЕСКАЯ ЧАСТЬ ПРОЕКТА 65

7.1. Организация работ 65

7.1.1. Состав исполнителей 65

7.1.2. Основные этапы разработки 65

7.2. Расчет сметной стоимости и цены проекта 67

7.2.1. Материалы, покупные изделия, полуфабрикаты 67

7.2.2. Затраты на специальное оборудование 67

7.2.3. Основная заработная плата исполнителей 68

7.2.4. Дополнительная заработная плата исполнителей 70

7.2.5. Единый социальный налог 71

7.2.6. Командировки 71

7.2.7. Контрагентские расходы 71

7.2.8. Затраты на накладные расходы 71

7.3. Определение экономической целесообраздности проекта 73

8. ЭКОЛОГИЧНОСТЬ И БЕЗОПАСНОСТЬ ПРОЕКТА 74

8.1. Введение 74

8.2. Характеристика условий труда 75

8.3. Расчет естественного освещения 79

8.4. Расчет вентиляции 82

8.5. Карта условий труда 85

8.6. Заключение 90

ЗАКЛЮЧЕНИЕ 91

ЛИТЕРАТУРА 92

ПРИЛОЖЕНИЕ А. ИНСТРУКЦИЯ ПОЛЬЗОВАТЕЛЯ 93

ПРИЛОЖЕНИЕ Б. ЛИСТИНГ ПРОГРАММЫ 106

ПРИЛОЖЕНЕИ В. МАКРОС ПО ЗАГРУЗКЕ СЧЕТОВ ИЗ ДОКУМЕНТА Excel В ПБД 145


ВВЕДЕНИЕ

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

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

Процесс биллинга состоит из совокупности нескольких основных технологических функций: фиксирование оказанной услуги, ее тарификация, обработка тарифицированных услуг, выставление счета и получение оплаты.

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

Современная автоматизированная система управления предприятием должна обеспечить руководство компании и ее сотрудников максимально полной информацией, необходимой для успешного ведения бизнеса. Microsoft Dynamics Axapta 4 (MDAX4) – это масштабируемая система для средних и крупных предприятий, корпораций и холдинговых структур, предоставляющая единое интегрированное решение, направленное на повышение управляемости и роста прибыли предприятия. В том числе (говоря применительно об интеграции с биллинговыми системами) в модуле расчеты с клиентами в MDAX4 существует возможность видеть задолженность клиентов перед компанией и своевременность оплаты выставленных счетов. Но в тоже время MDAX4 не предназначена для ведения расчетов биллинга, для этого, как было сказано раньше, существуют специальные системы.

Целью данной дипломной работы является разработка интерфейса между биллинговыми системами различного уровня и MDAX4.

Для этого необходимо:

  1.  проанализировать возможные решения по передачи данных;
  2.  разработать структуру передаваемых данных, в соответствии с поставленными задачами;
  3.  составить алгоритмы передачи данных между системами;
  4.  написать пользовательский интерфейс загрузки/выгрузки данных со стороны MDAX4
  5.  составить основные алгоритмы программного модуля по интеграции со стороны MDAX4
  6.  протестировать систему передачи данных

Для решения описанных выше задач использовались следующие средства: Microsoft Office Visio 2003, Microsoft SQL Server 2005,  Microsoft Office Excel 2003, Microsoft Dynamics Axapta 4. В частности разработка программных частей работы велась на языках TSQL, VBA, X++.

  1.  
    АНАЛИЗ БИЛЛИНГОВЫХ СИСТЕМ

  1.  Особенности биллинговых систем

Системы, вычисляющие стоимость услуг связи для каждого клиента и хранящие информацию обо всех тарифах и прочих стоимостных характеристиках, которые используются телекоммуникационными операторами для выставления счетов абонентам и взаиморасчетов с другими поставщиками услуг, носят название биллинговых; цикл выполняемых ими операций именуется биллингом. Биллинговая система - это программное обеспечение, разработанное специально для телекоммуникационных операторов. То есть речь не идет лишь об операторах сотовой связи. Биллинговые системы используются также операторами обычной (стационарной, проводной) связи. В малых офисах, например, можно вести биллинг телефонии (анализировать: кто звонил, когда, сколько длился разговор). IP-телефония - другая область применения биллинговых систем. Интернет-провайдеры тоже используют биллинговые системы, например, для формирования счетов, учета трафика. Любая биллинговая система создается на основе определенной системы управления базами данных (СУБД). Большинство биллинговых систем в мире создавалось на основе СУБД Oracle. Среди других СУБД можно выделить Sybase и Informix как рассчитанные на большие объемы информации. Многие компании самостоятельно пишут свои биллинговые системы, в таких случаях большинство их использует СУБД MS SQL. Наиболее известные биллинговые системы: BIS, Flagship, CBOSS, Arbor, Infranet, Bill-2000-prepaid.

Далее будут представлены основные понятия и определения, относящиеся к биллинговым системам, и в том числе к интерфейсу с биллинговыми системами [4].

Существуют несколько названий биллинговой системы: АСР - автоматизированная система расчетов; ИБС - информационная биллинговая система.

Одним из важных качеств биллинговой системы является ее гибкость, то есть способность приспосабливаться к изменившимся обстоятельствам. Гибкая система адаптирована не только к сиюминутным потребностям оператора; за счет таких качеств, как настраиваемость, модульность и открытость она позволяет решать перспективные задачи. Чем больше у системы возможностей для настроек, тем лучше. Модульный принцип построения системы - это такой принцип, при котором вся система собирается из отдельных частей (модулей). Биллинговая система тоже состоит из таких модулей - подсистем. Биллинговая система включает в себя, например, подсистему предварительной обработки данных, подсистему оперативного управления биллингом, подсистему оповещения клиентов. Под открытостью системы подразумевается открытость исходного кода программного продукта, что позволяет оператору не зависеть от разработчика в будущем и самостоятельно обслуживать и модернизировать систему. Тесно связано с гибкостью биллинговой системы и следующее качество автоматизированных систем расчета - масштабируемость.

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

Надежность - одно из основных требований, предъявляемое к любой системе. Надежность биллинговой системы определяется надежностью СУБД и технологий, используемых при разработке системы. Далеко не последнее место занимает надежность поставщика (разработчика) прикладного программного обеспечения: время его работы на рынке и, как косвенный показатель, процент присутствия разработанных им систем на телекоммуникационном рынке. Однако надежность биллинговых систем обеспечивается также соблюдением определенных стандартов при их разработке.

Мультиязычность - возможность устанавливать различные языки для представления информации.

Мультивалютность - возможность работать с любыми валютами.

Чтобы обеспечить взаимопонимание между различными биллинговыми системами разных операторов (это, например, требуется при роуминге) были разработаны группы стандартов биллинга. Основных международных групп стандартов три.

В 1998 г. американский институт стандартов ANSI утвердил стандарт ANSI 124. Дальнейшим усовершенствованием и поддержкой ANSI 124 занимается ассоциация TIA . После этого компания CIBERNET создала рабочую группу для определения спецификаций бизнес-процессов при передаче сообщений в стандарте ANSI 124, которые получили название NSDP-B&S. Данные спецификации устанавливают однозначное соответствие между бизнес-процессами телекоммуникационных операторов и информацией, передаваемой при обмене данными между коммутаторами по стандарту ANSI 124.

В 1998 г. было опубликовано описание первого североамериканского биллингового стандарта CIBER, который в настоящее время поддерживается фирмой CIBERNET и ее комитетом CAC-IS. Этот комитет объединяет разработчиков биллинговых систем и телекоммуникационных операторов. Главная область применения CIBER - сотовые сети стандарта AMPS .

Европейский (по происхождению) стандарт ТАР появился в 1992 г. Он поддерживается рабочей группой TADIG. Большинство операторов Европы используют ТАР2, хотя существует и третья версия. С 1995 г. модификация ТАР2, известная как спецификация TD.27, или NAGTAP2, начала применяться и в США.

  1.  
    Структура и функции биллинговых систем

Схема организации биллинга не сложна: информация о соединениях и их продолжительности записывается коммутатором и после предварительной обработки передается в расчетную систему (рис.1.1). Расчетной системе известны тарифы. Она идентифицирует вызов и выполняет необходимые расчеты, формируя тем самым счет абонента. Очевидно, что в памяти системы должны храниться не только нормативы, тарифы и информация об услугах, но и данные о клиентах, заключенных контрактах с абонентами и сторонними поставщиками услуг связи (если таковые имеются), а также о стоимости передачи информации по разным каналам и направлениям (системой должно быть также предусмотрено наличие дилеров: у них могут быть другие расценки, например, на подключение). Кроме этого, любая биллинговая система должна иметь базу, хранящую историю платежей: только эти сведения позволяют контролировать процесс оплаты и автоматизировать так называемую активацию/деактивацию абонентов. Эту функцию биллинговой системы можно еще назвать защитной, так как она не позволяет пользоваться услугами сотовой связи тем, кто за них не платит.

Рис. 1.1 Схема организации биллинга

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

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

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

В масштабе региона можно вполне обойтись стандартными биллинговыми системами. Однако и такие системы должны обладать качествами, перечисленными выше: гибкостью, масштабируемостью, надежностью.

Любая биллинговая система создается и настраивается на бизнес-процесс определенного оператора связи, имеет собственный набор функций, соответствующий технологическому циклу предоставления услуг, и может работать с конкретным сетевым оборудованием, поставляющим ей информацию о вызовах и соединениях, - то есть биллинговая система не является "коробочным" продуктом [2]. Но существует и стандартный набор функций, поддерживаемых практически всеми биллинговыми системами. В него входят:

  1.  операции, выполняемые на этапе предварительной обработки и анализа исходной информации, например, функция получения данных о соединениях и услугах (запросы к коммутатору);
  2.  операции управления сетевым оборудованием: функции активации/деактивации (блокировки/разблокировки) абонентов и команды изменения условий подписки абонентов, передаваемые непосредственно в коммутатор;
  3.  основные функции приложения СУБД, включающие в себя: тарификацию записей коммутатора о вызовах и услугах; формирование и редактирование таблиц базы данных расчетной системы; выставление счетов и их печать; кредитный контроль счетов; составление отчетов; архивацию.

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

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

Основным объектом интеграции биллинговых систем и MDAX4, в конечном счете, и должны являться выставленные счета.

Рис. 1.2 Процесс биллинга


2. ИНТЕГРАЦИЯ СИСТЕМ

  1.  Задачи и особенности интеграции систем

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

  1.  взаимодействие систем не должно существенно нарушать (а по возможности совсем не нарушать) бизнес-процессы организации, в которой внедряется интерфейс. Различные системы при внедрении сами по себе заставляют пересматривать часть ведения бизнес-процессов на предприятии.
  2.  интеграция систем не должна ставить своей целью «слепое» дублирование данных в нескольких системах, работающих на предприятии.

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

Далее перечислены основные требования, влияющие на выбор способа интеграции приложений.

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

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

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

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

  1.  
    Способы интеграции приложений

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

Ниже приведен обзор технологий построения интегрированных корпоративных систем и интеграции приложений и данных [5].

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

  1.  Удаленный вызов процедур

Реальные попытки создания инструментальных средств поддержки взаимодействия распределенных приложений начались после создания и первых реализаций стека протоколов TCP/IP и основывались на транспортных протоколах передачи сообщений TCP и UDP. Первым высокоуровневым механизмом взаимодействия приложений стал протокол и механизм удаленных вызовов процедур (Remote Procedure Call, RPC), созданный компанией Sun Microsystems в середине 80-х гг. Идея состояла в том, что с использованием RPC в приложении могла быть объявлена процедура, к которой могли обращаться приложения, выполняющиеся на других компьютерах сети, так, как если бы это была обычная локальная процедура. На стороне сервера (приложения, реализующего удаленную процедуру) и клиента (приложения, использующего вызовы удаленной процедуры) процедура описывается с использованием языка IDL (Interface Definition Language - язык определения интерфейсов). На основании IDL-описания автоматически строятся программные переходники-стабы (stub), являющиеся локальным представителем удаленной процедуры на стороне клиента и представителем вызывающего клиента на стороне сервера. Одной из функций клиентского стаба является преобразование аргументов вызова процедуры в машинно-независимую форму, а одной из функций серверного стаба преобразование полученных аргументов в машинное представление. Формат машинно-независимого представления аргументов регламентировался специально разработанным протоколом XDR (eXternal Data Representation - внешнее представление данных). Реализации RPC положили начало развития нового вида программных продуктов middleware, программных средств промежуточного уровня, находящихся между приложением и операционной системой.

Следует заметить, что спецификации протокола RPC с самого начала были открытыми, что допускало реализацию RPC не только в среде ОС UNIX. Необходимость развития исходных спецификаций и согласования разных реализаций RPC привели к появлению в конце 80-х гг. консорциума OSF (Open Software Foundation), который занялся разработкой стандартов семейства DCE (Distributed Computing Environment), включавшего, помимо RPC, спецификации средств обеспечения безопасности, управления распределенными транзакциями, распределенных файловых систем и т.д. Для всех компонентов профиля DCE имелись доступные на рынке реализации, и их сертификация на предмет соответствия DCE гарантировала требуемую интероперабельность. В 1996 г. консорциум OSF объединился с консорциумом X/Open, отвечавшим за стандартизацию и сертификацию UNIX-систем, и был образован новый, существующий до сих пор консорциум OpenGroup.

К концу 80-х гг. в компьютерном сообществе резко повысился интерес к объектным технологиям. Это относилось и к языкам программирования, и к базам данных, и к операционным системам, и к методологии проектирования программного обеспечения. Возникло ощущение, что объектно-ориентированный подход может качественно улучшить информационные технологии. Это ощущение распространялось и на средства построения распределенных систем и интеграции приложений. В 1989 г. по инициативе крупнейших компаний-производителей программного обеспечения был образован консорциум OMG (Object Management Group), перед которым была поставлена задача выработки спецификаций промежуточного программного обеспечения, позволяющего создавать распределенные системы на основе объектно-ориентированного подхода. Подход получил название CORBA (Common Object Request Broker Architecture).

Если отвлечься от особенностей терминологии, подход CORBA являлся непосредственным развитием идей RPC. Снова с использованием языка IDL (объектного варианта, разработанного в OMG) можно было описать интерфейс объекта на стороне реализующего его приложения-сервера и на стороне использующих приложений-клиентов. В соответствии с этим описанием снова автоматически генерировались переходники-стабы, представляющие объект-сервер на стороне клиентских приложений и объекты-клиенты на стороне серверного приложения. За доставку аргументов вызываемому методу объекта и возвращение результатов вызова отвечал специальный компонент брокер объектных заявок, опирающийся на специальный транспортный протокол IIOP. Этот протокол строится на основе стека TCP/IP и отличается, например, от протокола HTTP тем, что в нем поддерживается сохранение состояния сессии взаимодействия объектов. При разработке спецификаций CORBA большое внимание уделялось возможности интеграции приложений, написанных на разных языках программирования. Для этого разрабатывались компиляторы языка IDL в соответствующие языки. В пакет спецификаций CORBA входили и спецификации так называемых объектных служб, которые должны были обеспечивать средства поддержки безопасности, транзакционности, постоянного хранения объектов и так далее.

Как и в случае DCE, все спецификации CORBA были и являются открытыми, и поддерживалось несколько их реализаций (например, реализации компаний BEA Systems и Iona Technologies). При наличии большого числа сторонников технология CORBA (как и технология RPC) не получила очень широкого распространения, главным образом по той причине, что для использования этой технологии требовались очень квалифицированные разработчики. Кроме того, для включения приложения в новую интегрированную среду требовалась модификация текста его исходных программ, что далеко не всегда возможно на практике. Наряду с открытой брокерной технологией CORBA существует проприетарная технология от компании Microsoft DCOM, основанная на аналогичных принципах и обеспечивающая близкую функциональность. Имеются даже мосты и шлюзы между CORBA и DCOM.

В конце XX века громадное распространение технологии Web и бурное развитие различных методов применения языка XML вывели компьютерное сообщество на новый виток спирали развития средств взаимодействия приложений. Консорциум W3C (World Wide Web Consortium) опубликовал спецификации механизма Web Services, который обеспечивает функциональность, близкую функциональности CORBA, но полностью интегрируется с остальными технологиями Internet и гораздо проще используется. Если не вдаваться в технические подробности, то технология Web-сервисов очень проста. Для определения интерфейса Web-сервиса на соответствующем Web-сервере используется язык WSDL (Web Service Definition Language). На сервере (обычно за счет инструментальных средств базового сервера приложений) определяется связь операций Web-сервиса с реализующими их приложениями. Каждому Web-сервису однозначно соответствует уникальный в Internet идентификатор ресурса (URI), неразрывно связанный с определением интерфейса Web-сервиса на WSDL. Для обращения к операции Web-сервиса с известными URI и интерфейсом клиент формирует сообщение с использованием протокола SOAP, определяющего способ представления аргументов вызова операций Web-сервиса на языке XML. Это сообщение посылается по указанному адресу с использованием протокола HTTP. Web-сервер разбирает сообщение, формирует реальные аргументы вызова и обращается к приложению. После получения результата вызова формируется ответное SOAP-сообщение и посылается клиенту.

Легко видеть, что с точки зрения общей технологии Internet технология Web-сервисов является обобщением существовавших ранее частных возможностей, например, возможности формирования запроса от клиента к серверу с использованием заполнения форм. С другой стороны, понятно, что технология Web-сервисов, фактически, позволяет добиться всех результатов, обеспечиваемых технологией CORBA, оставаясь в рамках общей инфраструктуры Internet. Следует заметить, что при применении Web-сервисов для интеграции приложений, вообще говоря, не требуется доступ к исходным текстам приложений (в отличие от механизмов RPC и CORBA). Естественно, технология Web-сервисов поддерживается соответствующими средствами обеспечения безопасности и т.д.

Все спецификации Web-сервисов, разрабатываемые W3C и другими организациями (например, OASIS), являются открытыми, и на рынке имеется много реализаций различных инструментальных средств. Однако в последнее время крупнейшие производители программного обеспечения предлагают интегрированные пакеты инструментальных средств категории middleware, основанные на концепции Web-сервиса и удовлетворяющие все возможные потребности (наиболее известны IBM WebSphere и Oracle Fusion Middleware).

Появление технологии Web-сервисов активизировало еще одно направление интеграции корпоративных приложений, основанное на давно известной концепции потоков работ (workflow). Базовая идея состоит в том, что даже при наличии интегрированной корпоративной информационной системы крайне неразумно зашивать специфику бизнес-процессов предприятия в приложения. Тогда при появлении новых или отмирании существующих бизнес-процессов потребуется дорогостоящая переделка приложений. Подход потоков работ предполагает, что сценарии бизнес процессов, определяющие порядок и правила взаимодействия приложений и работников предприятия в каждом бизнес-процессе, описываются на специальном языке и проигрываются специальной программной системой. Разработкой спецификаций стандартов, связанных с потоками работ, занимается несколько консорциумов, в том числе, OMG, OASIS и WfMC (Workflow Management Coalition). Для различных спецификаций (они всегда были открытыми) существовали реализации, но до сих пор они не получали широкого распространения.

В начале 2000-х годов компании IBM, BEA Systems, Microsoft, SAP AG и Siebel Systems разработали спецификацию языка BPEL4WS (Business Process Execution Language for Web Services, язык выполнения бизнес-процессов для Web-сервисов; более часто язык называют просто BPEL). Работа над развитием и стандартизацией BPEL продолжается в консорциуме OASIS, но ряд компаний (в частности, IBM и Oracle) уже предлагают его реализации. Интегрированность BPEL с технологиями XML и Web-сервисов, очевидно, будут способствовать более широкому распространению этой технологии.

Нельзя не упомянуть об альтернативном подходе к созданию интегрированных корпоративных информационных систем, основанных на решениях поставщиков технологии ERP (Enterprise Resource Planning, планирование ресурсов предприятия). Лидерами в этой области являются компании SAP с продуктом R3 и Microsoft с продуктом Axapta. Использование технологий ERP является вполне обоснованным при разработке корпоративной системы с нуля, потому, что поставщики обеспечивают как инфраструктуру корпоративной системы, так и базовый набор модулей, на основе которых создаются приложения. Эти приложения интегрируются, главным образом, за счет использования общей корпоративной базы данных. Однако нельзя считать, что приобретение какого-либо ERP-решения немедленно приводит к появлению работоспособной корпоративной системы. Для этого необходимо выполнить относительно длительную и дорогостоящую процедуру внедрения, т.е. настройки под особенности предприятия, для чего обычно приходится привлекать внешних квалифицированных специалистов.

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

  1.  Интеграция данных

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

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

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

Частные решения задачи интеграции данных предлагали поставщики реляционных СУБД. Например, в СУБД IBM DB2 поддерживался (и до сих пор поддерживается) шлюз для доступа к иерархическим базам данных, управляемым СУБД IMS, в СУБД INGRES компании Computer Associates обеспечивался доступ к сетевым базам данных, управляемым СУБД IDMS и т.д. Более того, большинство поставщиков развитых реляционных СУБД (IBM, Oracle, Informix, Sybase и т.д.) поддерживало в своих продуктах шлюзы для доступа к базам данных, управляемых СУБД других поставщиков. Эти решения, конечно, были полезны, но в общем случае не позволяли справиться с проблемой интеграции данных в корпоративной системе.

Очень сильное воздействие на организацию корпоративных информационных систем с несколькими базами данных оказала разработка компанией Microsoft интерфейса ODBC (Open DataBase Connectivity) и появление соответствующей спецификации CLI (Call Level Interface, интерфейс уровня вызовов) в стандарте языка SQL. Стандартизация библиотеки функций и подмножества языка SQL, обеспечивающих возможность обращаться к любой базе данных, для которой поддерживается соответствующий ODBC-драйвер, сильно снизило остроту проблемы интеграции данных. Практически все поставщики коммерческих и свободно доступных СУБД обеспечили ODBC-драйвера для доступа к своим базам данных, а открытость стандарта позволила создавать такие драйверы сторонним разработчикам. Конечно, ODBC (и появившийся позже интерфейс JDBC Java DataBase Connectivity) обеспечивает не интеграцию данных, а всего лишь доступ из приложения к различным базам данных, но, во-первых, часто это оказывается достаточным, и, во-вторых, ограниченность технологии окупается ее простотой и эффективностью.

В последние годы, в связи с развитием технологии XML (языков XPath и XQuery, спецификации XML Schema и т.д.), снова повысился интерес к подходу виртуальной интеграции данных. Сегодня актуальной проблемой является виртуальная интеграция появляющихся в корпоративных системах баз XML-данных с существующими реляционными базами данных. Несмотря на схожесть этой проблемы с упоминавшейся выше проблемой интеграции неоднородных баз данных, новая проблема гораздо проще поддается решению, поскольку, говоря нестрого, табличный формат реляционных данных легко вписывается в общий формат XML-документа.

2.2.3. Хранилища данных, оперативная аналитическая обработка данных

Принято выделять два основных вида приложений, работающих с базами данных: приложения оперативной обработки транзакций (OLTP-приложения, OnLine Transaction Processing) и приложения оперативной аналитической обработки (OLAP-приложения, OnLine Analytical Processing). Простым примером OLTP-приложения является приложение управления складом с операциями принять на склад, отпустить со склада и т.д. OLTP-приложения интенсивно выполняют над базой данных короткие транзакции, обычно состоящие из очень простых операций, изменяющих состояние базы данных. В соответствующей базе данных обычно оказывается достаточным сохранять только текущие данные, характеризующие состояние управляемого объекта. Примером OLAP-приложения может служить система, поддерживающая составление бухгалтерских отчетов. Такому приложению обычно требуются сводные статистические данные, например, средняя зарплата некоторой категории служащих за определенное время. OLAP-приложения обычно не обновляют базу данных, и эта база данных должна сохранять в основном агрегированные данные за достаточно долгий период времени.

Более того, если для многих OLTP-приложений требуются только небольшая часть текущих корпоративных данных (складскому приложению данные о складе, бухгалтерскому приложению бухгалтерские данные и т.д.), то OLAP-приложению могут потребоваться общекорпоративные сводные (а может быть, и детальные данные), а также внешние данные, характеризующие, например, деятельность конкурирующих и/или партнерских предприятий. Это особенно проявляется в OLAP-приложениях, направленных на поддержку принятия решений корпоративными менеджерами высшего звена. Для поддержки таких приложений невозможно обойтись виртуальной интеграцией корпоративных баз данных, на которых основываются OLTP-приложения: в этих базах данных не сохраняется информация, которая была актуальна в прошлом, и внешняя информация (все это просто не требуется для OLTP-приложений). Все эти рассуждения приводят нас к потребности предприятия, нуждающегося в поддержке OLAP-приложений, в физически отдельной, потенциально громадной базе данных, хранящей агрегированные исторические данные о деятельности предприятия и внешнем мире. Такую базу данных называют хранилищем данных (неудачный, но принятый в русскоязычной литературе перевод американского термина data warehouse).

Совокупность технических, методологических и организационных средств построения хранилищ данных и обеспечения функционирования OLAP-приложений, включая средства поддержки принятия решений, в последние годы принято называть технологией business intelligence (BI). BI это обширная и активно обсуждаемая тема, и здесь стоит остановиться только на нескольких моментах.

В качестве технических средств построения хранилища данных обычно используется некоторая реляционная СУБД, способная поддерживать очень большие базы данных и эффективно выполнять над ними произвольно сложные запросы. Таким требованиям удовлетворяют, например, СУБД DB2 компании IBM, Teradata компании NCR, Oracle, отчасти Microsoft SQL Server. Нужно понимать, что для поддержки сверхбольших баз данных должны использоваться соответствующие мощные аппаратные средства. Для целей аналитической обработки вокруг центрального хранилища данных часто организуются меньшие по объему тематически ориентированные части хранилища данных, называемые витринами данных (data mart; русский вариант термина неудачен, но уже принят). Для технической поддержки витрин данных можно эффективно использовать специализированные многомерные СУБД (например, Oracle Express Server или OLAP Server компании SAS Institute).

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

Для заполнения хранилища данных применяется процедура ETL (Extraction, Transformation, Loading извлечение, преобразование, загрузка). Эта процедура обычно выполняется периодически (например, раз в день) и в соответствии с названием сводится к тому, что из источников данных извлекаются новые данные. Эти данные подвергаются трансформации в соответствии с форматами хранения, принятыми в хранилище данных и наконец, данные в преобразованной форме загружаются в хранилище данных (во время загрузки производится и требуемая агрегация данных). Основной сложностью процедуры ETL является то, что в разных источниках могут находиться эквивалентные по смыслу, но различающиеся по значениям данные. Нужно каким-то образом распознать факт наличия подобного противоречия и выбрать данные, которые по некоторым критериям являются более достоверными. Эта задача не является формализованной (и, скорее всего, не может быть формализована в общем случае), и поэтому выполнение процедуры ETL обычно является полуавтоматическим с участием эксперта-специалиста в предметной области предприятия.

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

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

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

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

  1.  передача данных через файлы;
  2.  передача данных с помощью удаленного вызова процедур;
  3.  промежуточная база данных (ПБД).

Преимущества способа передачи данных через файлы:

1) независимость от внутренней реализации интегрируемых приложений;

2) отсутствие в необходимости дополнительного преобразования данных при интеграции (обмен происходит в заранее оговоренный формат);

3) техническая простота реализации.

Недостатки способа передачи данных через файл:

1) наличие сложной структуры передаваемых данных;

2) проблемы общего доступа к файлам.

Преимущества способа передачи данных по средствам удаленного вызова процедур:

1) поддержка COM-интерфейса MDAX4, следовательно, простота реализации интерфейса с одной стороны;

2) отсутствие промежуточного звена в интерфейсе – передача данных осуществляется напрямую между взаимодействующими приложениями.

Недостатки способа передачи данных по средствам удаленного вызова процедур:

1) возможное явное отсутствие поддержки удаленного вызова процедур со стороны какой-либо биллинговой системы;

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

Преимущества способа передачи данных через промежуточную базу данных:

1) согласованность и однозначность хранящейся в ПБД информации;

2) простота анализа выгружаемых (загружаемых) данных.

Недостатки способа передачи данных через промежуточную базу данных:

1) трудности в проектирование согласованной структуры ПБД;

2) возможность работы различных биллинговых систем на различных СУБД (MS SQL Server, Oracle).

В соответствие с поставленными условиями, при проектировании интерфейса между приложениями, был выбран способ передачи данных через промежуточную базу данных, по средствам связи через ODBC источники данных. Это объясняется тем, что при анализе существующих биллинговых систем, к которым имелся доступ, выяснилось, что менее трудозатратным является выгрузка данных во внешние хранилище (файлы или промежуточная база данных). Способ интеграции через удаленный вызов процедур был отвергнут из-за того, что при таком решение необходима одновременная работа клиентских приложений как биллинговой системы, так и MDAX4, что может вызвать затруднения в работе интерфейса при наличие плохого канала связи. Предпочтение способу интеграции через ПБД было отдано в связи с наличием широкого набора средств позволяющих надежно, наглядно и универсально реализовать данное интеграционное решение, в отличие от способа обмена данными через файлы. Описанные выше недостатки данного способа интеграции в процессе реализации интерфейса были учтены и по возможности устранены.


3. ПОСТАНОВКА ЗАДАЧИ

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

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

В соответствие с этим следует разработать структуру промежуточной базы данных, которую можно будет равнозначно реализовывать как на СУБД MS SQL, так и на СУБД Oracle.

Интерфейс должен позволять:

а) передавать данные как из биллинговой системы в MDAX4, так и из MDAX4 в биллинговую систему;

б) осуществлять передачу данных в ручном режиме и в автоматическом, по предварительно настроенному графику;

в) вести историю передачи данных;

г) с наименьшими трудозатратами программиста масштабировать решение в части передачи новых структур данных.

Необходимо предусмотреть возможность написания универсального средства по передачи данных  из файлов Excel в ПБД, вследствие того, что не все биллинговые системы имеют возможность выгружать данные в ПБД.

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


4. РАЗРАБОТКА СТРУКТУРЫ ПЕРЕДАВАЕМЫХ ДАННЫХ

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

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

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

Каждая таблица ПБД, содержащая справочную информацию имеет поле ExternalId (внешний номер) – по которому можно однозначно идентифицировать запись справочника. Так же каждая таблица содержит поле State (статус), отвечающее за состояние записи в ПБД. Статус может иметь следующие значения:

0 – новая запись (выгружена или из биллинговой системы или из MDAX4);

1 – запись импортирована;

2 – запись не импортирована (в результате ошибки);

3 – запись аннулирована (в системе, откуда она была выгружена такой записи больше нет).

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

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

Рис. 4.1 Структура таблиц интерфейса

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

Для интеграции справочника услуг необходимо создать в ПБД таблицу IMP_INVENTTABLE с перечнем полей описанных в таблице 4.1.


Таблица 4.1

Структура таблицы IMP_INVENTTABLE

Поле

Тип

Описание

Примечание

Обязательность заполнения

State

Int

Статус записи

При выгрузке из биллинговой системы обязательно устанавливать значение 0.

+

ExternalId

Varсhar (40)

Внешний код номенклатуры

Идентификатор услуги из биллинговой системы

+

ItemGroupId

Varchar (18)

Номенклатурная группа

ItemId

Varсhar (20)

Код номенклатуры

+

ItemName

Varсhar (140)

Название номенклатуры

 

+

ItemType

Int

Тип

Tип, определяющий способ учета

+

Dimension_

Varсhar (18)

Аналитика Отдел

 

Dimension2_

Varсhar (18)

Аналитика Статья затрат

 

Dimension3_

Varсhar (18)

Аналитика Цель

 

Dimension4_

Varсhar (18)

Аналитика Контрагент

 

Dimension5_

Varсhar (18)

Аналитика Группа

Dimension6_

Varсhar (18)

Аналитика Объект

 

Dimension7_

Varсhar (18)

Аналитика Структура

 

Dimenson8_

Varchar (18)

Аналитика

Налог

Dimension9_

Varсhar (18)

Аналитика РБП

 


Продолжение таблицы 4.1

Поле

Тип

Описание

Примечание

Обязательность заполнения

Dimension10_

Varсhar (18)

Аналитика 10

 

NameAlias

Varсhar (55)

Краткое наименование

Имя для быстрого поиска номенклатуры\услуги

isActive

Int

Активно

Возможные значения:
0 - Нет
1 – Да

Необходимо проставить 1.

+

TaxItemGroup

Varсhar (18)

Налоговая группа номенклатуры

DimGroupId

Varсhar (10)

Группа складской аналитики

ModelGroupId

Varсhar (18)

Группа складских моделей

При импорте справочника услуг из таблицы ПБД IMP_INVENTTABLE в MDAX4 данные будут помещаться в следующие таблицы: InventTable (номенклатуры) и InventItemLocation (местонахождение номенклатуры).

Поля ItemGroupId, все Dimension, TaxItemGroup, DimGroupId, ModelGroupId должны ссылаться на записи, содержащиеся в MDAX4 (т.е. справочники номенклатурных групп, налоговых групп номенклатуры, групп складской аналитики и групп складских моделей должны быть синхронизированы до импорта данных справочника услуг).

Для интеграции справочника клиентов необходимо создать в ПБД таблицу IMP_CUSTVEND с перечнем полей описанных в таблице 4.2. Название таблицы уже говорит о том, что через эту таблицу могут передаваться данные не только о клиентах, но и о поставщиках.


Таблица 4.2

Структура таблицы IMP_CUSTVEND

Поле

Тип

Описание

Примечание

Обязательность заполнения

State

Int

Состояние записи

+

GroupId

Varchar (18)

Группа контрагента

+

ExternalId

Varchar (30)

Внешний код клиента

Идентификатор клиента в биллинговой системе

+

AccountNum

Varchar (30)

Код клиента

Код клиента в MDAX4

PartnerType

Int

Тип контрагента

0 – клиент

1 – поставщик

+

Name

Varchar (255)

Название контрагента

+

NameAlias

Varchar (55)

Краткое наименование контрагента

OKPD

Varchar (7)

Вид деятельности по ОКПД

INN

Varchar (12)

ИНН

+

OKPO

Varchar (10)

Классификация по ОКПО

OKATO

Varchar (11)

ОКАТО

KPP

Varchar (9)

КПП

+

TaxGroup

Varchar (18)

Налоговая группа

ContracnCheck

int

Контроль договора при сопоставлении

0 – нет

1 - да

+

Address

Varchar (250)

Адрес клиента юридический


Продолжение таблицы 4.2

Поле

Тип

Описание

Примечание

Обязательность заполнения

Address1

Varchar (250)

Адрес клиента фактический

Phone

Varchar (50)

Телефон

TeleFax

Varchar (50)

Факс

Email

Varchar (50)

E-mail

Dimension_

Varchar (18)

Dimension2_

Varchar (18)

Dimension3_

Varchar (18)

Dimension4_

Varchar (18)

Dimension5_

Varchar (18)

Dimension6_

Varchar (18)

Dimension7_

Varchar (18)

Dimension8_

Varchar (18)

Dimension9_

Varchar (18)

Dimension10_

Varchar (18)

При импорте справочника клиентов (поставщиков) из таблицы ПБД IMP_CUSTVEND в MDAX4 данные будут помещаться в следующие таблицы: CustTable (клиенты) и VendTable (поставщик). Процесс написания интерфейса предусматривает написание автоматической синхронизации справочников клиентов и поставщиков в MDAX4 при добавлении записей в одну из таблиц Axapta (в зависимости от настроек системы).

Поля GroupId, TaxGroup и все Dimension должны ссылаться на записи, содержащиеся в MDAX4 (справочники групп клиентов/поставщиков, налоговых групп и финансовых аналитик должны быть синхронизированы до импорта данных справочника контрагентов).

Для интеграции договоров необходимо создать в ПБД таблицу IMP_CONTRACTTABLE с перечнем полей описанных в таблице 4.3. Перед импортом данных из данной должны быть загружены данные из таблицы контрагентов (описана выше).

Таблица 4.3

Структура таблицы IMP_CONTRACTTABLE

Поле

Тип

Описание

Примечание

Обязательность заполнения

State

int

+

RContractPartnerType

Int (vend)

Тип контрагента

1 – клиент

2поставщик

+

ExternalId

Varchar

(30)

Внешний код договора

+

RContractNumber

Varchar

(40)

Номер бумажного договора

+

ContractDate

Date

Дата договора

+

ContractStartDate

Date

Начало действия

ContractEndDate

Date

Конец действия

ContractAmount

Num (18,2)

Сумма договора

CurrencyCode

Varchar (5)

Валюта договора (из справочника Axapta)

+

RContractPartnerCode

Varchar (30)

Код клиента

+

RContractStatus

Int

Статус договора

1 - действует

+

ContractPostingProfile

Varchar (10)

Счет разноски дебиторской задолженности из плана счетов MDAX4


Продолжение таблицы 4.3

Поле

Тип

Описание

Примечание

Обязательность заполнения

ContractPrepaymentPostingProfile

Varchar (10)

Счет разноски дебиторской задолженности по авансам из плана счетов MDAX4

RContractSubject

Memo

Предмет договора

RContractCode

Varchar (30)

Группа договоров

+

Prepayment

Int

Предоплата

1 - да

+

RContractType

Int

Договор/приложение к договору

0 – договор

1- приложение к договору

+

RContractAccountParent

Varchar (30)

Указатель на головной договор, если выбрано приложение

Dimension_

Varchar (18)

Аналитика

Dimension2_

Varchar (18)

Аналитика

Dimension3_

Varchar (18)

Аналитика

Dimension4_

Varchar (18)

Аналитика

Dimension5_

Varchar (18)

Аналитика

Dimension6_

Varchar (18)

Аналитика

Dimension7_

Varchar (18)

Аналитика

Dimension8_

Varchar (18)

Аналитика

Dimension9_

Varchar (18)

Аналитика


Продолжение таблицы 4.3

Поле

Тип

Описание

Примечание

Обязательность заполнения

Dimension10_

Varchar (18)

Аналитика

TaxGroup

Varchar (18)

Налоговая группа из справочника  MDAX4 (для отведения налога по предоплате)

TaxItemGroup

Varchar (18)

Налоговая группа номенклатуры из справочника  MDAX4 (для отведения налога по предоплате)

PaymCode

Varchar (18)

Условия оплаты из справочника Axapta (для определения сроков погашения задолженности)

EmplID

Varchar (10)

Сотрудник, ответственный за ведение договора

При импорте справочника договоров из таблицы ПБД IMP_CONTRACTTABLE в MDAX4 данные будут помещаться в таблицу RContractTable в зависимости от значения в поле RContractPartnerType будет различаться клиентский это договор или договор поставщика.

Для импорта счетов на оплату из биллинговой системы в MDAX4 необходимо в ПБД создать две таблицы IMP_DOCTABLE и IMP_DOCLINE (соответственно заголовки счетов и их детализация в разрезе услуг). Описание этих таблиц представлено в таблицах 4.4 и 4.5.

Таблица 4.4

Структура таблицы IMP_DOCTABLE 

Поле

Тип

Описание

Примечание

Обязательность заполнения

RecNo

Bigint Identity

Порядковый номер записи в таблице

Уникальный (суррогатный) первичный ключ записи

+

ImportBatch

Varchar (20)

Код пакета

Код пакета импорта

+

SystemId

Varchar (20)

Код СВС

Код системы вливания счетов

+

State

Int

Статус

+

ExternalId

Varchar (255)

Внешний номер счета

Номер счета в биллинговой системе

+

InvoiceType

Int

Тип счета

0 – доходный,

1 расходный.

+

RContractAccount

Varchar (30)

Код договора

Идентификатор договора (или внешний или внутренний номер)

+

AccountNum

Varchar (14)

Код клиента

Идентификатор клиента (или внешний или внутренний номер)

PeriodId

Varchar (10)

Период оказания услуг

+

InvoiceDate

Date

Дата счета-фактуры (дата поставки)

+

FactureDate

Date

Дата счета фактуры

CurrencyCode

Varchar (5)

Валюта

Код валюты из справочника MDAX4

+


Продолжение таблицы 4.4

Поле

Тип

Описание

Примечание

Обязательность заполнения

FixedExchRate

Num (18,2)

Фиксированный курс

Курс по которому выставили счет, умноженный на 100

Dimension

VarChar (18)

Аналитика Отдел

Dimension2_

VarChar (18)

Аналитика Статья затрат

Dimension3_

VarChar (18)

Аналитика Цель

Dimension4_

VarChar (18)

Аналитика Контрагент

Dimension5_

VarChar (18)

Аналитика Группа

Dimension6_

VarChar (18)

Аналитика Объект

Dimension7_

VarChar (18)

Аналитика Структура

Dimension8_

VarChar (18)

Аналитика Налог

 

Dimension9_

VarChar (18)

Аналитика РБП

 

Dimension10_

VarChar (18)

Аналитика 10

 

PoolId

VarСhar (18)

Кластер

FixedDueDate

Datetime

Дата оплаты

Если не заполнена, определяется по сроку оплаты от даты счета

Payment

VarСhar (18)

Условие оплаты

PostingProfile

Varchar (10)

Профиль разноски

Если не указан, определяется по договору.

inclTax

Int

Включает налог

0 нет,

1 – да

+


Продолжение таблицы 4.4

Поле

Тип

Описание

Примечание

Обязательность заполнения

ExternalInvoiceId

Varchar (20)

Внешний номер накладной

InvoiceNum

Varchar (20)

Внешний номер фактуры

Таблица 4.5

Структура таблицы IMP_DOCLINE 

Поле

Тип

Описание

Примечание

Обязательность заполнения

RecNo

Bigint Identity

Порядковый номер записи в таблице

Уникальный (суррогатный) первичный ключ записи

+

TableRecNo

Bigint

Ссылка на уникальный идентификатор заголовка счета

+

State

Int

Состояние

Обязательно устанавливать значение 0.

+

ItemId

VarChar (20)

Код услуги

+

Qty

Num(18,2)

Количество

Для услуги - 1

+

TaxFlag

 Int

Признак НДС

0 – без НДС
1 – с НДС

+

LineAmount

Num (18,2)

Сумма за услугу

С НДС или без НДС в зависимости от указанного параметра в заголовке "Цена включает налог"

+

CurrencyCode

Varchar (5)

Валюта

Код валюты из справочника MDAX4


Продолжение таблицы 4.5

Поле

Тип

Описание

Примечание

Обязательность заполнения

Name

VarChar (1000)

Название услуги для счета

TaxGroup

Varchar (10)

Налоговая группа

Заполняется из клиента

TaxItemGroup

Varchar (18)

Налоговая группа номенклатуры

Заполняется из номенклатуры

Однозначная связка заголовка счета (IMP_DOCTABLE) с его деталями (IMP_DOCLINE) осуществляется по связке RecNo – TableRecNo, фактически она должна соответствовать следующему ключу SystemId, ImportBatch и ExternalId. Именно этот ключ обусловлен тем, что данные в ПБД могут выгружаться одновременно из нескольких биллинговых систем (разделение происходит по полю SystemId). В пределах же одной биллинговой системы выгрузка одного и того же счета может происходить несколько раз (к примеру, при изменении данных счета, или набора оказанных услуг, в этом случае разделение происходит по полю ImportBatch). Значение поля ExternalId должно позволять однозначно идентифицировать выставленный счет на оплату в биллинговой системе, откуда он был выгружен.

Выше были описаны структуры таблиц данные, из которых можно загружать в MDAX4. Добавление новых справочников импортируемых в MDAX4 в интерфейс осуществляется достаточно просто, главное при этом включать в структуру таблиц общие поля State и ExternalId. Для операционных данных, которые имеют связи типа заголовок – строки, нужно обязательно включать в структуру поля RecNo, и в подчиненную таблицу поле TableRecNo (ссылка на родительский RecNo), и в зависимости от ситуации могут быть добавлены поля SystemId и ImportBatch. Но как показывает практика, кроме счетов на оплату из операционных данных биллинговые системы больше ничего не выгружают. Все прочие операционные данные, как правило, выгружаются из MDAX4.

Для организации экспорта данных из MDAX4 структура таблиц будет определена несколько иначе, не смотря на то, что предполагается выгрузка тех же справочных данных (клиенты, договора, услуги и прочее) в ПБД. Разделение таблиц на «экспортные» и «импортные» обусловлено тем, что, во-первых, возможна многосторонняя интеграция (к примеру, не только с биллинговой системой и MDAX4, но и MDAX4 с каким либо  еще приложением предприятия, или наоборот). Во-вторых, это связано с особенностями реализации алгоритма экспорта, который описан в следующей главе.

В таблице 4.6 приведен перечень полей, обязательный для всех таблиц ПБД, в которые будет происходить выгрузка данных из MDAX4.

Таблица 4.6

Перечень обязательных полей для таблиц экспорта

Поле

Тип

Описание

Примечание

Обязательность заполнения

RecId

Int64

Уникальный идентификатор записи в MDAX4

В пределах одной компании

+

DataAreaId

Varchar (3)

Идентификатор компании в MDAX4

Компания, из которой выгружались данные

+

Export_Date

Datetime

Дата и время выгрузки записи

+

Import_Flag

Int

Статус записи

0 – новая запись (выгружена);

1 – запись загружена сторонней системой;

2 – запись обновлена из MDAX4;

3 – запись аннулирована

+

Допускается использование в поле Import_Flag расширенного списка статусов, для нужд биллинговых систем. Следует так же отметить, что данных в MDAX4 могут храниться в разных компаниях (так называемое разделение хранения данных, видимое из приложения, но физически данные хранятся в одних и тех же таблицах, и разделяются только по полю DataAreaId, которое имеет каждая таблица MDAX4).

Набор выгружаемых данных определяется потребностями конкретной биллинговой системы. Существует возможность создавать таблицу в ПБД с набором полей, полностью идентичным набору полей выгружаемой из Axapta таблицы (справочник или данные). Так же возможно перечисление необходимых для выгрузки полей (на основе полей таблицы Axapta). При реализации данного интеграционного решения был написан ряд классов, позволяющий выгружать порядка 20 справочников и 5 типов операционных данных из MDAX4. Подробнее об этом будет написано в 5ой главе.

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

Создание (пересоздание) таблиц ПБД, экспорт, импорт данных должен отражаться в системе в таблице лога. Структура этой таблицы (GM_IMPORTLOG) представлена в таблице 4.7, так как данная таблица представляет интерес только для MDAX4, создана она в базе данных Axapta (это, в том числе, позволяет достаточно просто производить пользователям анализ выгрузки/загрузки данных, а администраторам отслеживать возникающие в процессе передачи данных ошибки).

Таблица 4.7

Перечень полей для таблицы лога

Поле

Тип

Описание

Примечание

Обязательность заполнения

RecId

Int64

Уникальный идентификатор записи в MDAX4

В пределах одной компании

+

DataAreaId

Varchar (3)

Идентификатор компании в MDAX4

Компания, из которой выгружались/ загружались данные

+

TransDate

Datetime

Дата экспорта/ импорта

Дата и время операции

+

UserID

Varchar (5)

Код пользователя

Код пользователя инициирующего обмен данными в MDAX4

+

Num

Varchar (30)

Идентификатор записи в MDAX4 или в биллинговой системе

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

RecNo

Int64

Уникальный идентификатор записи в MDAX4 или в ПБД

Значение, по которому можно однозначно программно идентифицировать запись


Продолжение таблицы 4.7

Поле

Тип

Описание

Примечание

Обязательность заполнения

TableName

Varchar (50)

Источник, приемник

Название таблицы ПБД

+

ImportBatch

Varchar (50)

Код пакета

Код пакета обмена данными

SystemID

Varchar (15)

Код системы

Код системы, с которой происходит обмен данными

+

Text

Varchar (255)

Описание оперции обмена

Описывает соверешенное действие, если была ошибка, то описывает ошибку

+

ErrorFlag

Int

Флаг ошибки

0 – действие выполнено без ошибки

1 – ошибка

+


5. РАЗРАБОТКА АЛГОРИТМА ПЕРЕДАЧИ ДАННЫХ

Передача данных как из биллинговых систем  в MDAX4, так и из MDAX4 осуществляется через промежуточную базу данных. Структура ПБД была описана в главе 4. В общем виде схема передачи данных представлена на рис. 5.1. Более светлым цветом на нем отмечены участки, которые будут реализованы в данной дипломной работе. Это доработка справочников и операций в MDAX4, в соответствии с разрабатываемым интерфейсом, создание механизма ведения истории операций связанных с обменом данных, разработка структуры ПБД и ее физическая реализация, непосредственно интерфейс между MDAX4 и ПБД. В результате исследования биллинговых систем на предмет возможного прямого взаимодействия их приложений с ПБД, было выявлено, что не все биллинговые системы могут (или не все поставщики этих решений способны) на однозначную связь биллинга с ПБД, но зато все эти системы способны выгружать данные в формат Microsoft Excel. В связи с этим было принято решение о написании макроса, позволяющего переводить данные из xls формата в ПБД.

Рис. 5.1 Схема передачи данных


5.1. Структура классов интерфейса

Внутренний язык MDAX4 X++ является объектно-ориентированным. В связи с этим при проектировании структуры классов интерфейса используется возможность наследуемости и полиформизма. Общая структура разрабатываемых классов представлена на рис. 5.2.

Разработку алгоритмов интерфейса можно разделить на 3 этапа:

  1.  разработка алгоритмов импорта справочников;
  2.  разработка алгоритмов экспорта справочников;
  3.  разработка алгоритмов импорта счетов.

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

Одним из требований, предъявляемых к интерфейсу, является возможность запуска заданий обмена данными в пакетном режиме. Для этого все родительские классы должны быть наследованы от системного класса MDAX4 – RunBaseBatch, который пре перекрытие ряда методов позовляет организовать как ручное выполнение заданий импорта/экспорта, так и выполнение их в пакетном режиме. Наглядные примеры данной реализации приведены в инструкции пользователя (приложение А).

Для импорта счетов создается базовый класс импорта счетов (GM_ImportBillsCls) с набором методов, относящихся к непосредственной работе с ПБД и низкоуровневой обработкой данных (считывание их из ПБД, преобразование к внутренним форматам Axapta, ведение лога операций). В этом же классе реализуется основной цикл считывания счетов из ПБД (метод Import и ImportDoc). В методе ImportDoc осуществляется импорт конкретного счета. Для этих целей происходит инициализация и вызов экземпляра класса обработки счета (GM_ImportBillDocTableCls). В данном классе уже реализуются методы, относящиеся к загрузке считанных данных во внутренние переменные класса, проверки импортируемых данных и сохранение их в таблицах Axapta.

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

Иерархия классов относящихся к экспорту данных представляет собой следующее:

- класс обработки групп экспорта (GM_ExportDict) – в нем осуществляется только работа с группами экспорта, предполагающая создание групп экспорта, наполнения их различными справочниками, задание параметров экспорта групп и непосредственный вызов экспорта данных справочников конкретной группы (с помощью вызова конкретного класса экспорта в методе Export);

- родительский класс для экспорта данных в ПБД (GM_ExportDictTableClass) – в нем реализуются общие методы, связанные с экспортом справочников, которые при необходимости перекрываются в дочерних классах, а также методы, связанные с преобразованием данных Axapta к формату ПБД и созданием необходимых запросов к ПБД;

- набор дочерних классов экспорта конкретных справочников (начинаются на GM_ExportDictTable).

Иерархия классов импорта справочников отличается от описанной выше иерархии классов экспорта только отсутствием класса обработки групп импорта. Вся логика работы с группами импорта реализована в родительском классе импорта данных из ПБД (GM_ImportFromPBD_Class). В дочерних же классах перекрываются методы, относящиеся к чтению данных из ПБД во внутреннее представление Axapta, проверка считанных данных и запись их во внутренние таблицы Axapta.


Рис. 5.2 Структура классов интерфейса


5.2. Алгоритмы импорта счетов из ПБД в MDAX4

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

На рис. 5.3 и 5.4 представлены схемы соответствующие основному методу импорта счетов из ПБД пакетами. Суть метода заключается в том, что данные из таблицы счетов в ПБД считываются пакетом, а потом уже передаются в подметод, который в цикле обрабатывает данный пакет.

Рис. 5.3 Общая схема алгоритма импорта счетов

Рис. 5.4 Схема метода импорта счетов

Схема алгоритма метода обработки счетов одного пакета представлена на рис. 5.5.

В начале данного метода формируется запрос выборки к таблице заголовков счетов (блок B1), который в результате выполнения должен возвратить набор данных, отвечающих условию выборки (блок C1). Далее считанные данные сохраняются в контейнере заголовков счетов (блок D1), по которому в последующем организуется цикл (блоки E1-H2). Внутри цикла осуществляется вызов метода записи в Axapta данных заголовка счета (блок F1 – рис.5.6) и считывание данных деталей счета из ПБД в Axapta аналогично циклу, описанному выше для считывания заголовках.

Считывание деталей счета происходит уже в разрезе не только конкретного пакета, но и в разрезе конкретного счета. Данный цикл показан на блоках G1-F2. Внутри цикла считывания деталей для каждой детали счета вызывается метод записи в Axapta данных детали счета (блок D2 – рис.5.7).

Рис. 5.5 Схема метода импорта счетов одного пакета

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

Что собой представляют методы считывания данных из ПБД (LoadGlobalData и LoadLineGlobalData), методы проверки данных (CheckParseGlobalData и CheckParseLineGlobalData) и методы записи данных в MDAX4 (ProcessSales и ProcessSalesLine)?

В методах считывания данных происходит перечисление полей, информацию из которых нужно обрабатывать в MDAX4. То есть, на этапе импорта документа (схема представлена на рисунке) осуществляется запрос к конкретной таблице ПБД (например, к таблице заголовков счетов IMP_DOCTABLE), в результате, которого считывается одна запись из указанной таблицы. Этот результат помещается в контейнер. В методах LoadGlobalData и LoadLineGlobalData происходит присвоение конкретным переменным класса импорта считанных данных. Такой механизм считывания данных используется и в других классах импорта данных из ПБД.

В методах проверки данных уже осуществляется сверка загруженных данных с данными, на которые они ссылаются в MDAX4. Ссылка может осуществляться как по уникальным кодам Axapta, так и по внешним номерам (для этого предварительно производится синхронизация справочной информации). Так же в этих методах проверяется обязательность заполнения полей в ПБД, обозначенных как обязательные при проектировании структуры данных.

В методах записи данных в MDAX4 генерируется (по предварительно настроенным номерным сериям в Axapta) внутренние уникальные номера счетов, строк счетов и прочее, то без чего невозможно хранить (и в последующем обрабатывать) данные в системе. Далее осуществляется заполнение таблиц Axapta загруженными и проверенными данными из ПБД. Данные по заголовкам счетов в MDAX4 будут загружаться в таблицу заказов на продажу. Данные по детализациям – в таблицу строк заказов на продажу.

В приложении Б содержится листинг классов импорта счетов.

Рис. 5.6 Схема метода импорта заголовка счета

Рис. 5.7 Схема метода импорта детали счета


5.3. Алгоритм импорта справочников из ПБД в MDAX4

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

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

Как и в импорте счетов, функционал позволяет реализовать пакетную загрузку данных. То есть пользователь осуществляющий загрузку данных может настроить выполнение этого задание автоматически с интервалами времени, и прочими параметрами. Этот механизм уже реализован в MDAX4, можно настраивать выполнение любого задания в пакетном режиме. Для того что бы новые, программируемые операции, в частности импорт данных, можно было выполнять в пакетном режиме необходимо разрабатываемые классы наследовать от класса RunBaseBatch (который как раз и реализуется пакетный механизм выполнения заданий).

После импорта каждой записи из таблицы ПБД, в независимости от результата (успешный импорт или с ошибкой) в таблице логов создается запись о результатах выполненного действия.

Рис. 5.8 Общая схема алгоритма импорта справочников


5.4. Алгоритм экспорта справочников и операций из MDAX4 в ПБД

На рис. 5.9 представлена общая схема экспорта данных из MDAX4 в ПБД. Предполагается, что пользователи будут создавать группы экспорта, в которые будут подтягивать таблицы Axapta (справочники, операции), которые должны быть выгружены в ПБД. При вызове экспорта функционал будет проходить по группе экспорта и для каждой выгружаемой таблицы инициализировать свой класс экспорта. Фактически при подтягивании т.н. таблиц Axapta в группу экспорта будет подтягиваться ссылка на класс экспорта. После инициализации создается отдельное подключение к ПБД для экспорта данных, и далее вызывается метод экспорта конкретных данных Axapta, схема которого находится на рис. 5.10.

Рис. 5.9 Общая схема алгоритма экспорта данных группы экспорта

Рис. 5.10 - Схема алгоритма экспорта справочников (конкретных данных)

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

Для выборки экспортируемых данных из Axapta было задействовано стандартное средство среды разработки MDAX4 – Query (статические запросы). Которые представляют собой запрос (может быть связным) к данным Axapta, с возможностью наложения каких-либо ограничений. Эти запросы могут быть сохранены в среде разработки MDAX4, в репозитарии объектов (наряду с таблицами, формами, классами), а в последующем использованы в коде. В коде эти статические запросы могут быть преобразованы в динамические, путем наложения дополнительных динамических ограничений. Это было, как раз сделано в решении экспорта данных, когда при уточнении задания на интеграцию пришлось добавить расширенные параметры экспорта, которые включали в себя: период, за который необходимо выгружать данные, признак выгрузки только новых или измененных данных (по сравнению со временем последней выгрузки).

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

В приложение Б приведен листинг таких классов. К примеру, класс дочерний класс GM_ExportDictTable_Currency перекрыт минимально, в нем только определен список выгружаемых полей, в остальном используется логика родительского класса GM_ExportDictTableClass. А в классе GM_ExportDictTable_CustVend перекрыт основной метод doExport. Связано это с тем, что данный класс отвечает за экспорт справочника контрагентов (который физически делится на два справочника – клиенты и поставщики). Можно было бы создать два отдельных класса, но это было не сделано из-за того, что, во-первых, структуры таблиц клиентов и поставщиков в Axapta схожи, а, во-вторых, биллинговым системам хотелось видеть эти два справочника как один.


6. ТЕСТИРОВАНИЕ И ОТЛАДКА ИНТЕРФЕЙСА

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

Ошибки ПО:

  1.  Технологические - ошибки документации и фиксирования программ в памяти электронно-вычислительной машины (ЭВМ). Они составляют 5-10% от общего числа ошибок, обнаруживаемых при отладке.
  2.  Программные - их количество зависит от квалификации разработчиков, от общего объема программы, от глубины логического и информационного взаимодействия модулей и от других факторов. Обычно такие ошибки составляют 1/3 всех ошибок. Существуют два типа программных ошибок:
  3.  синтаксические ошибки состоят в нарушении формальных правил написания программы и появляются в результате недостаточного знания пользователем языка программирования и невнимательности при технической подготовке программы к обработке на ЭВМ;
  4.  семантические или логические ошибки - причинами таких ошибок могут быть: не соответствие алгоритма поставленной задаче, неправильное понимание программистом смысла (семантики) операторов языка программирования.
  5.  Алгоритмические - это ошибки, обусловленные некорректной постановкой задач. Такие ошибки составляют 6-8% от общего числа.
  6.  Системные - возникают из-за неполной информации о реальных процессах, происходящих в источниках и потребителях информации. Обычно в начале отладки доля этих ошибок не велика (около 10%), но она существенно возрастает (до 35-40%) на завершающих этапах отладки.

Перед началом тестирования был составлен план тестирования отдельных участков интерфейса.

Глобально план тестирования делился на следующие этапы:

  1.  тестирование интерфейса по передачи справочных данных из биллинговой системы в MDAX4;
  2.  тестирование интерфейса по передачи справочных данных из MDAX4 в биллинговую систему;
  3.  тестирование интерфейса по передачи счетов из биллинговой системы в MDAX4.

Каждый из трех этапов тестировался как на ПБД, организованной на MS SQL 2005, так и организованной на Oracle9i.

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

- корректный импорт (вставка) не существующих данных в Axapta (включающий правильность считывания данных, их последующее преобразование к внутренним форматам Axapta);

- корректный импорт (обновление) существующих данных в Axapta (включающий правильность считывания данных, их последующее преобразование к внутренним форматам Axapta);

- проверка всех передаваемых ссылочных значений на связные справочники Axapta;

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

- обновление статусов у обработанных записей в таблицах ПБД, в соответствии с поставленными условиями, прописанными в техническом задании;

- соблюдение графика вызова заданий импорта в пакетном режиме.

На втором этапе были созданы следующие группы экспорта справочников. Группы, которые содержали одну экспортируемую сущность и группа, которая содержала все экспортируемые сущности. Были произведены экспорты каждой из этих групп, сначала в ручном режиме, со всеми возможными настройками экспорта, описанными  в техническом задании (экспорт только новых данных, экспорт  данных за различные периоды и т.д.). Потом в пакетном режиме с предварительными настройками интервала вызова заданий экспорта. В результате экспорта всех настроенных групп было проверены следующие условия:

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

- корректный экспорт (обновление) выгружаемых ранее данных в ПБД  (включающий правильность выборки данных согласно заданным условиям, нахождение данных, которые следует обновить в ПБД);

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

- обновление статусов у обработанных записей в таблицах ПБД, в соответствии с поставленными условиями, прописанными в техническом задании;

- соблюдение графика вызова заданий экспорта в пакетном режиме.

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

- корректный и полнообъемный импорт счетов (включая заголовки и детали счета);

- правильность выборки данных согласно условиям, заданным в параметрах импорта счетов (данные считывались в разрезе пакетов, если таковые были указаны);

- правильность преобразования считанных данных к внутренним форматам Axapta;

- корректные проверки импортируемых данных, в зависимости от параметров указанных в настройках импорта счетов (динамические проверки по обязательности передаваемых данных);

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

При возникновении ошибок, выявленных на этапах тестирования, начиналась отладка кода.

При отладке этого интерфейса использовались следующие средства локализации ошибок:

  1.  Печать в узлах, то есть печать промежуточных значений переменных, интересующих программиста в выбранных местах (узлах) интерфейса.
  2.  Слежение, как арифметическое (слежение за использованием в программе выбранных переменных), так и логическое (слежение за выполнением некоторых интересующих операторов).

В таблице 6.1 приведены основные ошибки, которые были выявлены на этапе отладки.

Таблица 6.1

Примеры ошибок интерфейса, выявленных на этапе отладки

Ошибка

Описание

Решение

Загрузка в Axapta данных, которые ссылаются на несуществующие записи

К примеру, при импорте счетов в заголовке счета были указаны ссылки на справочник периода оказания услуг, при этом такой период в Axapta заведен не был

Все считываемые данные из ПБД, ссылающиеся на какие-либо справочники в Axapta, проверяются на существование в Axapta


Продолжение таблицы 6.1

Ошибка

Описание

Решение

При выполнение SQL запросов к ПБД из Axapta возникали ошибки подключения к базе данных

Это было связано с тем, что предварительно не были настроены и проверены подключения к ПБД

Вызовы методов обращающихся к ПБД из Axapta были переписаны таким образом, что бы перед ними всегда шла проверка на наличие подключения к ПБД

При выполнение SQL запросов к ПБД из Axapta возникали ошибки выполнения запроса

Это было связано с тем, что из Axapta не было дано разрешение на выполнения запросов такого рода (новшество появившиеся в MDAX4)

Выполнение этих запросов пришлось заключить в специально предназначенную для этого конструкцию разрешающую доступ к внешним базам данных из Axapta

Задвоение данных при загрузке (выгрузке) в Axapta

Одни и те же сущности данных дублировались в таблицах ПБД и Axapta

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

При написании данного решения использовались следующие приемы предупреждения ошибок:

  1.  этапность разработки;
  2.  уровень языка;
  3.  наглядность текста программы;
  4.  личные приемы;
  5.  структурированность программы.

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

- автоматическое определение типа системы управления базой данных (СУБД) ПБД (MS SQL или Oracle) - этот параметр берется из настроек ODBC подключения, с последующем ветвлением методов формирования запросов к ПБД, которые в зависимости от типа СУБД ПБД были написаны соответствующем синтаксисом;

- во всех «узких» местах кода были добавлены обработчики исключительных ситуаций, позволяющие контролировать процесс обмена данных и не прерывать передачу данных на первой же ошибке;

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


7. ОРГАНИЗАЦИОННО-ЭКОНОМИЧЕСКАЯ ЧАСТЬ ПРОЕКТА

7.1. Организация работ

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

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

7.1.1. Состав исполнителей

Для разработки  программного продукта организации потребуется привлечь группу специалистов из четырёх человек:

  1.  Начальник отдела;
  2.  Ведущий инженер;
  3.  Техник №1;
  4.  Техник №2.

7.1.2. Основные этапы разработки

При проведении работ по созданию данной системы используются следующие этапы разработки проекта.

1. Техническое задание;

  1.  Анализ биллинговых систем;
  2.  Интеграция систем;
  3.  Разработка структуры передаваемых данных;
  4.  Разработка алгоритма;
  5.  Проектирование программного интерфейса;
  6.  Руководство пользователя;
  7.  Сдача заказчику.

Этапы работ по разработке интерфейса осуществляются в соответствии с планом-графиком, изображенным на рис. 7.1


Рис. 7.1 План-график работ


7.2. Расчет сметной стоимости и цены проекта

Расчет затрат рассчитывается по следующим калькуляционным статьям:

  1.  материалы, покупные изделия, полуфабрикаты, с учетом транспортно-заготовительных расходов;
  2.  специальное оборудование для научных (экспериментальных) работ;
  3.  основная заработная плата исполнителей;
  4.  дополнительная заработная плата исполнителей;
  5.  единый социальный налог;
  6.  командировочные расходы;
  7.  контрагентские расходы;
  8.  накладные расходы.

7.2.1. Материалы, покупные изделия, полуфабрикаты

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

Перечень и стоимость материалов и покупных изделий приведены в таблице 7.1.

Таблица 7.1

Материалы и покупные изделия

Наименование материала

ед. измер

Кол-во

Цена за ед., руб.

Сумма, руб.

1

Канцелярский набор

шт.

4

550

2200

3

Бумага ( А 4)

пачка

4

220

880

4

Диски CD

шт.

5

40

200

5

Флешки

шт.

2

1300

2600

6

Картридж для принтера

шт.

2

2100

4200

Итого:

10080

Транспортно-заготовительные расходы на материалы, покупные изделия составляют 18% от стоимости материалов: 10080 руб. * 0,18 = 1814,40 руб.

Итого по статье затрат «материалы, покупные изделия, полуфабрикаты» с учетом транспортных расходов составляют: 11894,4 руб.

7.2.2. Затраты на специальное оборудование

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

7.2.3 Основная заработная плата исполнителей

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

Расчет стоимости одного человеко-дня проводится с учетом того, что в месяце 22 рабочих дня.

Заработная плата участников проекта приведена в таблице 7.2.

Таблица 7.2

Заработная плата участников проекта

Должность

Основная заработная плата, руб.

Оплата за 1 день, руб.

1

Начальник отдела

25000

1136

2

Ведущий инженер

20000

909

3

Техник №1, №2

15000

682

Оплата за 1 рабочий день начальника отдела – y. Основная заработная плата x = 25000 руб., количестворабочих дней в месяце z = 22.

y = x/z = 25000/22 = 1136 руб.

Оплата за 1 рабочий день ведущего инженера – y. Основная заработная плата x = 20000 руб., количестворабочих дней в месяце z = 22.

y = x/z = 20000/22 = 909 руб.

Оплата  за  1  рабочий  день техника №1, №2 – y. Основная заработная плата x = 15000 руб., количестворабочих дней в месяце z = 22.

y = x/z = 15000/22 = 682 руб.

Затраты на основную заработную плату сотрудников, принимающих участие в разработке приведены в таблице 7.3.


Таблица 7.3

Затраты на основную заработную плату

№ п/п

Этап

Исполнитель

Трудоемкость, дни

Оплата за

1 день, руб.

Оплата за этап, руб.

1

Техническое задание

Начальник отдела

1

1136

1136

Техник №1

4

682

2728

2

Анализ биллинговых систем

Начальник отдела

1

1136

1136

Техник №1

7

682

4774

3

Интеграция систем

Ведущий инженер

1

909

909

Техник №2

6

682

4092

4

Разработка структуры передаваемых данных

Начальник отдела

2

1136

2272

Техник №1

10

682

6820

Техник №2

10

682

6820

5

Разработка алгоритма

Ведущий инженер

2

909

1818

Техник №1

13

682

8866

Техник №2

13

682

8866

6

Проектирование программного интерфейса

Техник №1

22

682

15004

7

Руководство пользователя

Техник №2

8

682

5456

8

Сдача заказчику

Начальник отдела

3

1136

3408

Итого

71377

Оплата за этап = u. Оплата за 1 рабочий день исполнителя = y. Количество дней, затраченное на выполнение этапа = v.

Этап «Техническое задание».

  1.  Исполнитель начальник отдела.

u = y*v = 1136*1 = 1136 руб.

  1.  Исполнитель техник №1

u = y*v =4*682 = 2728 руб.

Этап «Анализ биллинговых систем».

  1.  Исполнитель начальник отдела.

u = y*v = 1136*1 = 1136 руб.

2. Исполнитель Техник №1.

u = y*v = 682*7 = 4774руб.

Этап «Интеграция систем».

  1.  Исполнитель ведущий инженер.

u = y*v = 1136*1 = 1136 руб.

  1.  Исполнитель техник №2.

u = y*v = 682*6 = 4092 руб.

Этап «Разработка структуры передаваемых данных».

  1.  Исполнитель начальник отдела.

u = y*v = 1136*2 = 2272 руб.

  1.  Исполнитель техник №1.

u = y*v = 682*10 = 6820 руб.

  1.  Исполнитель техник №2

u = y*v = 682*10 = 6820 руб.

Этап «Разработка алгоритма».

  1.  Исполнитель ведущий инженер.

u = y*v = 909*2 = 1818 руб.

  1.  Исполнитель техник №1.

u = y*v = 682*13 = 8866 руб.

  1.  Исполнитель техник №2

u = y*v = 682*13 = 8866 руб

Этап «Проектирование программного интерфейса».

Исполнитель техник №1.

u = y*v = 682*22 = 15004 руб.

Этап «Руководство пользователя».

Исполнитель техник №2.

u = y*v = 682*8 = 5456 руб.

Этап «Сдача заказчику».

Исполнитель начальник отдела.

u = y*v = 1136*3 = 3408 руб.

7.2.4 Дополнительная заработная плата исполнителей

К статье затрат «Дополнительная заработная плата сотрудников»  (ДЗП) относятся выплаты, предусмотренные законодательством за неотработанное по уважительным причинам время: оплата очередных и дополнительных отпусков, по случаю временной нетрудоспособности и т.п.

Дополнительная заработная плата вычисляется по формуле (7.1):

ДЗП = 0,25 * ОЗП                                                                                     (7.1),

где ОЗП — основная заработная плата.

ДЗП = 0,25 * 71377 руб. = 17844 руб.

Таким образом, фонд оплаты труда, который равен суммарным расходам на основную и дополнительную заработную плату сотрудников составит:

71377 руб. + 17844 руб. = 89221 руб.

7.2.5 Единый социальный налог

В соответствии с изменениями, внесенными в Налоговый кодекс РФ, вступившими в силу с 1 января 2005 г., ставка единого социального налога составляет 26% от фонда оплаты труда.

89221 руб. * 0,26 = 23197,53 руб.

7.2.6 Командировки

Данный проект не предусматривает командировок.

7.2.7 Контрагентские расходы

Данный проект не предусматривает контрагентских расходов.

7.2.8 Затраты на накладные расходы

Затраты по данной статье составляют 250% от основной заработной платы исполнителей.

НР = 250% * ОЗП

НР = 2,5 * 71377 руб. = 178442,50 руб.

Итоговая стоимость разработки представлена в таблице 7.4.

Таблица 7.4

Смета затрат

№ п/п

Наименование статей затрат

Сумма, руб.

1

Материалы, покупные изделия, полуфабрикаты

11894,40

2

Специальное оборудование

-

3

Основная зарплата исполнителей

71377

4

Дополнительная зарплата исполнителей

17844

5

Единый социальный налог

23197,53

6

Командировки

-

7

Контрагентские расходы

-

8

Накладные расходы

178442,50

Стоимость разработки продукта

302755,43

Прибыль

90826,63

Оптовая цена предприятия

393582,06

НДС

68703,78

Договорная цена

462285

Стоимость разработки продукта организацией равна сумме затрат по всем статьям расходов и составляет 302755,43 руб.

Прибыль составляет 30% от стоимости затрат:

302755,43*0,30 = 90826,63

Оптовая цена равна сумме стоимости и прибыли:

302755,43+90826,63 = 393582,06

НДС составляет 18% оптовой цены предприятия за минусом материальных затрат.

НДС = (383582,06 руб.- 11894,40 руб.) * 0,18 = 68703,78 руб.

Договорная цена равна оптовой цене плюс НДС, и составляет:

393582,06 руб. + 68703,78 руб. ≈ 462285 руб.


7.3. Определение экономической целесообразности проекта

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

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

Заработная плата этих операторов составляет по 10000 руб.

Экономия за один месяц работы составляет 100000 руб.

Стоимость разработки данного продукта составляет 462285 руб. По истечении 4,5 месяцев программа окупается и начинает приносить прибыль.

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


8. ЭКОЛОГИЧНОСТЬ И БЕЗОПАСНОСТЬ ПРОЕКТА

8.1. Введение

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

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


8.2. Характеристика условий труда

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

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

Одно рабочее место включает в себя (рис. 8.1):

  1.  системный блок;
  2.  монитор;
  3.  клавиатура;
  4.  мышь;
  5.  принтер;
  6.  настольная лампа;
  7.  рабочее кресло.

Рис. 8.1. Рабочее место программиста

Рабочее помещение включает в себя (рис. 8.2):

  1.  рабочих мест оборудованных ПК – 4;
  2.  постоянно присутствующих людей – 4;
  3.  2 окна(размером 1,5х2 м), оборудованы жалюзи;
  4.  5 люминесцентных ламп;
  5.  1 кондиционер;
  6.  Размеры помещения –  6х4х3 м;
  7.  Общая площадь помещения 24 м2;
  8.  Окраска интерьера: белый потолок, светло-зеленые  стены, пол, обтянут линолеумом светло-бежевого цвета;
  9.  мебель в кабинете: столы коричневого цвета, стулья на колесиках черного цвета, вся оргтехника и компьютеры светлых тонов;
  10.  освещение: естественное (через окна и дверь) и искуственное.

Рис. 8.2. Рабочее помещение

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

Уровень шума в помещении также считается приемлемым. Соотношение шум/сигнал гораздо выше предела в 50%, при котором считается, что полезная информация подавляется полностью.

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

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

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

Предел допустимых условий по частоте мерцания монитора равен 100 Гц. В настоящее время мониторы выпускают с частотой в пределах допустимых.

Также отвечают эргономическим требованиям стандартов клавиатура и мышь. Клавиатура стандартная прямоугольная 101/102 клавиши. Размещение основных, вспомогательных и функциональных клавиш, основанное на частоте их использования, не меняется уже несколько десятилетий. В последние годы появились новые эргономичные клавиатуры с изломом и подушечкой под запястья. Эргономически это очень удобно, однако выигрыш в производительности и эффект удобства это дает при быстрой машинописи. Перед программистами задачи быстропечатания не стоит, поэтому нет веских оснований использовать именно такую клавиатуру.

Рабочее место и элементы программиста спроектированны эргономически корректно.

Программист – профессия, не требующая больших энергозатрат. Это «сидячая» работа. Энергозатраты в течение рабочей смены невелики. Физическое напряжение почти отсутствует.

Человек, работающий над проектом, работает в одну смену по восемь часов в сутки, что отвечает требованиям Трудового Кодекса Российской Федерации, устанавливающего максимальное время работы в сорок один час в неделю.

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


8.3. Расчет естественного освещения

В производственном помещении, в котором работа с использованием ПЭВМ является основной и связана с выше перечисленными факторами, имеется естественное и искусственное освещение.

Естественное освещение осуществляется через оконные проемы. Площадь одного оконного проема S = 1,5*2 = 3 м2, площадь помещения 24 м2 (длинна А=6 м, глубина В=4 м) Так как помещение прямоугольное, естественное освещение проникает в помещение также через дверной проем. Оконные проемы оборудованы регулируемыми устройствами типа: жалюзи.

Уровень рабочей поверхности hраб = 1 м. Средневзвешенный коэффициент отражения, учитывающий окраску помещения  = 0,4%. Световые проемы ориентированы по сторонам горизонта на (316 - 45)°. Противостоящего здания нет. Офис расположен в г. Москве.

Определяем нормированное значение коэффициента естественной освещенности (КЕО) для выполняемой зрительной работы. В соответствии с исходными данными для территории России без устойчивого снежного покрова значение равно 2 %. Занесем это значение в таблицу 8.1.

Таблица 8.1

Исходные данные и результаты расчета плащади световых проемов

Вид освещения

SП, м2

,%

,

%

0

КЗ

КЗД

r1

0

Площадь световых проемов, м2

Боковое одностороннее

24

2

1,8

15

1,7

1

2,45

0,675

59,95

2. Вычисляем КЕО для данного светового климата [5] в соответствии с формулой:

где - значение КЕО для зданий, расположенных в I поясе светового климата;

      m – коэффициент светового климата;

       С – коэффициент солнечности климата.

Город Москва расположен в I поясе светового климата, для которого (при заданном в условии диапазоне азимута m = 0,9) С= 1. Тогда = 1,8. Занесем это значение в таблицу 8.1.

3. Определяем площадь пола офисного помещения: Sп = A*B =24 м2. Занесем это значение в таблицу 8.1.

4. В соответсвии с  [5] выбираем значения:

1 – коэффициент, учитывающий потери света в светопропускающем материале, для оконного листового одинарного стекла  1 = 0,9.

2 – коэффициент, учитывающий потери света в переплетах светопроема, для деревянных одинарных переплетов  2 = 0,75.

3 – коэффициент, учитывающий потери света в несущих конструкциях (при боковом освещении 3 = 1).

4  - коэффициент, учитывающий потери света в солнцезащитных устройствах. Принимаем  4 = 1.

Определяем параметр 0 = 1  2  3  4 = 0,675.

Определим r1 – коэффициент, учитывающий повышение КЕО при боковом освещении благодаря свету, отраженному от поверхности помещения и подстилающего слоя, прилегающего к зданию.

Принимаем расстояние от верха окна до потолка hв. о равным 0,4м. Тогда высота от уровня условной рабочей поверхности до верха окна

h1 = Hhраб hв. о;   

h1 = 3 – 1 – 0,4 = 1,6 м.

Для отношения  и отношения расстояния l расчетной точки от наружной стены (принимается равным 1м) к глубине помещения   При использовании линейной  интерполяции  [5] получим для  =0,4% r1 = 2,45.

Кзд – коэффициент, учитывающий затенение окон противостоящими зданиями. При отсутствии противостоящих зданий  Кзд =1.

Кз – коэффициент запаса, учитывающий снижение освещенности в процессе эксплуатации остекления и зависящий от концентрации вредностей в воздушной среде рабочей зоны и расположения светопропускающего материала. Для офисного помещения при наклонном освещении Кз =1,7.

Занесем коэффициенты 0, r1, Кзд, Кз  в таблицу 8.1.

5. Рассчитаем требуемую площадь световых проемов для офисного помещения по формуле:

  ;

Здесь 0 – световая характеристика окон. Для = 4,32 и   = 1,5 применяя линейную интерполяцию [5], получим 0 = 15. Занесем это значение в таблицу 8.1.

Тогда требуемая площадь световых проемов

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


8.4.
Расчёт вентиляции

Согласно постановления СанПин 2.2.2/2.4.1340-03 "Гигиенические требования к персональным электронно-вычислительным машинам и организации работы" (п.3.4) площадь на одно рабочее место должна составлять не менее 6м2.

На одного работающего в помещении вычислительного центра приходится S = (6*4)/4 =6 м2. Следовательно, фактические значения площади на одного работающего соответствуют нормативным значениям.

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

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

Проектирования  вентиляции  производится для комнаты площадью S=24 м2,  длина которой A =6 м, ширина B = 4 м, высота h=3 м.

Соответственно объем помещения равен:

V помещения  = А  В  h =72 м3

Объем приточного воздуха, необходимый для поглощения избытков тепла G рассчитывается по формуле:

Qизбыт – избыточная теплота, Вт;

Сp = 1000– удельная теплопроводность воздуха, Дж/кг о С;

= 1,2 – плотность воздуха, кг/м3;

tуд и tпр – температура удаляемого и приточного воздуха.

Температура удаляемого воздуха определяется по формуле:

tуд = tр.з. + ( Н - 2 )a , где

tр.з. = 25 – температура в рабочей зоне, оС;

a = 0,5…1,5 – нарастание температуры на 1м высоты, оС/м;

H = 2,6 – высота от пола до вытяжного отверстия, м;

tпр = 17 оС.

tуд = 25 + ( 2,6 - 2 ) 0,9 =25,54 оС 

Qизбыт = Qизб.1 + Qизб.2 + Qизб.3+ Qизб.4 , где

Qизб.1 – избыток тепла от освещения:

, где

N =  – суммарная мощность источников освещения, Вт;

- коэффициент тепловых потерь (= 0,55);

Qизб.1 = 0,55 * 432=237,6 Вт

Qизб.2 – избыток тепла от вычислительной техники:

, где

N = – суммарная мощность устройств вычислительной техники, Вт;

- коэффициент тепловых потерь (= 0,4…0,7).

Вт

Qизб.3 – теплопоступление от солнечной радиации:

, где

Fост = (1,5*2)*2= 6 – площадь поверхности остекления, м2;

qост = 127 – удельное выделение от солнечной радиации, Вт/м2;

Аост = 1,15 – коэффициент учета характера остекления.

 Вт

Qизб.4 - тепловыделения людей:

Qизб.4 = q1 +q2 +…+qn, где

В рассматриваемом помещении работаю четыре человека: два мужчины, выделяющие по q = 132 Вт и две женщины по – q  = 112 Вт

Qизб.4 = 132*2+112*2 = 488 Вт

Qизбыт = 237,6+760,5+876,3+488=2362,4 Вт

Тогда

м3

Вентиляционная система состоит из следующих элементов:

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

2.   Круглого стального воздуховода длиной 1,5 м;

3.   Воздухораспределителя для подачи воздуха в помещение.

Потери давления в вентиляционной системе определяются по формуле:

, где

P - потери давления, Па;

R - удельные потери давления на трение в воздуховоде, Па/м;

l = 1,5- длина воздуховода, м;

V = 4 – скорость воздуха, м/с;

р = 1,2 - плотность воздуха, кг/м3.

Потребная площадь воздуховода определяется по формуле:

, где

Gi = 1100,5 – расход воздуха на i-том участке сети, м3/ч;

Ui = 4 м/с– допустимая скорость движения воздуха.

Тогда,

м2

Принимаем в качестве диаметра величину – 0,35 м, при которой удельные потери давления на трение в воздуховоде – R=0,22 Па/м.

Местные потери возникают в железной решетке (=1,2), воздухораспределителе (=1,4) и калорифере (=2,2). Отсюда, суммарный коэффициент местных потерь в системе:

= 1,2 +1,4 + 2,2 = 4.8

Тогда

Па

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

В рассматриваемом помещении в холодное время года температура воздуха колеблется от 20 до 23 °C, а в теплое – от 22 до 25 °C. Следовательно, значения параметров микроклимата соответствуют нормативным, а система вентиляции спроецирована корректно.

Так же приток воздуха осуществляется за счет поступления из смежных помещений (коридора), а естественное удаление воздуха из помещения происходит через окно.

Ежедневно проводится влажная уборка рабочего места и систематическое проветривание кабинета после каждого часа работы на ПЭВМ.


8.5. Карта условий труда

Одним из факторов оценки труда является категория тяжести труда.

Тяжесть труда подразделена на 6 категорий в зависимости от изменения функционального состояния человека по сравнению с исходным состоянием покоя. Категория тяжести труда определяется медицинской оценкой или эргономическим расчетом (результаты близки).

Составляется «Карта условий труда на рабочем месте», в которую заносят все биологически значимые показатели (факторы) условий труда с оценкой их по 6-ти бальной шкале. Данная карта условий труда представлена в таблице 8.2.

Таблица 8.2

Карта условия труда

№ п/п

Показатели условий труда. Единица измерений

ПДУ, ПДК

Оценка показателя

Длительность воздействия

Балл с учетом экспозиции (временного воздействия)

Абсолютная

В баллах

Мин.

В долях смены

1

2

3

4

5

6

7

8

1

Напряжение зрения: освещенность раб.места, лк

330

330

1

420

0,88

1

размеры объекта, мм

3

1

480

1

1

разряд зрительной работы

5

2

360

0,75

2

энтропия зрительной информации, бит/сигн.

16

2

420

0,88

2

число информационных сигналов в час

70

1

360

0,75

1

2

Напряжение слуха: уровень шума, дБ

70

60

1

400

0,83

1

Соотношение сигнал/шум, %

80

1

400

0,83

1


Продолжение таблицы 8.2

1

2

3

4

5

6

7

8

2

энтропия слуховой информации, бит/сигн

16

2

420

0,88

2

3

Напряжение внимания: длительность сосредоточенного наблюдения, % времени смены

35

2

360

0,75

2

число важных объектов наблюдения

4

1

336

0,70

1

темп - число движений пальца в час

720

2

336

0,70

2

4

Напряжение памяти: необходимость помнить об элементах работы свыше двух часов

1

1

336

0,70

1

Поиск рассогласований, в % от числа регулируемых параметров

30

2

336

0,70

2

5

Нервно-эмоциональное напряжение. Экспертная оценка

2

360

0,75

2

6

Интеллектуальное напряжение. Экспертная оценка

3

360

0,75

3

7

Физическая нагрузка: энергозатраты, Вт

190

2

400

0,83

2

Внешняя механическая работа, Вт (1 Вт = 6 кгс. м/мин)

18

1

360

0,75

1


Продолжение таблицы 8.2

1

2

3

4

5

6

7

8

8

Статическая нагрузка в течение смены, кгс.сек на одну руку

12000

1

360

0,75

1

на две руки

24000

1

360

0,75

1

на весь корпус

50000

1

360

0,75

1

9

Рабочее место, рабочая поза, перемещение в пространстве. Экспертная оценка

1

1

480

1,00

1

10

Сменность

1

1

480

1,00

1

11

Продолжительность работы в течение суток, час

10

2

480

1,00

2

12

Монотонность: число приемов в операции

6

2

360

0,75

2

длительность повторяющихся операций, с

35

2

360

0,75

2

13

Режим труда и отдыха

3

180

0,38

1,13

14

Температура воздуха на рабочем месте: теплый период, С

23-28

22

3

480

1,00

3

холодный период, С

20-22

23

1

480

1,00

1

15

Вредные химические вещества, кратность превышения ПДК

*

*

480

1,00

*

16

Промышленная пыль, кратность превышения ПДК

*

*

480

1,00

*


Продолжение таблицы 8.2

1

2

3

4

5

6

7

8

17

Вибрация, ПДУ плюс превышение, дБ

*

*

480

1,00

*

18

Ультразвук в воздухе, ПДУ плюс превышение, дБ

2

480

1,00

2

19

Тепловое излучение, Вт/см2

1

480

1,00

1

Из таблицы 8.2 видно, что несколько факторов имеет баллы – 3. Под воздействием этих показателей формируется более неблагоприятная категория тяжести труда. Поэтому в расчет принимаются только этот фактор, так как прочие факторы, имеющие оценку в 1 – 2 балла в расчет категории тяжести труда не принимаются, так как они составляют обычный фон жизнедеятельности человека.

Баллы рассматриваемых факторов ki суммируют и находят усредненный балл по формуле:

, где

n – число факторов;

ki – балл i – того показателя.

Определяем интегральный показатель воздействия на человека всех факторов по формуле:

Отсюда

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

1 категория - оптимальные условия труда, т.е. такие, которые при систематическом и длительном воздействии на человека обеспечивают нормальное состояние его организма. Опасные и вредные факторы отсутствуют. Работоспособность высокая,  отсутствуют функциональные сдвиги по медицинским показателям.

2 категория - условия труда допустимые, т.е. такие, которые могут вызывать временное ухудшение самочувствия человека. После отдыха состояние человека нормализуется..

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

4 категория – отличающиеся условия от допустимых.

5 категория - резко отличается от допустимых условий.

6 категория – за гранью допустимых условий.

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

Показатель работоспособности вычисляется по формуле:

Чтобы понизить категорию тяжести труда по факторам, превышающим балл 3, предлагаю следующее:

- для снижения интеллектуального напряжения – необходимо делать каждые 2 часа перерывы на 15-20 минут. Чередование работы и отдыха является важным условием плодотворной интеллектуальной деятельности. Так как работа инженера-программиста это сидячая работа, то для устранения дефицита мышечной деятельности можно порекомендовать физические упражнения и разминку. Они приводят в действие естественные резервы человека, создавая и поддерживая основу высокой работоспособности, возможность к длительному напряжению наиболее сложных функций нервной системы.

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


8.6 Заключение

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


ЗАКЛЮЧЕНИЕ

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

Данный интерфейс позволяет осуществлять полноценную интеграцию между ERP системой MDAX4 и любой биллинговой системой.

Разработанное интеграционное решение имеет следующие достоинства:

  1.  программа имеет простой и удобный пользовательский интерфейс;
  2.  существует возможность пакетного запуска заданий обмена данными;
  3.  решение может работать как с биллинговыми системами на СУБД MS SQL, так и Oracle;
  4.  с помощью реализованного интерфейса есть возможность обмениваться данными не только с биллинговыми системами.

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

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


ЛИТЕРАТУРА

  1.  Алексей Еременко, Руслан Шашков. Разработка бизнес-приложений в Microsoft Business Solutions - Axapta версии 3.0. – М.: Альпина Бизнес Букс, 2005 – 503 с.,
  2.   Муссель К. М. Предоставление и биллинг услуг связи. Системная интеграция. – М: Эко-Трендз, 2003 – 320 с.,
  3.  Гарнаев А.Ю. Самоучитель VBA. – СПб, «БХВ-Петербург», 2003 – 512 с.
  4.  Денис Соколов. «Биллинговые системы: основные понятия»  http://www.ixbt.com/mobile/review/billing.shtml
  5.  СНиП (23-05-95). Естественное и искусственное освещение. Нормы проектирования. - М.: Стройиздат, 1995г.
  6.  Самгин Э.Б. “Освещение рабочих мест” – М.:МИРЭА ,1989 г.
  7.  Санитарные правила и нормы 2.2.2.542-96. “Гигиенические требования к видеодисплейным терминалам, персональным электронно-вычислительным машинам и организации работы” – М.: Стройштад , 1996 г.
  8.  Розанов В.С., Рязанов А.В. “Обеспечение  оптимальных параметров воздушной среды в рабочей зоне”.- М.:МИРЭА, 1998г.
  9.  Санитарные правила и нормы П.04.91. “Отопление, вентиляция, кондиционирование”. - М.: Стройиздат,1991г.
  10.  Сергей Кузнецов. «Когда, как, что и зачем стоит интегрировать?» http://citcity.ru/10881/ 


ПРИЛОЖЕНИЕ А

ИНСТРУКЦИЯ ПОЛЬЗОВАТЕЛЯ

Перед началом работы с интерфейсом необходимо сделать настройки импорта и экспорта данных. Это делается в меню Администрирование – Настройки (рис. А.1).

Рис. А.1 Вызов настроек интерфейса

В настройках импорта (рис. А.2) справочников необходимо указать имя источника данных (предварительно настроенного на подключение к ПБД), при необходимости указать логин и пароль подключение к базе (если не настроена аутентификация пользователя по доменному имени). Так же можно задать параметры очистки ПБД (очищать – импортированные данные будут удалены в таблицах ПБД; не очищать – у импортированных данных будут проставлены соответствующие статусы).

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

Продолжение приложения А

Рис. А.2 Настройки импорта справочников

Настройки экспорта справочников аналогичны (рис. А.3).

Рис. А.3 Настройки экспорта справочников

На первой закладке формы настройки импорта счетов необходимо сделать те же настройки, что и в настройках импорта/экспорта справочников (рис. А.4).

Продолжения приложения А

Рис. А.4 Первая закладка настройки импорта счетов

На второй закладке расположены параметры импорта счетов (Рис. А.5). Описание каждого параметра приведено в таблице A.1.

Таблица А.1

Описание параметров импорта счетов

Параметр

Описание

Способ импорта

Стандартный импорт или импорт с учетом скидок

Импорт детализаций по счету

Импорт деталей счета

Режим  импорт

Не создавать дубликаты; создавать дубликаты после подтверждения; создавать дубликаты

Тип создаваемых ЗЗ

Заказ/закупка; Предложение

Перезаписывать только с типом предложение

Обновлять импортируемые счета, только в статусе предложение

Очистка ПБД

Не очищать; Очищать

Игнорировать отсутствующие в базе договоры


Продолжение приложения А

Продолжение таблицы А.1

Внешние коды по КА

При импорте передаются внешние коды контрагентов

Разрешать нулевую цену в строках

Уникальный внешний ID

Проверка уникальности данных в ПБД перед импортом

Брать КА из договора

При импорте подтягивать контрагента из договора

Брать валюту из договора

При импорте подтягивать валюту счета из договора

Проверять КА из договора (с префиксом)

Проверять указанный в ПБД контрагент с контрагентом, указанным в договоре

Пересчитывать суммы с включенным налогом

Автоматическое сопоставление по валюте

Валюта, по которой осуществлять автоматическое сопоставление

Кодировка utf-8

Рис. А.5 Вторая закладка настройки импорта счетов


Продолжение приложения А

Вызов процедур обмена данными и журнала импорта/экспорта происходит из меню Администрирование - Периодические операции (рис. А.6).

Рис. А.6 Вызов процедур обмена данными

Перед началом импорта счетов в Axapta должны быть загружены все связанные с биллингом справочники (клиенты, договора, услуги и прочее). Форма настройки групп импорта справочников представлена на рис. А.7 и А.8.

Продолжение приложения А

Рис. А.7 – Первая закладка формы групп импорта

Рис. А.8 Вторая закладка формы групп импорта

Вначале пользователь формирует группу импорта, далее он переходит на закладку «Справочники» и с помощью кнопки «Добавить» выбирает справочники, которые нужно будет загружать в Axapta через указанную группу. Добавление справочников показано на рисунке А.9.

Продолжение приложения А

Рис. А.9 Форма выбора справочников импорта

Пользователь может добавить в одну группу от одного до всех импортируемых справочников. Кнопка «Обновить» используется в случае изменения кода соответствующих классов импорта.

После того как группа импорта настроена можно импортировать данные, кнопка «Импорт» (рис. А.7). В появившемся диалоговом окне (рис. А.10) отображается импортируемая группа справочников и поле «Импортируемый пакет», в котором можно указать идентификатор пакета, в этом случае данные при импорте будут браться в разрезе этого идентификатора (если поле не заполнять, то будут импортированы все данные.

Рис. А.10 Первая закладка диалогового окна импорта справочников

Продолжение приложения А

На закладке «Пакет» (рис. А.11) можно настроить пакетное выполнение импорта справочников, для этого необходимо проставить признак «Пакетная обработка» и при желании выбрать группу пакетов (если пакетный сервер будет запускаться в последующем от конкретной группы пакетов).

Рис. А.11 Вторая закладка диалогового окна импорта справочников

При нажатии на кнопку «Повторение» открывается окно настройки повторений выполнения задания импорта справочников (в случае пакетного выполнения заданий). Интуитивно понятные настройки показаны на рис. А.12.

Рис. А.12 Форма настроек периодического исполнения заданий

Продолжение приложения А

В случае если был выбран пакетный режим импорта, задание становится в очередь, и будет выполнено в указное время при запущенном пакетном сервере. В противном случае импорт начинается сразу при нажатии кнопки «Ok».

После импорта в окне сообщений Axapta выводятся данные об импорте, пример окна приведен на рис. А.13.

Рис. А.13 Результаты импорта группы импорта справочников

Операция экспорта справочников проходит по аналогичной схеме. Единственное отличие наличие в деталях групп экспорта кнопки «Параметры» (рис. А.14), по которой можно задать динамические параметры экспорта данных из таблиц Axapta.

Продолжение приложения А

Рис. А.14 Вторая закладка формы настроек групп экспорта справочников

В параметрах экспорта (рис. А.15) можно выбирать период модификации данных для выгрузки или с помощью дат или через период выгрузки (выгружать данные можно за последний день, за последние 3 дня, за последнюю неделю, за последний месяц). Признак «Выгружать только новые записи» позволяет экспортировать только те данные, которые были изменены в Axapta после последней выгрузки данного справочника.

Рис. А.15 Параметры экспорта справочника

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

Продолжение приложения А

Если данные нужно сначала загрузить в ПБД из шаблонов Excel, то запускается макрос загрузки счетов в ПБД, после этого открывается диалоговое окно, в котором необходимо выбрать загружаемый файл (рис. А.16 и А.17).

Рис. А.16 Форма переноса данных из Excel в ПБД

Рис. А.17 Окно выбора файла

После того как пользователь выбрал файл со счетами из биллинговой системы и нажал кнопку открыть начинается процесс экспорта счетов в ПБД. После успешного окончания этого процесса выдается сообщение, представленное на рис. А.18.

Рис. А.18 Сообщение о переносе данных в ПБД

Продолжение приложения А

Далее можно переходить в Axapta и запускать периодическую операцию импорта счетов из ПБД (рис. А.19). При необходимости можно поставить задание в очередь пакетной обработки.

Рис. А.19 Диалоговое окно импорта счетов

После импорта счетов в Axapta выводится информационное окно с результатами импорта (рис. А.20).

Рис. А.20 Сообщение о результатах импорта счетов

Продолжение приложения А

Результаты импорта счетов можно увидеть в форме заказов модуля Расчеты с клиентами – рис. А.21.

Рис. А.21 Форма заказов


ПРИЛОЖЕНИЕ Б

ЛИСТИНГ ПРОГРАММЫ

class GM_ImportBillCls extends RunBaseBatch

{

   //Основной класс импорта счетов из ПБД

   #GM_ImportBillMacro

   boolean                     bRunAvi;

   FormRun                     animationForm;

   UserConnection          logConnection;

   OdbcConnection          CDefault;

   OdbcConnection          CDefault2;

   OdbcConnection          CDefaultUpdate;

   container               KnownTables;

   boolean                 bAllowBadContract;

   boolean                 bBlockedLines;

   boolean                 bClearDB;

   boolean                 bImportDetails;

   boolean                 bCheckUniqueExtId;

   GM_ExpandedImport       expandedImport;

   GM_TransDocImportMode   ImportMode;

   GM_IMPORTBATCH          IMPORTBATCH;

   boolean                 bInitialTblApprove_Init;

   FormRun                 formInitialTblApprove;

   RContractPartnerType    CPType;

   str                     ExchangeTypeId;

   GM_PeriodsIdService     PeriodId;

   companyinfo             companyinfo;

   // Счетчики документов

   int                     nDocTableCounterAll;

   int                     nDocTableCounterOk;

   boolean                 bPurchNotSales;

   boolean                 bOracleGlobal;

   SqlStatementExecutePermission   permission;

   TransDate               toDate, fromDate;

   queryRun                queryRun;

}

// метод построения запроса на удаление

str buildDeleteClause(str _table, str _where)

{

   if (_where)  _where = "where " + _where;

   return StrFmt("delete from %1 %2", _Table, _where);

}

// метод построения запроса на вставку

str BuildInsertClause(str Table, container Data)

{

   str         strFields = '';

   str         strValues = '';

   str         strField;

   str         strValue;

   int         nCount = conlen(Data);

   int         i;

   container   Pair;

   ;

   this.FieldsCheck(Table, Data);

   for (i = 1; i <= nCount; i++)

Продолжение приложения Б

   {

       Pair = conpeek(Data, i);

       if (conlen(Pair) < 2) break;

       strField = conpeek(Pair, 1);

       strValue = conpeek(Pair, 2);

       if (bOracleGlobal)

       {

           strFields += ((i != 1)? ",":"") + strField + "";

           strValues += ((i != 1)? ",":"") + strValue;

       }

       else

       {

           strFields += ((i != 1)? ",[":"[") + strField + "]";

           strValues += ((i != 1)? ",":"") + strValue;

       }

   }

   return StrFmt("insert into %1 (%2) values (%3)", Table, strFields, strValues);

}

// метод построения запроса на выборку

str BuildSelectClause(str Table, container Data, str _Tail)

{

   str         strFields = '';

   str         strField;

   int         nCount = conlen(Data);

   int         i;

   container   Pair;

   ;

   this.FieldsCheck(Table, Data);

   for (i = 1; i <= nCount; i++)

   {

       Pair = conpeek(Data, i);

       if (conlen(Pair) < 2) break;

       strField = conpeek(Pair, 1);

       if (bOracleGlobal)

           strFields += ((i == 1)? "" : ",") + strField + "";

       else

           strFields += ((i == 1)? "[" : ",[") + strField + "]";

   }

   return  bOracleGlobal ? StrFmt("select %1 from %2 %3", strFields, Table, _Tail) : StrFmt("select %1 from [%2] %3", strFields, Table, _Tail);

}

// метод построения запроса на обновление

str BuildUpdateClause(str Table, container Data, str _where)

{

   str         strPairs = '';

   str         strField;

   str         strValue;

   int         nCount = conlen(Data);

   int         i;

   container   Pair;

   ;

   this.FieldsCheck(Table, Data);

   for (i = 1; i <= nCount; i++)

   {

       Pair = conpeek(Data, i);

       if (conlen(Pair) < 2) break;

       strField = conpeek(Pair, 1);

       strValue = conpeek(Pair, 2);

       if (bOracleGlobal)

Продолжение приложения Б

           strPairs += ((i == 1)? " " : ",") + strField + "="+strValue;

       else

           strPairs += ((i == 1)? "[" : ",[") + strField + "]="+strValue;

   }

   if (_where)  _where = "where " + _where;

   return bOracleGlobal ? StrFmt("update %1 set %2 %3", Table, strPairs, _where) : StrFmt("update [%1] set %2 %3", Table, strPairs, _where);

}

//метод проверки на уникальность счтеов в ПБД

boolean checkForUniqueExternalId(OdbcConnection _conn = CDefault)

{

   boolean                     ret = true;

   Str                         SQL;

   Statement                   S = _conn.createStatement();

   GM_ODBCResultSet            rst;

   ResultSet                   R;

   List                        listNonUniqueId = new List(Types::Container);

   ListIterator                it;

   GM_BillSystemId             SystemId;

   GM_ExternalId               ExternalId;

   ;

   SQL = strFmt("select count(*) as Cnt, State, ImportBatch, SystemId, ExternalId" +

         " from %1" +

         " group by State, ImportBatch, SystemId, ExternalId" +

         " having count(*) > 1 AND State = 0 AND IMPORTBATCH='%2'",

         #TBL_DOCTABLE, IMPORTBATCH);

   permission = new SqlStatementExecutePermission(SQL);

   permission.assert();

   rst = new GM_ODBCResultSet(s.executeQuery(SQL), #TBL_DOCTABLE);

   CodeAccessPermission::revertAssert();

   while (rst.next())

   {

       SystemId = rst.getString("SystemId");

       ExternalId = rst.getString("ExternalId");

       listNonUniqueId.addEnd([SystemId, ExternalId]);

   }

   if (listNonUniqueId.elements() > 0)

   {

       ret = false;

       this.Log(#TBL_DOCTABLE, '', '', '', '', true, "@GMC29366");

       it = new ListIterator(listNonUniqueId);

       while (it.more())

       {

           [SystemId, ExternalId] = it.value();

           this.Log(#TBL_DOCTABLE, SystemId, ExternalId, '', '', true, "@GMC29367");

           it.next();

       }

   }

   return ret;

}

// метод очистки данных в таблицах ПБД

boolean ClearTables(OdbcConnection _conn = CDefault)

Продолжение приложения Б

{

   int                 i;

   Statement           S = _conn.createStatement();

   str                 strSQL;

   ;

   try

   {

       if (bClearDB)

       {

           for (i = 1; i <= conlen(KnownTables); i++)

           {

               strSQL = StrFmt("delete from [%1] where IMPORTBATCH='%2'", conpeek(conpeek(KnownTables, i), 1), IMPORTBATCH);

               permission = new SqlStatementExecutePermission(strSQL);

               permission.assert();

               S.executeUpdate(strSQL);

               CodeAccessPermission::revertAssert();

           }

       }

       else

       {

           info('@GMC2410');

       }

       return true;

   }

   catch (Exception::Error)

   {

   }

   catch (Exception::Internal)

   {

   }

   catch (Exception::DDEerror)

   {

   }

   error("@GMC2411");

   return false;

}

//метод закрытия формы анимации (прогресса процесса импорта)

void CloseAni()

{

   if (bRunAvi)

   {

       animationForm.close();

   }

   bRunAvi = false;

}

// метод вызова формы параметров импорта

void config()

{

   formRun     newForm;

   newForm  = ClassFactory.formRunClass(new Args(formStr(GM_ImportBillSetup)));

   newForm.init();

   newForm.run();

   newForm.wait();

}

// метод подключение к ПБД

boolean Connect()

Продолжение приложения Б

{

   GM_ImportBill           dsnParm = GM_ImportBill::find();

   GM_ImportBillParameters parms = GM_ImportBillParameters::find();

   boolean                 bOk = true;

   LoginProperty           LPSrc;

   if (dsnParm.RecId == 0)

   {

       error("@GMC2412");

       return false;

   }

   if (!dsnParm.DSN)

   {

       error("@GMC3079");

       bOk = false;

   }

   if (dsnParm.UsrPwd)

   if (!dsnParm.Usr)

   {

       error("@GMC3080");

       bOk = false;

   }

   if (!bOk) return false;

   bAllowBadContract   = parms.AllowBadContract == NoYes::Yes;

   ImportMode          = parms.ImportMode;

   bClearDB            = parms.ClearDB == GM_ImportClearDB::Enabled;

   bBlockedLines       = parms.BlockedLines == NoYes::Yes;

   expandedImport      = parms.ExpandedImport;

   bImportDetails      = parms.ImportDetails;

   bCheckUniqueExtId   = parms.CheckUniqueExternalId;

   LPSrc = new LoginProperty();

   LPSrc.setDSN(dsnParm.DSN);

   if (dsnParm.UsrPwd)

   {

       //LPSrc.setUsername(dsnParm.Usr);

       //LPSrc.setPassword(GM_ImportBillCls::Encode(dsnParm.Pwd));

       LPSrc.setOther(strfmt("DSN=%1;Uid=%2;Pwd=%3", dsnParm.DSN, dsnParm.Usr, dsnParm.Pwd));

   }

   CDefault = new OdbcConnection(LPSrc);

   if (!CDefault)

   {

       error("@GMC3081");

       return false;

   }

   CDefault2 = new OdbcConnection(LPSrc);

   if (!CDefault2)

   {

       error("@GMC3081");

       return false;

   }

   CDefaultUpdate = new OdbcConnection(LPSrc);

   if (!CDefaultUpdate)

   {

       error("@GMC3081");

       return false;

   }

   return true;}

Продолжение приложения Б

// метод создания таблицы в ПБД 

void CreateTable(str strTable, OdbcConnection _conn = CDefault)

{

   Statement           S = _conn.createStatement();

   str                 strField;

   str                 strExtra;

   str                 strFields = '';

   int                 nCount;

   int                 i;

   container           Pair;

   container           Fields;

   boolean             bOracle = _conn.odbcGetInfoStr(17) == "Oracle";

   str                 sqlString;

   ;

   info(StrFmt('@GMC2413', strTable)); infolog.redrawAllWindows();

   Fields = this.Fields(strTable, bOracle);

   nCount = conlen(Fields);

   GM_Global::dropTableIfExist(S, strTable, bOracle);

   for (i = 1; i <= nCount; i++)

   {

       Pair = conpeek(Fields, i);

       if (conlen(Pair) < 2) break;

       strField = conpeek(Pair, 1);

       strExtra = conpeek(Pair, 2);

       if (!this.StrContains(strExtra, 'default'))

       {

           if (strExtra == #FLD_IDENTITY)

           {

               strExtra = bOracle  ? strfmt("int not null constraint pk_id_%1 primary key", strTable)

                                   : "[bigint] IDENTITY (1, 1) NOT NULL";

           }

           else if (this.StrContains(strExtra, 'varchar'))

           {

               strExtra += " DEFAULT ''";

           }

           else if (this.StrContains(strExtra, 'bigint'))

           {

               strExtra += " DEFAULT 0";

           }

           else if (this.StrContains(strExtra, 'int'))

           {

               strExtra += " DEFAULT 0";

           }

           else if (this.StrContains(strExtra, 'numeric'))

           {

               strExtra += " DEFAULT 0";

           }

           else if (this.StrContains(strExtra, 'float'))

           {

               strExtra += " DEFAULT 0";

           }

           else if (this.StrContains(strExtra, 'real'))

           {

               strExtra += " DEFAULT 0";

           }

       }

       if (bOracle)

           strFields += ((i == 1)? " ":", ") + strField + " " + strExtra;

Продолжение приложения Б

       else

           strFields += ((i == 1)? "[":",[") + strField + "] " + strExtra;

   }

   if (bOracle)

       sqlString = "CREATE TABLE " + strTable + " ( " + strFields + " )";

   else

       sqlString = "CREATE TABLE [" + strTable + "] (" + strFields + ", primary key (RecNo)) ON [PRIMARY]";

   permission = new SqlStatementExecutePermission(sqlString);

   permission.assert();

   S.executeUpdate(sqlString);

   CodeAccessPermission::revertAssert();

   this.Log(strTable, '', '', '', '', false, '@GMC2414');

}

// метод создания таблиц в ПБД

boolean CreateTables(OdbcConnection _conn = CDefault)

{

   int i;

   ;

   if (Box::yesNo('@GMC2415', DialogButton::No) != DialogButton::Yes) return false;

   try

   {

       for (i = 1; i <= conlen(KnownTables); i++)

       {

           this.CreateTable(conpeek(conpeek(KnownTables, i), 1));

       }

       return true;

   }

   catch (Exception::Error)

   {

   }

   catch (Exception::Internal)

   {

   }

   catch (Exception::DDEerror)

   {

   }

   error("@GMC2417");

   return false;

}

//метод удаления таблиц ПБД

void DropTable(Statement S, str Table)

{

   str                     sqlString;

   ;

   sqlString = StrFmt("if exists "+

"(select * from sysobjects where id = object_id(N'[%1]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)"+

"drop table [%1]", Table);

   permission = new SqlStatementExecutePermission(sqlString);

   permission.assert();

   S.executeUpdate(sqlString);

   CodeAccessPermission::revertAssert();

}

// метод возвращающий набор полей таблиц ПБД

container Fields(str Table, boolean _bOracle = false)

Продолжение приложения Б

{

// Описание поля: [<Название поля в ПБД>, <тип поля в ПБД>]

   container   cNull;

   str 30      tInt = "int",

               tBigint =   _bOracle ? "int"    : "bigint",

               tDate =     _bOracle ? "date"   : "datetime",

               tReal =     _bOracle ? "real"   : "float",

               tNumeric =  isConfigurationKeyEnabled(configurationKeyNum(GM_Project4))

                                    ? "numeric (18, 4)" : "numeric (18, 2)",

               tVarchar =  GM_ImportBill::find().utf8 ? "varchar2(%1 char)"  : "varchar(%1)";

   ;

   switch (Table)

   {

       case #TBL_DOCTABLE: return [

               ["RecNo",                   #FLD_IDENTITY],

               ["State",                   tInt],

               ["IMPORTBATCH",             strfmt(tVarchar,20)],

               ["SystemId",                strfmt(tVarchar,20)],

               ["ExternalId",              strfmt(tVarchar,40)],

               ["InvoiceType",             tInt],

               ["RContractCode",           strfmt(tVarchar,10)],

               ["RContractAccount",        strfmt(tVarchar,30)],

               ["ExchangeTypeId",          strfmt(tVarchar,10)],

               ["AccountNum",              strfmt(tVarchar,20)],

               ["PeriodId",                strfmt(tVarchar,21)],

               ["InvoiceDate",             tDate],

               ["FactureDate",             tDate],

               ["InvoiceNum",              strfmt(tVarchar,30)],

               ["InclTax",                 tInt],

               ["CurrencyCode",            strfmt(tVarchar,5)],

               ["TransTxt",                strfmt(tVarchar,100)],

               ["FixedExchRate",           tReal],

               ["Dimension",               strfmt(tVarchar,18)],

               ["Dimension2_",             strfmt(tVarchar,18)],

               ["Dimension3_",             strfmt(tVarchar,18)],

               ["Dimension4_",             strfmt(tVarchar,18)],

               ["Dimension5_",             strfmt(tVarchar,18)],

               ["Dimension6_",             strfmt(tVarchar,18)],

               ["Dimension7_",             strfmt(tVarchar,18)],

               ["Dimension8_",             strfmt(tVarchar,18)],

               ["Dimension9_",             strfmt(tVarchar,18)],

               ["Dimension10_",            strfmt(tVarchar,18)],

               ["PoolId",                  strfmt(tVarchar,18)],

               ["FixedDueDate",            tDate],

               ["PostingProfile",          strfmt(tVarchar,14)],

               ["ExternalInvoiceId",       strfmt(tVarchar,30)],

               ["TaxAmount",               tNumeric],

               ["CorTransNum",             strfmt(tVarchar,20)],

               ["SalesVendAccount",        strfmt(tVarchar,16)],

               ["GenInvoice4PaymNum",      tInt],          //генерировать номер счета на оплату из номерной серии, а не использовать ExternalId

               ["Invoice4PaymDate",        tDate],     //дата счета на оплату, если отличается от InvoiceDate

               ["FormRemarksGroup",        strfmt(tVarchar,18)],  //группа примечаний к печатным формам

               ["NumberSequenceGroup",     strfmt(tVarchar,18)],

               ["Cancel",                  tInt],

               ["Amount",                  tReal],

               ["TaxPeriod",               strfmt(tVarchar,21)],

Продолжение приложения Б

               ["InternalInvoiceId",       strfmt(tVarchar,30)],

               ["InternalFactureId",       strfmt(tVarchar,30)]

       ];

       break;

       case #TBL_DOCLINE: return [

               ["RecNo",                   #FLD_IDENTITY],

               ["State",                   tInt],

               ["IMPORTBATCH",             strfmt(tVarchar,20)],

               ["ExternalId",              strfmt(tVarchar,40)],

               ["TableRecNo",              tBigint],

               ["ItemId",                  strfmt(tVarchar,30)],

               ["Qty",                     tNumeric],

               ["UnitId",                  strfmt(tVarchar,18)],

               ["TaxFlag",                 tInt],

               ["SalesPrice",              tNumeric],

               ["LineAmount",              tNumeric],

               ["CurrencyCode",            strfmt(tVarchar,5)],

               ["Name",                    strfmt(tVarchar,1000)],

               ["TaxGroup",                strfmt(tVarchar,18)],

               ["TaxItemGroup",            strfmt(tVarchar,18)],

               ["LedgerAccount",           strfmt(tVarchar,16)],

               ["LineServComis",           tNumeric],

               ["LineDiscPrice",           tNumeric],

               ["LineDiscount",            tNumeric],

               ["LineVAT",                 tNumeric],

               ["LineTotalAmount",         tNumeric],

               ['InventBatchId',           strfmt(tVarchar,50)],

               ['InventLocationId',        strfmt(tVarchar,18)],

               ['InventSerialId',          strfmt(tVarchar,50)],

               ['ConfigId',                strfmt(tVarchar,18)],

               ['InventSizeId',            strfmt(tVarchar,18)],

               ['InventColorId',           strfmt(tVarchar,18)],

               ['InventGtdId_RU',          strfmt(tVarchar,30)],

               ['InventRespPers',          strfmt(tVarchar,10)],

               ['InventPostingProfile',    strfmt(tVarchar,10)],

               ['ConsigneeAccount_RU',     strfmt(tVarchar,30)],

               ['ConsignorAccount_RU',     strfmt(tVarchar,30)]

       ];

       break;

   }

   throw error(StrFmt('@GMC2510', Table));

   return cNull;

}

//метод считывания данных по типам из результата запроса

container FillData(ResultSet R, container cFields)

{

   container   cRes;

   container   cField;

   str         strField;

   FieldId     fld;

   int         i;

   int         fldType;

   str 30      per;

   ;

   for (i = 1; i <= conlen(cFields); i++)

   {

       cField = conpeek(cFields, i);

       if (conlen(cField) < 2) break;

       strField = conpeek(cField, 1);

Продолжение приложения Б

       if (strField == '') break;

       switch (this.FldGetAxType(conpeek(cField, 2)))

       {

       case #TYP_BOOL:

           cRes = conins(cRes, i, R.getBoolean(i));

           break;

       case #TYP_DATE:

           per = R.getString(i);

           per = strdel(per, 11, strlen(per));

           cRes = conins(cRes, i, str2date(per,321));

           break;

       case #TYP_INTG:

           cRes = conins(cRes, i, str2int(R.getString(i)));

           break;

       case #TYP_REAL:

           if (bOracleGlobal)

               cRes = conins(cRes, i, R.getReal(i));

           else

               cRes = conins(cRes, i, R.getReal(i));

           break;

       case #TYP_STRN:

           cRes = conins(cRes, i, R.getString(i));

           break;

           }

   }

   return cRes;

}

//метод, возвращающий значение конкретное поля из выбранной записи

anytype GetData(container cData, container cFields, str Field)

{

   int         i;

   container   cField;

   str         strField;

   for (i = 1; i <= conlen(cFields); i++)

   {

       cField = conpeek(cFields, i);

       if (conlen(cField) < 1) break;

       strField = conpeek(cField, 1);

       if (strField == '') break;

       if (Field == strField) return conpeek(cData, i);

   }

   throw error('@GMC2420');

   return 0;

}

// метод импорта счетов из ПБД

boolean Import()

{

   str     strSection;

   boolean bRes = true;

   try

   {

       nDocTableCounterAll     = 0;

       nDocTableCounterOk      = 0;

       strSection = '@GMC2421';

       if (!this.Connect()) return false;

Продолжение приложения Б

       bOracleGlobal = CDefault.odbcGetInfoStr(17) == "Oracle";

       if (!this.SelectPackage(true)) return false;

       if (bCheckUniqueExtId)

       {

           if (!this.checkForUniqueExternalId(CDefault))

               return false;

       }

       this.ShowAni();

       strSection = '@GMC2426';

       if (!this.ImportDoc(CDefault))

       {

           warning('@GMC2427');

           bRes = false;

       }

   }

   catch (Exception::Internal)

   {

       error(StrFmt('@GMC2428', strSection));

       bRes = false;

   }

   this.ClearTables(CDefault);

   this.CloseAni();

   info('@GMC2429');

   info(StrFmt("@GMC2435",             nDocTableCounterAll));

   info(StrFmt("@GMC2436",          nDocTableCounterOk));

   return bRes;

}

//метод импорта пакета счетов

boolean ImportDoc(OdbcConnection _conn = CDefault)

{

   GM_ImportBillDocTableCls    ImportBillDocTableCls = GM_ImportBillDocTableCls::construct();

   boolean                     bInTTS = false;

   boolean                     bPrevRecSuccess;

   str                         SQL;

   str                         strTail;

   Statement                   S = _conn.createStatement();

   Statement                   SLine = CDefault2.createStatement();

   container                   cFieldsLine;

   container                   cDataLine;

   ResultSet                   RLine;

   ResultSet                   R;

   container                   cFields;

   container                   cData;

   boolean                     bOk = true;

   boolean                     bLineOk = true;

   str                         strMsg;

   SysOperationProgress        pr = new SysOperationProgress();

   ;

   if (Appl.ttsLevel())

   {

       ttscommit;

   }

Продолжение приложения Б

   select companyinfo;

   cFieldsLine = this.FieldsExclude(this.Fields(#TBL_DOCLINE), ["State","IMPORTBATCH"]);

   cFields = this.FieldsExclude(this.Fields(#TBL_DOCTABLE), ["State","IMPORTBATCH"]);

   if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project16)))

       strTail = strFmt("where State=0 order by InvoiceType", IMPORTBATCH);

   else

       strTail = strFmt("where State=0 AND IMPORTBATCH='%1' order by InvoiceType", IMPORTBATCH);

   SQL = this.BuildSelectClause(#TBL_DOCTABLE, cFields, strTail);

   permission = new SqlStatementExecutePermission(SQL);

   permission.assert();

   R = S.executeQuery(SQL);

   CodeAccessPermission::revertAssert();

   bPrevRecSuccess = false;

   while (R.next())

   {

       nDocTableCounterAll++;

       processedLines = new List(Types::Integer);

       processedLineDetails = new List(Types::Integer);

       try

       {

           ttsbegin;

           cData = this.FillData(R, cFields);

           bPrevRecSuccess = false;

           bLineok = true && ImportBillDocTableCls.ImportDoc(this, cData, cFields, bAllowBadContract, ImportMode);

           {

               if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project16)))

                   strTail = strFmt("where State=0 and ExternalId='%2'",

                               IMPORTBATCH, this.GetData(cData, cFields, 'ExternalId'));

               else

                   strTail = strFmt("where State=0 and IMPORTBATCH='%1' and ExternalId='%2'",

                               IMPORTBATCH, this.GetData(cData, cFields, 'ExternalId'));

               SQL = this.BuildSelectClause(#TBL_DOCLINE, cFieldsLine, strTail);

               permission = new SqlStatementExecutePermission(SQL);

               permission.assert();

               RLine = SLine.executeQuery(SQL);

               CodeAccessPermission::revertAssert();

               while (RLine.next())

               {

                   cDataLine = this.FillData(RLine, cFieldsLine);

                   ImportBillDocTableCls.ClearLineNum();

                   if (ImportBillDocTableCls.ImportLine(cDataLine, cFieldsLine))

                   {

                       processedLines.addEnd(this.GetData(cDataLine, cFieldsLine, 'RecNo'));

                   }

                   else

                   {

                       bLineOk = false;

Продолжение приложения Б

                   }

               }

               ImportBillDocTableCls.addDelta();

               ImportBillDocTableCls.ClearLineNum();

               strMsg = ImportBillDocTableCls.setTax();

               if (strMsg)

               {

                   bLineOk = false;

                   ImportBillDocTableCls.Err(strfmt("Ошибка в выставлении налогов (RecNo=%1) - %2",this.GetData(cData, cFields, 'RecNo'),strMsg));

               }

               if (bLineOk)

               {

                   ImportBillDocTableCls.ReportDocOk();

                   bPrevRecSuccess = true;

                   nDocTableCounterOk++;

               }

           }

           if (bLineOk)

           {

               ttscommit;

               this.SetState(#TBL_DOCTABLE, StrFmt('RecNo=%1', this.GetData(cData, cFields, 'RecNo')));

               if (processedLines.elements() > 0)

               {

                   listIt = new ListIterator(processedLines);

                   while (listIt.more())

                   {

                       this.SetState(#TBL_DOCLINE, StrFmt('RecNo=%1', listIt.value()));

                       listIt.next();

                   }

               }

               if (processedLineDetails.elements() > 0)

               {

                   listIt = new ListIterator(processedLineDetails);

                   while (listIt.more())

                   {

                       this.SetState(#TBL_DOCDETAILS, StrFmt('RecNo=%1', listIt.value()));

                       listIt.next();

                   }

               }

           }

           else

           {

               ttsabort;

               this.SetState(#TBL_DOCTABLE, StrFmt('RecNo=%1', this.GetData(cData, cFields, 'RecNo')), 3);

               if (processedLines.elements() > 0)

               {

                   listIt = new ListIterator(processedLines);

                   while (listIt.more())

                   {

                       this.SetState(#TBL_DOCLINE, StrFmt('RecNo=%1', listIt.value()), 3);

                       listIt.next();

Продолжение приложения Б

                   }

               }

               if (processedLineDetails.elements() > 0)

               {

                   listIt = new ListIterator(processedLineDetails);

                   while (listIt.more())

                   {

                       this.SetState(#TBL_DOCDETAILS, StrFmt('RecNo=%1', listIt.value()), 3);

                       listIt.next();

                   }

               }

           }

       }

       catch (Exception::Error)

       {

           ttsabort;

           this.Log(#TBL_DOCTABLE, '', '', '', '', true, '@GMC2437');

           bOk = false;

       }

   }

   return bOk;

}

// метод открытия формы выбора пакета счетов для последующего импорта

void importForm()

{

   formRun     newForm;

   newForm  = ClassFactory.formRunClass(new Args(formStr(GM_ImportBill)));

   newForm.init();

   newForm.run();

   newForm.wait();

}

// метод инициализации класса

public boolean init()

{

   boolean ret;

   ret = super();

   KnownTables = [

   [#TBL_DOCTABLE,     true    , 0],

   [#TBL_DOCLINE,      false   , 0]

   ];

   logConnection = new UserConnection();

   Return ret;

}

// метод записи лога

void Log(

           str 50          TableName,

           GM_BillSystemId SystemId,

           GM_ExternalId   ExternalId,

           Num             DocNumAx,

           str 20          LineId,

           boolean         bErr,

           str             Text = '',

Продолжение приложения Б

           boolean         bInfoLog = true

        )

{

   str IMsg = '';

   ;

   GM_IMPORTLOG::Log(IMPORTBATCH, TableName, SystemId,

                     ExternalId, DocNumAx, bErr ? 1 : 0, Text, LineId);

   if (!Text)

   {

       Text = bErr ? '@GMC3072' : 'Ok';

   }

   if (SystemId)

       IMsg += StrFmt('SystemId="%1": ', SystemId);

   if (SystemId)

       IMsg += StrFmt('ExternalId="%1": ', ExternalId);

   IMsg += Text;

   if (bInfoLog)

   {

       if (bErr)

       {

           error(StrFmt('@GMC2453', TableName, IMsg));

       }

       else

       {

           info(StrFmt('@GMC2453', TableName, IMsg));

       }

   }

}

// метод считывания пакетов счетов в ПБД, для последующего их выбора

boolean SelectPackage(boolean bShowErr, OdbcConnection _conn = CDefault)

{

   GM_ImportPackage     _ImportPackage;

   FormRun             _formSelect;

   Args                _args;

   Statement           S;

   ResultSet           R;

   int                 i;

   str                 sCurTbl;

   boolean             bMasterTbl;

   str                 sqlString;

   ;

   delete_from _ImportPackage;

   for (i = 1; i <= conlen(KnownTables); i++)

   {

       [sCurTbl, bMasterTbl] = conpeek(KnownTables, i);

       if (!bMasterTbl) continue;

       S = _conn.createStatement();

       sqlString = StrFmt("select IMPORTBATCH from %1 where State=0 group by IMPORTBATCH", sCurTbl);

       permission = new SqlStatementExecutePermission(sqlString);

       permission.assert();

       R = S.executeQuery(sqlString);

       CodeAccessPermission::revertAssert();

       while (R.next())

       {

           IMPORTBATCH = R.getString(1);

Продолжение приложения Б

           select firstonly _ImportPackage where _ImportPackage.IMPORTBATCH == IMPORTBATCH;

           if (_ImportPackage.RecId == 0)

           {

               _ImportPackage.IMPORTBATCH = IMPORTBATCH;

               _ImportPackage.IMPORTBATCH_NAME = ((IMPORTBATCH != '') ? IMPORTBATCH : '@GMC2454');

               _ImportPackage.insert();

           }

       }

       R.close();

   }

   IMPORTBATCH = '';

   select count(recid) from _ImportPackage;

   switch (_ImportPackage.RecId)

   {

   case 0:

       if (bShowErr) error("@GMC2455");

       return false;

   case 1:

       select _ImportPackage;

       IMPORTBATCH = _ImportPackage.IMPORTBATCH;

       return true;

   default:

       select _ImportPackage;

       _args = new Args();

       _args.name(FormStr(GM_ImportPackage));

       _args.caller(this);

       _args.record(_ImportPackage);

       _formSelect = classFactory.formRunClass(_args);

       _formSelect.init();

       _formSelect.run();

       _formSelect.wait();

       if (_formSelect.closedOk())

       {

           IMPORTBATCH = _args.parm();

           info(StrFmt('@GMC2456', IMPORTBATCH));

           return true;

       }

       else

       {

           if (bShowErr) warning("@GMC2457");

           return false;

       }

   }

}

//метод установки состояния обработанных записей в ПБД

void SetState(str Table, str Cond, int State = 1, str Field = 'State', OdbcConnection _conn = CDefaultUpdate)

{

   Statement           S = _conn.createStatement();

   str                 sqlString;

   ;

   sqlString = StrFmt("update %1 set %2=%3 where %4", Table, Field, State, Cond);

   permission = new SqlStatementExecutePermission(sqlString);

   permission.assert();

   S.executeUpdate(sqlString);

Продолжение приложения Б

   CodeAccessPermission::revertAssert();

}

//метод открытия формы анимации (прогресс процесса импорта)

void ShowAni(str    _str = "@GMC2212")

{

   Args                    args;

   FormStaticTextControl   text;

   FilePath                AnimationFile;

   FormAnimateControl      animationControl;

   ;

   if (bRunAvi) this.CloseAni();

   if (winApi::folderExists(Xinfo::directory(DirectoryType::INCLUDE) + "107.avi"))

   {

       animationFile = Xinfo::directory(DirectoryType::INCLUDE) + "107.avi";

       args = new Args(formstr(GM_AnimationBox));

       animationForm = ClassFactory.formRunClass(args);

       animationForm.init();

       animationForm.design().caption(_str);

       animationControl = animationForm.design().controlName("AnimationControl");

       animationControl.animateFile(animationFile);

       text = animationForm.design().controlName("Text");

       text.text("@GMC2458");

       animationForm.run();

       animationControl.play();

   }

   bRunAvi = true;

}

//главный метод класса

static void main(Args args)

{

   GM_ImportBillCls    ImportCls;

   ;

   ImportCls = new GM_ImportBillCls();

   ImportCls.init();

   if (args.parmEnumType() != enumnum(GM_BillingRunType))

       throw error("@GMC2461");

   switch (args.parmEnum())

   {

       case GM_BillingRunType::execute :

           ImportCls.ImportForm();

       break;

       case GM_BillingRunType::config  :

           ImportCls.Config();

       break;

       case GM_BillingRunType::checkConnection  :

           if (ImportCls.Connect())

           {

               info("@GMC2462");

           }

       break;

       case GM_BillingRunType::createTemplate  :

           if (ImportCls.Connect() && ImportCls.CreateTables())

           {

               Info("@GMC2463");

           }

       break;}}

Продолжение приложения Б

//Основные методы класса GM_ImportBillDocTableCls (вспомогательного класса импорта счетов из ПБД, поштучно)

//метод импорта конкретного счета из ПБД

boolean ImportDoc(  GM_ImportBillCls         _ImportBillCls,

                   container               _cData,

                   container               _cFields,

                   boolean                 _AllowBadContract,

                   GM_TransDocImportMode    _ImportMode)

{

   boolean             bOk;

   ;

   ImportBillCls       = _ImportBillCls;

   cData               = _cData;

   cFields             = _cFields;

   bDocOk              = true;

   bAllowBadContract   = _AllowBadContract;

   ImportMode          = _ImportMode;

   nDocVer             = 1;

   logConn             = ImportBillCls.GetLogConnection();

   tableName = #TBL_DOCTABLE;

   if (!this.LoadGlobalData()) return false;

   if (!this.CheckParseGlobalData())

   {

       this.Err('@GMC2495');

       return false;

   }

   bOk = bPurchNotSales ? this.ProcessPurch() : this.ProcessSales();

   if (!bOk)

   {

       this.Err(StrFmt('@GMC2496', bPurchNotSales ? "@GMC2497" : "@GMC2498"));

       return false;

   }

   DBIDTmp = DBID;

   return true;

}

//метод импорта расшифровки счета

boolean ImportLine( container               _cData,

                   container               _cFields

                 )

{

   boolean bOk;

   ;

   tableName = #TBL_DOCLINE;

   if (!bDocOk) return false;

   cData               = _cData;

   cFields             = _cFields;

   if (!this.LoadLineGlobalData()) return false;

   if (!this.CheckParseLineGlobalData())

   {

       this.Err('@GMC2495');

       return false;

   }

Продолжение приложения Б

   bOk = bPurchNotSales ? this.ProcessPurchLine() : this.ProcessSalesLine();

   if (!bOk)

   {

       this.Err(StrFmt('@GMC2499', bPurchNotSales ? "@GMC2497" : "@GMC2498"));

       return false;

   }

   this.Inf('@GMC2500');

   return true;

}

//метод проверки данных заголовка счета

boolean CheckParseGlobalData()

{

   str             strMsg;

   int             i, DimCnt = enumCnt(SysDimension);

   Currency        currencyTable;

   void CheckDim(int _n, boolean bMondatory = false)

   {

       Dimensions      _dims;

       SysDimension    _sd = _n - 1;

       if (Dim[_n])

       {

           select _dims where _dims.Num == Dim[_n] && _dims.DimensionCode == _sd;

           if (!_dims.RecId)

           {

               this.Err(strfmt("@GMC3087",enum2str(_sd),Dim[_n]));

           }

       }

       else

       {

           if (bMondatory)

           {

               this.Err(strfmt("@GMC3088",enum2str(_sd)));

           }

       }

   }

   if (parms.CheckAccountFromContractPrefix == NoYes::Yes)

   {

       select firstonly RContractTable

       where RContractTable.RContractPartnerType == RContractPartnerType::Cust

          && RContractTable.ExternalId == RContractAccount

          && RContractTable.RContractPartnerCode like "*" + AccountNum;

       if (!RContractTable.RecId)

           this.Err(strfmt("Договор <%1> не соответсвует контрагенту <%2>", RContractAccount, AccountNum));

       else

       {

           AccountNum = RContractTable.RContractPartnerCode;

           if (CurrencyCode == CompanyInfo::find().CurrencyCode)

           {

               if (CurrencyCode != RContractTable.CurrencyCode)

                   this.Err(strfmt("Валюта договора <%1> не соответсвует валюте счета <%2>", RContractTable.CurrencyCode, CurrencyCode));

           }

           else

           {

               if (CurrencyCode != RContractTable.CurrencyCode)

               {

Продолжение приложения Б

                   currencyTable = currency::find(RContractTable.CurrencyCode);

                   if (currencyTable.CurrencyCodeParent != CurrencyCode || currencyTable.Rate != 1)

                       this.Err(strfmt("Валюта договора <%1> не соответсвует валюте счета <%2>", RContractTable.CurrencyCode, CurrencyCode));

                   else

                       CurrencyCode = RContractTable.CurrencyCode;

               }

           }

       }

       if (!bDocOk) return false;

   }

       if (parms.AccountFromContract == NoYes::No)

       {

           if (CurrencyCode)

           {

               if (!Currency::exist(CurrencyCode))

               {

                   this.Err(StrFmt('@GMC2464', CurrencyCode));

               }

           }

           else

           {

               this.Err('@GMC2465');

           }

           if (parms.ExternalAccount == NoYes::Yes)

           {

               if (bPurchNotSales)

               {

                   VendTable = VendTable::findExternalId(AccountNum);

                   if (!VendTable.RecId)

                       this.Err(StrFmt('@GMC2466', AccountNum));

                   else

                   {

                       AccountNum = VendTable.AccountNum;

                       Payment = PaymTerm::find(VendTable.PaymTermId);

                   }

               }

               else

               {

                   CustTable = CustTable::findExternalId(AccountNum);

                   if (!CustTable.RecId)

                       this.Err(StrFmt('@GMC2467', AccountNum));

                   else

                   {

                       AccountNum = CustTable.AccountNum;

                       Payment = PaymTerm::find(CustTable.PaymTermId);

                   }

               }

           }

           else

           {

               if (bPurchNotSales)

               {

                   VendTable = VendTable::find(AccountNum);

                   if (!VendTable)

                       this.Err(StrFmt('@GMC2466', AccountNum));

                   else

                       Payment = PaymTerm::find(VendTable.PaymTermId);

               }

Продолжение приложения Б

               else

               {

                   CustTable = CustTable::find(AccountNum);

                   if (!CustTable)

                       this.Err(StrFmt('@GMC2467', AccountNum));

                   else

                       Payment = PaymTerm::find(CustTable.PaymTermId);

               }

           }

       }

       else //брать КА из договора

       {

           if (bPurchNotSales)

               rContractTable = rContractTable::findExternalId(RContractAccount, RContractPartnerType::Vend);

           else

               rContractTable = rContractTable::findExternalId(RContractAccount, RContractPartnerType::Cust);

           if (!rContractTable.RecId)

           {

               this.Err(strfmt("контракт '%1' не найден в справочнике", RContractAccount));

               return false;

           }

           RContractAccount = rContractTable.RContractAccount;

           RContractCode = rContractTable.RContractCode;

           AccountNum = rContractTable.RContractPartnerCode;

           CurrencyCodeContract = rContractTable.CurrencyCode;

           if (bPurchNotSales)

           {

               VendTable = VendTable::find(AccountNum);

               Payment = PaymTerm::find(VendTable.PaymTermId);

           }

           else

           {

               CustTable = CustTable::find(AccountNum);

               Payment = PaymTerm::find(CustTable.PaymTermId);

           }

       }

   if (!bDocOk) return false;

   if (ExpandedImport == GM_ExpandedImport::Normal && parms.AccountFromContract == NoYes::No)

   {

       if (parms.ExternalAccount == NoYes::Yes)

       {

           if (RContractAccount)

           {

           if (bPurchNotSales)

               rContractTable = rContractTable::findExternalId(RContractAccount, RContractPartnerType::Vend);

           else

               rContractTable = rContractTable::findExternalId(RContractAccount, RContractPartnerType::Cust);

           if (!rContractTable.RecId)

           {

               this.Err(strfmt("контракт '%1' не найден в справочнике", RContractAccount));

               return false;}

Продолжение приложения Б

           RContractAccount = rContractTable.RContractAccount;

           RContractCode = rContractTable.RContractCode;

           }

       }

       else

       {

           if (!RContractCode) if (RContractAccount)

           {

               while select rContractTable

               where rContractTable.RContractAccount == RContractAccount

                  && rContractTable.RContractPartnerCode == AccountNum

                  && rContractTable.RContractPartnerType == CPType

               {

                   RContractCode = rContractTable.RContractCode;

                   this.Warn(StrFmt('@GMC2468', RContractCode));

                   break;

               }

               if (!RContractCode)

               {

                   while select rContractTable

                   where rContractTable.RContractNumber == RContractAccount

                      && rContractTable.RContractPartnerCode == AccountNum

                      && rContractTable.RContractPartnerType == CPType

                   {

                       this.Warn("@GMC2469");

                       RContractAccount = rContractTable.RContractAccount;

                       RContractCode = rContractTable.RContractCode;

                       CurrencyCodeContract = rContractTable.CurrencyCode;

                       break;

                   }

                   if (!RContractCode)

                   {

                       strMsg = strfmt("@GMC2470",RContractAccount);

                       if (bAllowBadContract)

                       {

                           this.Warn(strMsg+"@GMC2471");

                       }

                       else

                       {

                           this.Err(strMsg);

                       }

                   }

               }

           }

           if (RContractCode && RContractAccount)

           {

               if (!RContractTypes::find(CPType, RContractCode))

               {

                   strMsg = StrFmt('@GMC2472', RContractCode);

                   if (bAllowBadContract)

                   {

                       this.Warn(strMsg+"@GMC2473");

                       RContractCode = '';

                       RContractAccount = '';

                   }

                   else

                   {

                       this.Err(strMsg);

                   }

               }

               else

               {

Продолжение приложения Б

                   select firstonly rContractTable

                   index hint ContractTypeCodeAccountIdx

                   where rContractTable.RContractPartnerType == CPType

                      && rContractTable.RContractPartnerCode == AccountNum

                      && rContractTable.RContractCode        == RContractCode

                      && rContractTable.RContractAccount     == RContractAccount;

                   if (!rContractTable)

                   {

                       strMsg = StrFmt('@GMC2474', RContractAccount);

                       if (bAllowBadContract)

                       {

                           this.Warn(strMsg+"@GMC2473");

                           RContractCode = '';

                           RContractAccount = '';

                       }

                       else

                       {

                           this.Err(strMsg);

                       }

                   }

               }

           }

           else

               RContractAccount = '';

       }

   }

   if (ExchangeTypeId)

   {

       ExchangeType = GM_ExchangeType::find(ExchangeTypeId);

       if (!ExchangeType)

       {

           this.Err(StrFmt('@GMC2475', ExchangeTypeId));

           ExchangeTypeId = '';

       }

   }

   if (PeriodId)

   {

       Period = GM_PeriodsService::find(PeriodId);

       if (Period)

       {

           if (!InvoiceDate) if (ExchangeTypeId)

           {

               InvoiceDate = GM_PeriodsService::getCountDate(PeriodId, ExchangeTypeId);

           }

       }

       else

       {

           this.Err(StrFmt('@GMC2476', PeriodId));

       }

   }

   if (!bDocOk)  return false;

   if (PoolId)

   {

Продолжение приложения Б

       if (bPurchNotSales ? (!PurchPool::exist(PoolId)) : (!SalesPool::exist(PoolId)))

       {

           this.Err(StrFmt('@GMC2484', PoolId));

       }

   }

   if (PostingProfile)

   {

       if (bPurchNotSales ? (!VendLedger::exist(PostingProfile)) : (!CustLedger::exist(PostingProfile)) )

       {

           this.Err("@GMC2485");

       }

   }

   else

   {

       if (rContractTable.RecId)

       {

           PostingProfile = rContractTable.ContractPostingProgile;

       }

   }

   CheckDim(1, bFirstDimMandatory);

   for (i=1; i<=DimCnt; i++)

       CheckDim(i);

   if (parms.difCompany)

       company = GM_CompanyDimensions::findAreaId(Dim[1]);

   company = company ? company : curext();

   if (!FixedExchRate && CurrencyCode)

   {

       FixedExchRate = Currency::exchRate(CurrencyCode, INVOICEDATE);

   }

   if (!FixedDueDate)

   {

       if (Payment)

           FixedDueDate = Global::dateMthFwd(InvoiceDate, Payment.NumOfMonths) + Payment.NumOfDays;

       else

           FixedDueDate = InvoiceDate;

   }

   if (! bPurchNotSales && salesVendAccount)

   {

       if (! isConfigurationKeyEnabled(configurationKeyNum(GM_SalesVendAccount)))

       {

           this.Err(StrFmt("@GMC29304",

                   new DictConfigurationKey(configurationKeyNum(GM_SalesVendAccount)).label(),

                   configurationKeyStr(GM_SalesVendAccount)));

       }

       if (! VendTable::exist(salesVendAccount))

       {

           this.Err(StrFmt('@GMC2466', AccountNum));

       }

   }

Продолжение приложения Б

   if (NumberSequenceGroup && !NumberSequenceGroup::exist(NumberSequenceGroup))

   {

       this.Err(StrFmt('Не найдена группа номерных серий "%1"', NumberSequenceGroup));

   }

   return bDocOk;

}

//метод проврки данных деталей счета

boolean CheckParseLineGlobalData()

{

   str         strMsg;

   if (!ItemId)

   {

       this.Err("@GMC2486");

   }

   else

   {

       InventTable = InventTable::find(ItemId);

       if (!InventTable)

       {

           InventTable = InventTable::findExternalId(ItemId);

           if (!InventTable)

               this.Err(StrFmt('@GMC2487', ItemId));

           else

               ItemId = InventTable.ItemId;

       }

   }

   if (UnitId && ExpandedImport == GM_ExpandedImport::Normal)

   {

       if (!Unit::exist(UnitId))

       {

           this.Err(StrFmt('@GMC2488', UnitId));

       }

   }

   if (parms.AllowZeroLinePrice)

   {

       if (PurchSalesPrice < 0)

       {

           this.Err("@GMC2489");

       }

   }

   else

   {

       if (PurchSalesPrice <= 0)

       {

           this.Err("@GMC2489");

       }

   }

   if (Qty == 0)

   {

       this.Err("@GMC29266)");

   }

   if (bDocOk)

   {

       if (!Name) Name = InventTable.ItemName;

   }

Продолжение приложения Б

   if (InventLocationId && !InventLocation::exist(InventLocationId))

       this.Err(StrFmt("Склад %1 не найден в справочнике", InventLocationId));

   if (parms.CurrencyFromContract == NoYes::Yes)

   {

       if (CurrencyCodeLine)

       {

           if (!Currency::exist(CurrencyCodeLine))

           {

               this.Err(StrFmt('@GMC2464', CurrencyCodeLine));

           }

       }

       else

       {

           this.Err('@GMC2465');

       }

   }

   return bDocOk;

}

//метод записи данных заголовка счета в Axapta

boolean ProcessSales()

{

   boolean             bNeedDelete;

   boolean             bNeedStorno;

   boolean             bUpdate;

   RecId               SalesRecId;

   int                 salesTypeTmp;

   GM_FormRemarksGroup remarksGroupLoc;

   SalesParameters     salesParameters = SalesParameters::find();

   ;

   if (parms.recalcInclTaxAmount == NoYes::Yes)

   {

       AmountWithoutTax = Amount - TaxAmount;

       koefInclTax = Amount ? AmountWithoutTax / Amount : 0;

       sumOfLine = 0;

   }

   Address = Address::findActive(tablenum(CustTable), CustTable.RecId, AddressType::Invoice);

   bNeedDelete = false;

   bNeedStorno = false;

   Sales.selectForUpdate(true);

   select firstonly Sales

   order by BillSys_DocVer desc

   where Sales.BillSys_Id == SystemId

      && Sales.ExternalId == ExternalId;

   if (Sales.RecId != 0)

   {

       SalesRecId = Sales.RecId;

       DocNumAx = Sales.SalesId;

       if (Sales.SalesStatus == SalesStatus::Backorder)

       {

           if (parms.OverwriteOnlyQuotation && (Sales.SalesType != SalesType::Quotation))

           {

               this.Inf("@GMC29371");

Продолжение приложения Б

               return false;

           }

           bNeedDelete = true;

       }

       else

       {

           switch(ImportMode)

           {

               case GM_TransDocImportMode::ModeOverwrite:

                   bNeedStorno = true;

                   break;

               case GM_TransDocImportMode::ModeAskOverwrite:

                   bNeedStorno = this.Confirm(Sales.SalesId, Sales.InvoiceAccount);

                   if (!bNeedStorno)

                   {

                       this.Inf("@GMC29369");

                       return false;

                   }

                   break;

               case GM_TransDocImportMode::ModeSkip:

                   this.Inf(strFmt("@GMC29370", enum2str(GM_TransDocImportMode::ModeSkip)));

                   return false;

                   break;

            }

       }

       if (bNeedStorno)

       {

           nDocVer = Sales.BillSys_DocVer + 1;

           this.StornoSales();

           select Sales where Sales.RecId == SalesRecId;

           if (Sales.SalesStatus != SalesStatus::Backorder)

           {

               this.Inf('@GMC2507');

               return false;

           }

       }

       if (bNeedDelete)

       {

           nDocVer = Sales.BillSys_DocVer + 1;

           if (!this.DeleteSales())

           {

               this.Inf('@GMC2508');

               return false;

           }

       }

   }

   DocNumAx = "";

   if (!bUpdate)

   {

       Sales.clear();

       if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project16)))

       {

           Sales.SalesId = ExternalId;

       }

       else

Продолжение приложения Б

       {

           Sales.SalesId = NumberSeq::newGetNum(SalesParameters::numRefSalesId(), true).num(); // Converted to AX-3.0 SSL

       }

       Sales.initValue();

   }

   Sales.RContractAccount      = RContractAccount;

   Sales.RContractCode         = RContractCode;

   Sales.initContractAccount_RU();

   Sales.FixedExchRate         = FixedExchRate;

   Sales.BillSys_Id            = SystemId;

   if (!bUpdate)

   Sales.ExternalId            = ExternalId;

   Sales.BillSys_DocVer        = nDocVer;

   salesTypeTmp = parms.SalesPurchType;

   Sales.SalesType             = salesTypeTmp;

   if (ExchangeTypeId)

   Sales.ExchangeTypeId        = ExchangeTypeId;

   Sales.InvoiceAccount        = AccountNum;

   Sales.TaxGroup              = CustTable.TaxGroup;

   Sales.CustGroup             = CustTable.CustGroup;

   Sales.LanguageId            = CustTable.LanguageId;

   Sales.PeriodsIdService      = PeriodId;

   Sales.InvoiceDate           = InvoiceDate;

   Sales.FactureExternalId_RU  = InvoiceNum;// ? InvoiceNum : this.getNewFactureExternalId_RU();

   Sales.FactureDate_RU        = FactureDate;

   Sales.ExternalInvoiceId     = ExternalInvoiceId;

   Sales.CurrencyCode          = parms.CurrencyFromContract && CurrencyCodeContract ? CurrencyCodeContract : CurrencyCode;

   Sales.FixedDueDate          = FixedDueDate;

   if (PoolId)

   Sales.SalesPoolId           = PoolId;

   if (PostingProfile)

   Sales.PostingProfile        = PostingProfile;

   Sales.SalesStatus           = SalesStatus::Backorder;

   Sales.Dimension = gm_global::setDimension(GM_global::setDimension(Sales.Dimension, Dim),

                                             GM_Global::setDimension(RContractTable.Dimension, CustTable.Dimension));

   Sales.SalesName             = CustTable.Name;

   Sales.CustAccount           = CustTable.AccountNum;

   Sales.DeliveryName          = CustTable.Name;

   Sales.DeliveryDate          = InvoiceDate;

   Sales.ReceiptDateRequested  = InvoiceDate;

   Sales.ShippingDateRequested = InvoiceDate;

   Sales.OtherBankAccount          = rContractTable.GM_OtherBankAccount ? rContractTable.GM_OtherBankAccount : CustTable.GM_OtherBankAccount;

Продолжение приложения Б

   Sales.GM_PaymOnCustAccount      = rContractTable.GM_SalesPaymOnCustAccount ? rContractTable.GM_SalesPaymOnCustAccount : CustTable.GM_SalesPaymOnCustAccount;

   Sales.GM_Invoice4PaymDesignCode = rContractTable.GM_Invoice4PaymDesignCode ? rContractTable.GM_Invoice4PaymDesignCode : CustTable.GM_Invoice4PaymDesignCode;

   Sales.GM_ConsigHypenIfEmpty     = CustFormletterParameters::find().GM_FactConsigHypenIfEmptyDef;

   Sales.GM_SalesVendAccount   = salesVendAccount ? salesVendAccount : Sales.GM_SalesVendAccount;

   if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project1)))

       Sales.NumberSequenceGroup   = NumberSequenceGroup ? NumberSequenceGroup : Sales.NumberSequenceGroup;

   else

       Sales.NumberSequenceGroup   = NumberSequenceGroup ? NumberSequenceGroup : Sales.vendTable_salesVendAccount().GM_SalesNumberSeqGroupId;

   Sales.GM_GenInvoice4PaymNumIfExternal = genInvoice4PaymNum;

   Sales.GM_Invoice4PaymDate   = Invoice4PaymDate;

   if (formRemarksGroup)

   {

       remarksGroupLoc = formRemarksGroup;

   }

   else

   {

       remarksGroupLoc = rContractTable.GM_FormRemarksGroup ? rContractTable.GM_FormRemarksGroup : custTable.GM_FormRemarksGroup;

   }

   Sales.GM_FormRemarksGroup   = remarksGroupLoc;

   if (parms.SettleCurrencyCode == "")

   {

       Sales.SettleVoucher = SettlementType::None;

   }

   else

       if (parms.SettleCurrencyCode == currencyCode)

       {

           Sales.SettleVoucher = SettlementType::OpenTransact;

       }

   if (Address)

   {

       Sales.DeliveryAddress   = Address.Address;

       Sales.DeliveryZipCode   = Address.ZipCode;

       Sales.DeliveryCountryRegionId = Address.CountryRegionId;

       Sales.DeliveryState     = Address.State;

   }

   if (!Sales.DeliveryAddress) Sales.DeliveryAddress   = CustTable.Address;

   if (!Sales.DeliveryZipCode) Sales.DeliveryZipCode   = CustTable.ZipCode;

   if (!Sales.DeliveryCountryRegionId) Sales.DeliveryCountryRegionId   = CustTable.CountryRegionId;

   if (!Sales.DeliveryState)   Sales.DeliveryState     = CustTable.State;

   if (!bDocOk) return false;

   if (!bUpdate)

       Sales.insert();

   DocNumAx = Sales.SalesId;

   Sales.TransTxt              = TransTxt;

Продолжение приложения Б

   Sales.InclTax               = InclTax ? NoYes::Yes : NoYes::No;

   Sales.OriginalTaxAmount     = TaxAmount;

   Sales.TaxPeriod             = TaxPeriod ? TaxPeriod : Sales.TaxPeriod;

   Sales.reservation      = salesParameters.reservation;

   Sales.write();

   return true;

}

//метод записи расшифровки счета в Axapta

boolean ProcessSalesLine()

{

   InventTableModule   _InventTableModule;

   InventDim           InventDim;

   InventItemGroup     inventItemGroup;

   ExchRate            exchRate;

   SalesLn.selectForUpdate(true);

   SalesLn.clear();

   SalesLn.SalesId             = Sales.SalesId;

   SalesLn.ItemId              = ItemId;

   SalesLn.SalesUnit           = UnitId;

   SalesLn.OriginalLineAmount  = LineAmount;

   SalesLn.OriginalCurrencyCode= CurrencyCodeLine;

   InventDim.clear();

   InventDim.ConfigId = ConfigId ? ConfigId : InventDim.ConfigId;

   InventDim.InventSizeId = InventSizeId ? InventSizeId : InventDim.InventSizeId;

   InventDim.InventColorId = InventColorId ? InventColorId : InventDim.InventColorId;

   InventDim.inventDimId = InventDim::findOrCreate(InventDim).inventDimId;

   SalesLn.InventDimId = InventDim.inventDimId;

   SalesLn.Name                    = Name;

   SalesLn.createLine(false, true, true);

   LineNum = num2str(SalesLn.LineNum, -1, 0, -1, -1);

   SalesLn.LineAmount              = LineAmount;

   SalesLn.SalesStatus             = SalesStatus::Backorder;

   SalesLn.SalesQty                = Qty;

   SalesLn.QtyOrdered              = Qty;

   SalesLn.RemainSalesPhysical     = Qty;

   SalesLn.RemainInventPhysical    = Qty;

   if (PurchSalesPrice == 0)

   {

       SalesLn.SalesPrice          = 0;

   }

   else

   {

       SalesLn.SalesPrice              = PurchSalesPrice;

   }

   if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project16)) && LineAmount)

   {

       SalesLn.LineAmount = LineAmount;

       SalesLn.SalesPrice = (Qty) ? currency::amount(LineAmount / Qty) : 0;

   }

   if (parms.CurrencyFromContract == NoYes::Yes)

Продолжение приложения Б

   {SalesLn.LineAmount = CurrencyCodeLine != SalesLn.CurrencyCode ? currency::curAmount2CurAmount(LineAmount,CurrencyCodeLine,SalesLn.CurrencyCode,InvoiceDate) : LineAmount;

       SalesLn.SalesPrice = (Qty) ? Currency::amount(SalesLn.LineAmount / Qty) : 0;

   }

   if (parms.recalcInclTaxAmount == NoYes::Yes)

   {

       SalesLn.LineAmount = Currency::amount(LineAmount * koefInclTax);

       sumOfLine += SalesLn.LineAmount;

       SalesLn.SalesPrice = (Qty) ? currency::amount(SalesLn.LineAmount / Qty) : 0;

   }

   SalesLn.Blocked                 = ImportBillCls.get_bBlockedLines() ? NoYes::Yes : NoYes::No;

   TaxGroup = TaxGroup ? TaxGroup : CustTable.TaxGroup;

   SalesLn.TaxGroup        = TaxGroup ? TaxGroup : SalesLn.TaxGroup;

   _InventTableModule = InventTableModule::find(ItemId, ModuleInventPurchSales::Sales);

   if (_InventTableModule) TaxItemGroup = TaxItemGroup ? TaxItemGroup : _InventTableModule.TaxItemGroupId;

   SalesLn.TaxItemGroup    = TaxItemGroup ? TaxItemGroup : SalesLn.TaxItemGroup;

   SalesLn.LedgerAccount   = LedgerAccountS ? LedgerAccountS : LedgerAccountS;

   InventDim = SalesLn.inventDim(true);

   InventDim.InventBatchId = InventBatchId ? InventBatchId : InventDim.InventBatchId;

   InventDim.InventLocationId = InventLocationId ? InventLocationId : InventDim.InventLocationId;

   InventDim.InventSerialId = InventSerialId ? InventSerialId : InventDim.InventSerialId;

   InventDim.ConfigId = ConfigId ? ConfigId : InventDim.ConfigId;

   InventDim.InventSizeId = InventSizeId ? InventSizeId : InventDim.InventSizeId;

   InventDim.InventColorId = InventColorId ? InventColorId : InventDim.InventColorId;

   InventDim.InventGtdId_RU = InventGtdId_RU ? InventGtdId_RU : InventDim.InventGtdId_RU;

   InventDim.InventRespPers = InventRespPers ? InventRespPers : InventDim.InventRespPers;

   InventDim.InventPostingProfile = InventPostingProfile ? InventPostingProfile : InventDim.InventPostingProfile;

   if (isConfigurationKeyEnabled(configurationKeyNum(GM_Project16)))

   {

       inventTable = InventTable::find(ItemId);

       inventItemGroup = InventItemGroup::find(inventTable.ItemGroupId);

       if (inventItemGroup)

       {

           InventDim.inventDimDefect = inventItemGroup.InventSortId;

       }

   }

   InventDim.inventDimId = InventDim::findOrCreate(InventDim).inventDimId;

   SalesLn.InventDimId = InventDim.inventDimId;

   SalesLn.update();

   return true;}


Продолжение приложения Б

//Родительский класс для экспорта справочников и его основные методы

//От этого справочника должны наследоваться классы для экспорта конкретных таблиц справочников

abstract class GM_ExportDictTableClass extends runBase//Batch

{

   #define.FLD_IMPORTFLAG('Import_Flag')

   #define.FLD_DATAAREAID('DATAAREAID')

   #define.FLD_DATEOFOPER('Export_Date')

   #define.FLD_RECID('RecId')

   OdbcConnection  conn;

   date            curDate;

   boolean         bNewRec;

   date            dateToRec, dateFromRec, lastImportDate;

   GM_ExportPeriod exportPeriod;

   DialogField     dialogNewRec, dialogDateToRec, dialogDateFromRec, dialogExportPeriod;

   boolean         bAll;

   GM_ImportFlag   status;

   boolean         bOracle;

   str 50          dateType, realType;

   TimeOfDay       lastImportTime;

   QueryRun        queryRun;

   map             mapFields,mapResults;

   TableId         baseTableId;

   boolean         utf8;

   SqlStatementExecutePermission   permission;

}

//метод проверки стурктуры (в дочерних классах должен быть перекрыт при необходимости)

boolean checkStructure()

{

   return(false);

}

//основной метод экспорта данных (в дочерних классах при необходимости может быть перекрыт)

public boolean doExport(GM_ExportDictPresetCode _exportPresetCode,

   Common _common = new DictTable(this.baseTableId()).makeRecord(),

   str _condDel = "")

{

   int                 nCounter;

   int                 nCounterIns;

   int                 nCounterUpd;

   Str                 Cond, SQL;

   Str                 Table = this.tableName();

   boolean             bExists;

   boolean             bNew;

   boolean             bNeedUpdate;

   Statement           S = conn.createStatement();

   GM_ODBCResultSet    rst;

   GM_ODBCTableMan     GM_ODBCTableMan;

   container           con;

   ;

   this.mapFields();

   if (this.checkStructure())

   {

       GM_ODBCTableMan = new GM_ODBCTableMan();

Продолжение приложения Б

       if (!GM_ODBCTableMan.checkODBCTable(tableId2Name(_common.TableId), Table, conn, this.fields()))

       {

           info(StrFmt("@GMC1029", Table));

           return false;

       }

   }

   try

   {

       nCounter = 0;

       nCounterIns = 0;

       nCounterUpd = 0;

       if (_condDel == "DELETE ALL")

       {

           SQL = this.buildDeleteClause("");

           permission = new SqlStatementExecutePermission(SQL);

           permission.assert();

           S.executeUpdate(SQL);

           CodeAccessPermission::revertAssert();

           this.Log(_exportPresetCode, true, '', "@GMC29124");

       }

       else

       {

           this.initParams();

           this.SetDeletedFlag(S, _condDel);

       }

       while (queryRun.next())

       {

           _common = queryRun.getNo(1);

           Cond = StrFmt("RecId=%1 AND %2=%3", this.odbcInt64(_common.RecId), #FLD_DATAAREAID, this.odbcStr(curExt()));

           SQL = this.BuildSelectClause(this.Fields(), Cond);

           permission = new SqlStatementExecutePermission(SQL);

           permission.assert();

           rst = new GM_ODBCResultSet(S.ExecuteQuery(SQL), Table);

           CodeAccessPermission::revertAssert();

           bExists     = rst.next();

           bNeedUpdate = false;

           bNew        = false;

           [bNeedUpdate,con] = this.mapResults(bExists,rst,_common,this.axResultUserFields(_common));

           if (bExists)

           {

               if (bNeedUpdate)

               {

                   SQL = this.BuildUpdateClause(this.mapUpdResults(GM_ImportFlag::UpdatedRec),Cond);

                   permission = new SqlStatementExecutePermission(SQL);

                   permission.assert();

                   S.executeUpdate(SQL);

                   CodeAccessPermission::revertAssert();

                   nCounterUpd++;

               }

               else

Продолжение приложения Б

               {if (bAll)

                   {

                       this.setNewFlag(S, Cond);

                   }

               }

           }

           else

           {

               SQL = this.BuildInsertClause(this.mapUpdResults(GM_ImportFlag::NewRec));

               permission = new SqlStatementExecutePermission(SQL);

               permission.assert();

               S.executeUpdate(SQL);

               CodeAccessPermission::revertAssert();

               nCounterIns++;

           }

           nCounter++;

       }

       this.Log(_exportPresetCode, true, '',

                StrFmt('@GMC17344', nCounter, nCounterIns, nCounterUpd));

       return true;

   }

   catch (Exception::Error)

   {

       error("@GMC3072" + ": " + Table);

   }

   catch (Exception::Internal)

   {

       error("@GMC2005: " + ": " + Table);

   }

   catch (Exception::DDEerror)

   {

       error("@GMC2006: " + ": " + Table);

   }

   this.Log(_exportPresetCode, false, '');

   return false;

}

//класс экспорта справчоника контрагентов

class GM_ExportDictTable_CustVend extends GM_ExportDictTableClass

{

}

//метод перечисляющий экспортируемые поля

container axExportFields()

{

   container ret;

   ret += "AccountNum";

   ret += "Name";

   ret += "NameAlias";

   ret += "Address";

   ret += "Phone";

   ret += "Inn_Ru";

   ret += "Kpp_Ru";

   ret += "ExternalId";

   ret += "CurrencyCode";

   ret += "InvoiceAccount";

   ret += "PaymTermId";

   ret += "Blocked";

   ret += "DlvTerm";

   ret += "DlvMode";

Продолжение приложения Б

   ret += "ClearingPeriod";

   ret += "Email";

   ret += "CreditRating";

   ret += "TaxGroupId";

   ret += "PaymMode";

   ret += "BankAccount";

   ret += "PaymSched";

   ret += "ContactPersonId";

   ret += "LineOfBusinessId";

   ret += "DestinationCodeId";

   ret += "PaymSpec";

   ret += "BankCentralBankPurposeText";

   ret += "BankCentralBankPurposeCode";

   ret += "OKATO_RU";

   ret += "Name4PO";

   ret += "BankOrderOfPayment_RU";

   ret += "PostingProfilePrepayment";

   ret += "TaxItemGroupPrepayment";

   ret += "TaxGroupPrepayment";

   ret += "GM_RContractAccount";

   ret += "GM_RContractCode";

   ret += "Dimension";

   ret += #FLD_DATAAREAID;

   ret += #FLD_RECID;

   return ret;

}

//метод экспорта (перекрытый)

public boolean doExport(GM_ExportDictPresetCode _exportPresetCode,Common _common,str _condDel = "")

{

   boolean                 bExists;

   boolean                 bNew;

   boolean                 bNeedUpdate;

   int                     nCounter;

   int                     nCounterIns;

   int                     nCounterUpd;

   CustTable               CustTable;

   VendTable               VendTable;

   CustVendTable           CustVendTable;

   Statement               S = conn.createStatement();

   GM_ODBCResultSet        rst;

   str                     Table   =  this.tableName();

   Str                     Cond, SQL,SQLselect;

   Name                    Name;

   NameAlias               NameAlias;

   RContractPartnerType    PartnerType;

   CustVendGroupId         groupId;

   str                     DataAreaId;

   map                     cvMap = new Map(Types::Integer,Types::Integer);

   container               con;

   void InCycleFunc()

   {

           Cond = StrFmt("AccountNum=%1 and PartnerType=%2 and %3",

             this.odbcStr(CustVendTable.AccountNum), int2str(PartnerType), this.AreaCond());

           SQL = this.BuildSelectClause(this.Fields(), Cond);

           permission = new SqlStatementExecutePermission(SQL);

           permission.assert();

           rst = new GM_ODBCResultSet(S.ExecuteQuery(SQL), Table);

Продолжение приложения Б

           CodeAccessPermission::revertAssert();

           bExists     = rst.next();

           bNeedUpdate = false;

           bNew        = false;

           [bNeedUpdate,con] = this.mapResults(bExists,rst,CustVendTable,this.axResultUserFields(CustVendTable));

           if (bExists)

           {

               if (bNeedUpdate)

               {

                   SQL = this.BuildUpdateClause(this.mapUpdResults(GM_ImportFlag::UpdatedRec), Cond);

                   permission = new SqlStatementExecutePermission(SQL);

                   permission.assert();

                   S.executeUpdate(SQL);

                   CodeAccessPermission::revertAssert();

                   nCounterUpd++;

               }

               else

               {

                   if (bAll)

                   {

                       this.setNewFlag(S, Cond);

                   }

               }

           }

           else

           {

               SQL = this.BuildInsertClause(this.mapUpdResults(GM_ImportFlag::NewRec));

               permission = new SqlStatementExecutePermission(SQL);

               permission.assert();

               S.executeUpdate(SQL);

               CodeAccessPermission::revertAssert();

               nCounterIns++;

           }

           cvMap.insert(CustVendTable.RecId,CustVendTable.RecId);

           nCounter++;

   }

   ;

//    super(_exportPresetCode, dimensions, _condDel);

   this.mapFields();

   try

   {

       nCounter = 0;

       nCounterIns = 0;

       nCounterUpd = 0;

       this.initParams();

       this.SetDeletedFlag(S, this.AreaCond());

       while (queryRun.next())

       {

           custTable = queryRun.getNo(1);

           vendTable = queryRun.getNo(2);

Продолжение приложения Б

           // Клиенты

           if (custTable)

           {

               PartnerType = RContractPartnerType::Cust;

               CustVendTable = CustTable;

           }

           // Поставщики

           if (vendTable)

           {

               PartnerType = RContractPartnerType::Vend;

               CustVendTable = VendTable;

               if (VendTable.CustAccount) // Если есть связанный клиент

               {

                   if (cvMap.exists(CustTable::find(VendTable.CustAccount).RecId)) // Если мы клиента уже проэкспортировали

                   {

                       continue; // Пропускаем его (поставщика)

                   }

               }

           }

           InCycleFunc();

       }

       this.Log(_exportPresetCode, true, '',

                StrFmt('@GMC17344', nCounter, nCounterIns, nCounterUpd));

       return true;

   }

   catch (Exception::Error)

   {

       error("@GMC3072" + ": " + Table);

   }

   catch (Exception::Internal)

   {

       error("@GMC2005: " + ": " + Table);

   }

   catch (Exception::DDEerror)

   {

       error("@GMC2006: " + ": " + Table);

   }

   this.Log(_exportPresetCode, false, '');

   return false;

}

//метод инициализации динамических параметров запроса (Query)

void initParams()

{

   tableId tableId;

   super();

   tableId = queryRun.query().dataSourceNo(2).table();

   if (bNewRec)

   {

       queryRun.query().dataSourceNo(2).addRange(fieldName2Id(tableId,"ModifiedDate")).value(queryRange(lastImportDate,maxdate()));

       queryRun.query().dataSourceNo(2).addRange(fieldName2Id(tableId,"ModifiedTime")).value(queryRange(lastImportTime,60*60*24));

   }

   else

   {

Продолжение приложения Б

       queryRun.query().dataSourceNo(2).addRange(fieldName2Id(tableId,"ModifiedDate")).value(queryRange(dateFromRec,dateToRec));

   }

}

// метод, возвращающий название соотв. таблицы ПБД

public str tableName()

{

   ;

   return "EXP_CUSTVENDTABLE";

}
Продолжение приложения Б

// класс экспорта справочника валют

class GM_ExportDictTable_Currency extends GM_ExportDictTableClass

{

}

//выгружаемые поля

container axExportFields()

{

   container ret;

   ret += "CurrencyCode";

   ret += "Txt";

   ret += #FLD_DATAAREAID;

   ret += #FLD_RECID;

   return ret;

}

//перекрытый метод проверки структуры

boolean checkStructure()

{

   return true;

}

//метод экспорта

public boolean doExport(GM_ExportDictPresetCode _exportPresetCode,Common _common,str _condDel = "")

{

   boolean             ret;

   Currency            Currency;

   ret = super(_exportPresetCode, Currency, _condDel);

   return ret;

}

// метод, возвращающий название соотв. таблицы ПБД

public str tableName()

{

   ;

   return "EXP_CURRENCY";

}


ПРИЛОЖЕНИЕ В
МАКРОС ПО ЗАГРУЗКЕ СЧЕТОВ

ИЗ ДОКУМЕНТА Excel в ПБД

На рис. В.1 представлена главная формы макроса экспорта счетов из форм Excel в ПБД.

Рис. В.1 Форма MainForm

На рис. В.2 представлена форма настроек подключения к ПБД в макросе экспорта счетов из форм Excel в ПБД.

Рис. В.1 Форма UserForm

Далее приведен листинг макроса экспорта счетов из форм Excel в ПБД.

‘--> код формы MainForm

Private Sub ButtonSetting_Click()

   UserForm2.Show

End Sub

Private Sub ButtonExit_Click()

   Call procExit

End Sub

Private Sub ButtonOpenFile_Click()

   MainForm.Hide

   Call procSpravka

End Sub

Private Sub UserForm_Activate()

   Call name_ID

End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)

   MainForm.Hide

   Call procExit

End Sub

‘--> код формы UserForm

Public par As String

Private Sub CommandButton1_Click()

   If TextBox3.Text = "" Then

Продолжение приложения В

       MsgBox "Поле DSN должно быть обязательно заполнено"

       Exit Sub

   End If

   ThisWorkbook.Sheets("PBD").Cells(3, 2) = TextBox1.Text

   ThisWorkbook.Sheets("PBD").Cells(5, 2) = TextBox3.Text

   UserForm2.Hide

   MainForm.Hide

   MainForm.Show

End Sub

Private Sub TextBox2_Change()

   If TextBox2.Text <> String(Len(TextBox2.Text), 42) Then

       ThisWorkbook.Sheets("PBD").Cells(4, 2) = TextBox2.Text

       TextBox2.Text = String(Len(TextBox2.Text), 42)

   End If

End Sub

Private Sub UserForm_Activate()

   TextBox1.Text = ThisWorkbook.Sheets("PBD").Cells(3, 2)

   TextBox2.Text = ThisWorkbook.Sheets("PBD").Cells(4, 2)

   TextBox3.Text = ThisWorkbook.Sheets("PBD").Cells(5, 2)

End Sub

‘--> код класса mAUTO_OPEN – основной класс

Public F_work, F_forma

Public name_UID, name_PWD, name_WSID, name_DSN

Public identDoc, nameJournal, nameData, nameDoc, nameList

Public iRow

Public mMonth, mYear, erRow, erLst

Public errPr As Integer

Public chekOk As Boolean

Dim РабочаяОбласть As Workspace

Dim БазаДанных As Database

Dim Запись As Recordset

Dim ЗаписьДубль As Recordset

Dim Альбом As String

Dim Критерий As String

Dim Закладка As Variant

Dim i As Integer

Sub name_ID()

' Определение констант

name_WSID = Application.UserName

name_UID = ThisWorkbook.Sheets("PBD").Cells(3, 2).Text

name_PWD = ThisWorkbook.Sheets("PBD").Cells(4, 2).Text

name_DSN = ThisWorkbook.Sheets("PBD").Cells(5, 2).Text

ThisWorkbook.Sheets("Axapta").Select

End Sub

Sub AUTO_OPEN()

   F_work = ActiveWorkbook.Name

   MainForm.Show

End Sub

Sub procExit()

Application.DisplayAlerts = True

' Очистить рабочие листы

Windows(F_work).Activate

Sheets("pl_f").Select

Cells.Select

Selection.Clear

Range("A1").Select

Продолжение приложения В

Sheets("axapta").Select

Cells.Select

Selection.Clear

ThisWorkbook.Sheets("Dim").Select

Cells.Select

Selection.Clear

ThisWorkbook.Sheets("AXAPTA").Select

Range("A1").Select

' Закрыть файл формы

ThisWorkbook.Save

Excel.Application.Quit

End Sub

Sub procEnd()

' Очистить рабочие листы

Windows(F_work).Activate

Sheets("pl_f").Select

Cells.Select

Selection.Clear

Range("A1").Select

Sheets("axapta").Select

Cells.Select

Selection.Clear

ThisWorkbook.Sheets("Dim").Select

Cells.Select

Selection.Clear

ThisWorkbook.Sheets("AXAPTA").Select

Range("A1").Select

MainForm.Hide

MainForm.Show

End Sub

‘--> код класса mSendXls – класс добавления записей в ПБД

Sub SendXls()

   Dim wrkPbd As Workspace

   Dim dbsPbd As Database

   

   Dim InvoiceType As String

   Dim SystemID As String

   Dim Importbatch As String

   Dim ExternalId, PrevExternalId As String

   Dim AccountNum As String

   Dim ContractAccount As String

   Dim InvoiceDate As String

   Dim InvoiceNum As String

   Dim CurrencyCode As String

   Dim LineAmount, CheckAmount, TaxAmount As String

   Dim ExternalLineId As String

   Dim ItemId As String

   Dim Qty As String

       

       

   Dim strSQL As String

   Dim cnt As Integer

   Dim retCode As Integer

   mUID = name_UID

   mPWD = name_PWD

   mDSN = name_DSN

   

Продолжение приложения В

   SystemID = identDoc

   mcodAxapta = ""

   

   miRow = iRow

   

   If miRow < 1 Then

   Exit Sub

   End If

   

   

   Set wrkPbd = CreateWorkspace("", mUID, mPWD, dbUseODBC)

       

   Set dbsPbd = wrkPbd.OpenDatabase("", , False, "ODBC;DSN=" & mDSN)

       

   

   Windows(F_work).Activate

   Sheets("pl_f").Select

   cnt = 2

   tmpSumAmountLine = 0

   Do

       strSQL = "BEGIN TRANSACTION" & Chr$(10) & Chr$(12)

       strSQL = strSQL & "SET DATEFORMAT dmy" & Chr$(10) & Chr$(12)

    

       Importbatch = Cells(cnt, 20)

       

       InvoiceType = Cells(cnt, 1)

       ExternalId = Cells(cnt, 2)

       PrevExternalId = Cells(cnt - 1, 2)

       AccountNum = Cells(cnt, 3)

       ContractAccount = Cells(cnt, 4)

       InvoiceDate = Cells(cnt, 5)

       InvoiceNum = Cells(cnt, 6)

       CurrencyCode = Cells(cnt, 8)

       LineAmount = Cells(cnt, 18)

       CheckAmount = Cells(cnt, 9)

       TaxAmount = Cells(cnt, 10)

       ExternalLineId = Cells(cnt, 15)

       ItemId = Cells(cnt, 16)

   

       

       If (CurrencyCode = "1") Then

           CurrencyCode = "USD"

       End If

       If (CurrencyCode = "2") Then

           CurrencyCode = "RUR"

       End If

           

       If CCur(Replace(LineAmount, ".", ",")) < 0 Then

           Qty = "-1"

       Else

           Qty = "1"

       End If

       LineAmount = Replace(LineAmount, ",", ".")

       TaxAmount = Replace(TaxAmount, ",", ".")

       If ExternalId <> PrevExternalId Then

                      

           strSQL = strSQL & "DELETE FROM EXP_DOCTABLE" & Chr$(10) & Chr$(12)

           strSQL = strSQL & "WHERE" & Chr$(10) & Chr$(12)

           strSQL = strSQL & "IMPORTBATCH='" & Importbatch & "' AND" & Chr$(10) & Chr$(12)

Продолжение приложения В

           strSQL = strSQL & "ExternalId='" & ExternalId & "'" & Chr$(10) & Chr$(12)

            

           strSQL = strSQL & "DELETE FROM EXP_DOCLINE" & Chr$(10) & Chr$(12)

           strSQL = strSQL & "WHERE" & Chr$(10) & Chr$(12)

           strSQL = strSQL & "IMPORTBATCH='" & Importbatch & "' AND" & Chr$(10) & Chr$(12)

           strSQL = strSQL & "ExternalId='" & ExternalId & "'" & Chr$(10) & Chr$(12)

           

           strSQL = strSQL & "INSERT EXP_DOCTABLE" & _

           "(State,IMPORTBATCH,SystemID,ExternalId,InvoiceType,AccountNum,RContractAccount,PoolId,TransTxt," & _

           "InvoiceDate,FactureDate,InvoiceNum,ExternalInvoiceId,InclTax,CurrencyCode,TaxAmount,Amount) VALUES " & _

           "(0,'" & Importbatch & "','" & SystemID & "','" & ExternalId & "'," & InvoiceType & ",'" & AccountNum & "','" & ContractAccount & "','CBOSS','Счет из CBOSS'," & _

           "'" & InvoiceDate & "','" & InvoiceDate & "','" & InvoiceNum & "','" & InvoiceNum & "',0,'" & CurrencyCode & "'," & TaxAmount & "," & CheckAmount & ")" & Chr$(10) & Chr$(12)

           

       End If

       

       tmpAmount = CCur(Replace(Cells(cnt, 9), ".", ","))

       tmpSumAmountLine = tmpSumAmountLine + CCur(Replace(Cells(cnt, 18), ".", ","))

           

       strSQL = strSQL & "INSERT EXP_DOCLINE" & _

       "(State,IMPORTBATCH,ExternalId,ItemId,LineAmount,Qty) VALUES " & _

       "(0,'" & Importbatch & "','" & ExternalId & "','" & ItemId & "'," & LineAmount & "," & Qty & ")" & Chr$(10) & Chr$(12)

       cnt = cnt + 1

     

       strSQL = strSQL & "COMMIT TRANSACTION" & Chr$(10) & Chr$(12)

              

       'Open "C:\uuu.txt" For Output As #1

       'Print #1, strSQL

       'Close #1

       

       dbsPbd.Execute strSQL

       

   Loop While cnt <= miRow

       

   dbsPbd.Close

End Sub

Sub checkSum()

   

   miRow = iRow

   If miRow < 1 Then

   Exit Sub

   End If

   

   Windows(F_work).Activate

   Sheets("pl_f").Select

   cnt = 2

   

Продолжение приложения В

   chekOk = True

   PrevExternalId = ""

   Do

       

       ExternalId = Cells(cnt, 2)

       PrevExternalId = Cells(cnt - 1, 2)