Управление материальными потоками
Описание задачи "Управление материальными потоками"
Создаваемая с помощью платформы lsFusion информационная система должна содержать функциональность для учета движения товаров на складе.
Для упрощения зададим в системе один вид документа, увеличивающего остаток на складе - приходная накладная от поставщика и один вид документа, уменьшающего остаток на складе - расходная накладная оптовой продажи товара покупателю.
Задание предметной логики
Информационная система будет состоять из подмножества модулей, в каждом из которых реализуется некоторая логически обособленная функциональность. В каждом из модулей может использоваться функциональность других модулей, для чего используются специальные конструкции задания зависимости модулей.
Исходя из постановки задачи выделим перечень модулей, подлежащих реализации: модуль склада, модуль товара, модуль организации, модуль приходной накладной, модуль расходной накладной, модуль текущих остатков. Отдельно также выделяется головной модуль, который будет запускаться на выполнение и фактически будет представлять собой скомпонованное прикладное решение. Состав модулей может быть и иным, и определяется разработчиком самостоятельно исходя из потребности повторного использования функциональности.
Определение склада
Создаем модуль, в котором далее определим сущность склада и его атрибуты.
MODULE Stock;
Задаем понятие склада и его атрибуты: наименование, адрес.
CLASS Stock 'Склад';
name 'Наименование' = DATA STRING[100] (Stock) IN base;
address 'Адрес' = DATA STRING[150] (Stock) IN base;
Определение товара
Создаем модуль, в котором определяем сущность товара и его атрибуты.
MODULE Item;
Задаем понятие товара и его атрибуты: наименование, штрих-код.
CLASS Item 'Товар';
name 'Наименование' = DATA STRING[100](Item) IN base;
barcode 'Штрихкод' = DATA BPSTRING[13](Item) IN base;
Для товара зададим цену продажи, по которой он будет реализовываться оптовым покупателям.
salePrice 'Оптовая цена' = DATA NUMERIC[17,2](Item) IN base;
Определение организации
Создаем модуль, в котором определяем сущность организации и ее атрибуты. Организации в системе будут выступать в роли поставщиков и покупателей.
MODULE LegalEntity;
Задаем понятие организации и ее атрибуты: наименование, юридический адрес, ИНН.
CLASS LegalEntity 'Организация';
name 'Наименование' = DATA STRING[100](LegalEntity) IN base;
address 'Адрес' = DATA STRING[150](LegalEntity) IN base;
inn 'ИНН' = DATA BPSTRING[9](LegalEntity) IN base;
Задаем уникальность ИНН для организации.
legalEntityINN = GROUP AGGR LegalEntity legalEntity BY inn(legalEntity);
Свойство legalEntityINN
связывает организацию и ИНН один к одному, и позволяет по заданному ИНН определить организацию. Выражение свойства можно трактовать следующим образом: при группировке организаций по ИНН (свойству inn
) в каждой из групп должна быть не повторяющаяся организация.
Определение приходной накладной
Создаем модуль, в котором определяем все сущности и атрибуты, необходимые для задания логики приходной накладной от поставщика.
MODULE Receipt;
Зададим использование в модуле Receipt
функциональности из других модулей.
REQUIRE Stock, Item, LegalEntity;
Задаем понятия, определяющие логику приходной накладной. Будем исходить из принципа, что все документы (как приходные, так и расходные) в системе состоят из шапки и товарной спецификации. Соответственно определим понятия шапка приходной накладной и строка приходной накладной.
CLASS Receipt 'Приходная накладная';
CLASS ReceiptDetail 'Строка приходной накладной';
У каждой строки приходной накладной есть ссылка на шапку документа, и в итоге шапка документа и подмножество строк со ссылкой на этот документ в совокупности определяют приходную накладную с точке зрения пользователя. Параметр NONULL
обозначает, что ссылка должна быть задана. Параметр DELETE
определяет, что при удалении основного объекта Receipt
, все строки ReceiptDetail
, ссылающиеся на него, будут также удалены. По умолчанию, при удалении объекта все ссылки на него обнуляются. Таким образом, без параметра DELETE
будет выдана ошибка, что ссылка не задана.
receipt 'Документ строки' = DATA Receipt (ReceiptDetail) NONULL DELETE;
Определяем номер строки в приходной накладной.
index 'Номер строки' (ReceiptDetail d) =
PARTITION SUM 1 IF d IS ReceiptDetail
ORDER d BY receipt(d);
Использование в выражениях имени класса объекта равнозначно использованию его идентификационного номера (id), создаваемого системой для всех объектов автома тическим счетчиком. В данном случае использование для сортировки конструкции ORDER d
позволяет отсортировать строки накладной в порядке возрастания их id, т.е. фактически в порядке последовательности их создания.
Здесь в операторе PARTITION
используется блок BY
, группирующий объекты по некоторому атрибуту и расчет нарастающим итогом суммы выражения выполняется в рамках каждой из групп. В данном случае определение номера строки идет только в рамках документа этой строки (свойство receipt
).
Задаем набор основных атрибутов шапки накладной: номер, дата, поставщик и его наименование, склад, на которой осуществляется оприходование товара, и его наименование. Наименование поставщика и склада в последующем понадобятся для удобного отображения на форме.
number 'Номер накладной' = DATA BPSTRING[10] (Receipt);
date 'Дата накл адной' = DATA DATE (Receipt);
supplier 'Поставщик' = DATA LegalEntity (Receipt);
nameSupplier 'Наименование поставщика' (Receipt r) = name(supplier(r));
stock 'Склад' = DATA Stock (Receipt);
nameStock 'Наименование склада' (Receipt r) = name(stock(r));
Задаем набор основных атрибутов строки накладной: товар и его наименование, количество, цена поставщика, сумма поставщика (рассчитывается умножением цены на количество).
item 'Товар' = DATA Item (ReceiptDetail);
nameItem 'Наименование товара' (ReceiptDetail d) = name(item(d));
quantity 'Количество' = DATA NUMERIC[16,4] (ReceiptDetail);
price 'Цена поставщика' = DATA NUMERIC[17,2] (ReceiptDetail);
sum 'Сумма поставщика' (ReceiptDetail d) = quantity(d) * price(d);