Главная О компании Новости Обучение Обратная связь Форум
сервер контра

ABACUS Financial ABACUS Builder ABACUS Professional PROPHIX
ABACUS WEB

Клиенты и представители Компания Омега. Советы разработчикам на ABACUS Builder. Репликация

Вашему вниманию предлагается еще одна зарисовка по опыту разработки и внедрения корпоративной системы автоматизации учета и управления экономической деятельностью предприятий и холдингов оптовой торговли ПроектИнформ Негоциант™ (ПИН™), созданной с помощью системы разработки корпоративных информационных систем ABACUS Builder™ (AB7™) производства московской фирмы "ОМЕГА". О том, что собой представляет система ПИН™, ее основных возможностях, истории ее создания, а так же об инструменте разработки AB7™ я рассказывал в своей предыдущей статье "ПроектИнформ Негоциант™ или что могут сделать с ABACUS Builder полтора человека за полтора года.

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

Все эти аспекты взаимоотношений с контрагентами предприятий холдинга и необходимо было отразить в корпоративной системе ПИН™.

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

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

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

В модели данных AB7™ понятия предметной области принято называть "категориями", свойства понятий "атрибутами категорий" а иерархические связи понятий "связями категорий". Атрибуты могут быть строковыми, числовыми или иметь тип "дата". Связи категорий направлены и имеют "силу" сверху вниз "один ко многим" (предприятие может включать в свой состав несколько подразделений), снизу вверх "один к одному" (подразделение может входить в состав только одного предприятия).

Экземпляр категории называется "объектом". Объект "наследует все свойства категории. То есть, объект может иметь атрибуты, определенные для категории, и направленные связи с объектами других категорий, если между этими категориями определены такие связи. Причем "снизу" может быть привязано любое количество объектов "нижней категории" (связь "один ко многим"), а "сверху" к объекту может быть привязан только один объект другой категории (связь "один к одному"). Обратите внимание, что "сверху" к объекту могут быть привязаны несколько объектов, но только по одному из каждой категории. Для простоты восприятия можно провести аналогию с обычными таблицами базы данных (сразу скажу, что в AB7™ это сделано совсем иначе). "Категория" - это таблица с полями ("атрибуты категории"). "Объект" это запись в таблице с реальными значениями полей ("атрибуты объекта"). "Связи" - это скрытые ключевые поля таблиц.

Встроенный в AB7™ язык запросов к базе данных AQL (ABACUS Query Language) имеет директивы перемещения по "дереву объектов":

  • ":КАТЕГОРИЯ" - перемещение вниз на категорию. Возвращает объекты указанной категории, привязанные снизу к объектам, переданным на вход директивы.
  • "^КАТЕГОРИЯ" - перемещение вверх на категорию. Возвращает объекты указанной категории, привязанные сверху к объектам, переданным на вход директивы.

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

Как же была решена проблема клиентов и представителей?

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

Рассмотрим преимущества и недостатки первого способа.

Преимущества:

  • Логичная иерархическая схема данных.
  • Простота определения представителей для клиентов и клиента для представителей. В AQL-запросе достаточно, соответственно "спуститься вниз" от объекта клиента на категорию "Представители" или "подняться вверх" от объекта представителя на категорию "Клиенты".
  • Поскольку и представители, и клиенты являются контрагентами предприятий холдинга, то есть имеют одну и ту же структуру данных, то необходимо создать две различные категории с абсолютно одинаковым набором категорий и связей. Кстати говоря, категория "Контрагент" имеет 62 атрибута, 11 верхних и 72 нижних связи.
  • Поскольку функциональность у этих категорий также одинаковая необходимо для каждой категории создать практически повторяющийся набор экранных форм. В Системе ПИН™ в настоящее время существует 32 экранных формы для категории "Контрагент". На самом деле с точки зрения хранимых данных это не большой объем информации. Главная проблема в другом. В процессе разработки и эксплуатации системы постоянно приходится дорабатывать экранные формы, добавляя в них новый функционал, улучшая представление и т.д. В таком подходе все изменения придется проводить дважды, что в два раза повышает вероятность возникновения ошибки и увеличивает время разработки.
  • Во всех документах (накладных, счетах-фактурах, платежных поручениях и т.д. и т.п.) необходимо обеспечить возможность их привязки как к клиентам, так и к представителям.
  • Во всех единых справочниках контрагентов необходимо собирать объекты из двух категорий: "Клиенты" и "Представители".

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

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

Не буду утомлять вас подробными рассуждениями и приведу сразу окончательную схему данных (Рис. 1)

Направление связи между категориями "Клиенты" и "Контрагенты" здесь совершенно не принципиально. Категории "Клиенты" и "Представители" являются промежуточными и пустыми (содержат никаких атрибутов). Они служат только для определения направления связи. Объект категории "Контрагент", привязанный к объекту категории "Клиент", является клиентом, а объект категории "Контрагент", привязанный к объекту категории "Представитель", является догадайтесь сами кем. "Дерево объектов" для клиента "Контрагент 1" и трех его представителей: "Контрагент 2", "Контрагент 3" и "Контрагент 4" (Рис. 2). Обращаю ваше внимание, что для каждого клиента, имеющего представителей должна существовать пара объектов "Клиенты" и "Представители".

Соответственно, в AQL-запросах для получения списка представителей клиента теперь необходимо написать:

НЕКОТОРЫЙ_КОНТРАГЕНТ ^ КАТЕГОРИЯ_КЛИЕНТЫ : 
КАТЕГОРИЯ_ПРЕДСТАВИТЕЛИ : КАТЕГОРИЯ_КОНТАГЕНТЫ

Для определения клиента представителя необходимо написать:

НЕКОТОРЫЙ_КОНТРАГЕНТ ^ КАТЕГОРИЯ_ПРЕДСТАВИТЕЛИ ^ 
КАТЕГОРИЯ_КЛИЕНТЫ : КАТЕГОРИЯ_КОНТАГЕНТЫ

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

Пишем запрос:

select 
# ^ КАТЕГОРИЯ_КОНТРАГЕНТЫ.НАЗВАНИЕ,
#.СУММА_НАКЛАДНОЙ
from ПРЕДПРИЯТИЕ_ХОЛДИНГА : КАТЕГОРИЯ_НАКЛАДНЫЕ
range_val(ДАТА_НАКЛАДНОЙ,
ДАТА_НАЧАЛА ПЕРИОДА,ДАТА_ОКОНЧАНИЯ_ПЕРИОДА)

То есть, (идем от конструкции "from") для некоторого предприятия холдинга получаем все накладные (: КАТЕГОРИЯ_НАКЛАДНЫЕ) за период (range_val(…)), и для каждой накладной получаем контрагента (конструкция ".НАЗВАНИЕ" означает, что необходимо получит название контрагента) и сумму накладной. В результате выполнения запроса получаем таблицу, в первой колонке которой представлены контрагенты по накладным, а во второй суммы накладных. Далее, проходя по каждой строке таблицы, получаем уже известным нам запросом клиента для контрагента и заменяем в первой колонке представителя (если контрагент является представителем) клиентом. Теперь достаточно сгруппировать таблицу по первому столбцу с суммированием второго столбца, и мы получает таблицу, в первой колонке которой представлены клиенты, а во второй для каждого клиента представлены суммы всех накладных по клиентам вместе с их представителями.

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

Пишем запрос:

select 
# ^ КАТЕГОРИЯ_КОНТРАГЕНТЫ.НАЗВАНИЕ,
# ^ КАТЕГОРИЯ_КОНТРАГЕНТЫ ^ КАТЕГОРИЯ_
ПРЕДСТАВИТЕЛИ ^ КАТЕГОРИЯ_КЛИЕНТЫ : КАТЕГОРИЯ_КОНТРАГЕНТЫ.НАЗВАНИЕ,
#.СУММА_НАКЛАДНОЙ
from ПРЕДПРИЯТИЕ_ХОЛДИНГА : КАТЕГОРИЯ_НАКЛАДНЫЕ
range_val(ДАТА_НАКЛАДНОЙ,ДАТА
_НАЧАЛА ПЕРИОДА,ДАТА_ОКОНЧАНИЯ_ПЕРИОДА)

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

Таким образом решена проблема клиентов и представителей в корпоративной системе автоматизации учета и управления экономической деятельностью предприятий и холдингов оптовой торговли ПроектИнформ Негоциант™.

Владимир Русецкий
ПроектИнформ
(495)775-61-41
torus@p-inform.ru


Первый кассовый аппарат был сконструирован американцем Джеймсом Ритти в 1876 г. Прибор показывал сумму денег, полученную за каждую покупку, и хранил информацию обо всех, сделанных за день, операциях. Возможность ошибок и мелкого жульничества практически перестала существовать, и была достигнута исключительная точность записи всех торговых операций". Из истории создания ККМ, арифмометров и счетных машин
Компания Омега. Советы разработчикам на ABACUS Builder. Репликация

  © Компания "ОМЕГА"   www.omega.ru   (495) 234-42-32,  (495) 727-43-50