إدارة المعلمات في تطبيقات الأعمال المشابهة لنظام التحكم في الإصدار

صورة

في التطبيقات المختلفة ، تنشأ المهمة بانتظام لدعم منطق التغيير في وقت سمة معينة لكائن ما بالنسبة لموضوع معين (أو مواضيع). على سبيل المثال ، قد يكون هذا تغييرًا في سعر التجزئة للسلع في المتاجر أو مؤشرات KPI للموظفين.

في هذه المقالة ، سأبين منطق المجال والواجهات التي يمكن بناؤها لحل هذه المشكلة. سأبدي تحفظًا فورًا على أنها ستهتم بالتأثير الإداري للمستخدم على السمة وليس انعكاس التغيير التاريخي.

سيتم تقديم التطبيق على أساس منصة lsFusion المفتوحة والحرة ، ولكن يمكن تطبيق مخطط مماثل عند استخدام أي تقنية أخرى.

مقدمة


للحصول على عرض وفهم أبسط للمقالة ، نأخذ السعر كسمة ، وسيكون المنتج ككائن ، والمستودع هو الموضوع. في هذه الحالة ، سيكون الحد الأدنى للفاصل الزمني الممكن لإعداد السمة هو التاريخ. وبالتالي ، سيكون المستخدم قادرًا على تحديد سعر تاريخ معين لأي منتج ومستودع.

سيكون مخطط إدخال المستخدم لتغيرات الأسعار مشابهًا لنظام المستخدم في أنظمة التحكم في الإصدار الكلاسيكي. أي تغيير ، من وجهة نظر منطق المجال ، سيكون التزامًا واحدًا ، على أساس أنه سيتم حساب حالة تاريخ معين. في العديد من المجالات ، تسمى هذه الالتزامات المستندات أو المعاملات. في هذه الحالة ، بهذا الالتزام ، فإننا نعني قائمة الأسعار المزعومة. ستحدد كل قائمة أسعار البضائع والمستودعات التي تم تضمينها فيها ، بالإضافة إلى فترة الصلاحية.

للمخطط الموصوف المزايا التالية:

  • الذرية . يتم إصدار كل تغيير كوثيقة منفصلة. لذلك ، يمكن حفظ هذه المستندات مؤقتًا ، ولكن لن يتم نشرها. مع إدخال خاطئ ، من السهل التراجع عن التغيير بأكمله.
  • الشفافية. من السهل تحديد من قام بالتغيير ومتى ، وكذلك الإشارة إلى السبب من خلال التعليق على المستند.

الفرق الرئيسي من نظام التحكم في الإصدار هو أن التعهدات الصريحة مستقلة عن بعضها البعض. وبالتالي ، من الممكن حذف جميع الالتزامات غير المؤلمة نسبيًا في أي وقت. بالإضافة إلى ذلك ، يمكن تعيين كل التزام من هذا القبيل عند توقفه عن العمل ، وهذا بالطبع ليس في نظام التحكم في الإصدار.

تطبيق


نبدأ في تعريف منطق المجال مع المستودعات. دعنا نعقد الحل قليلاً عن طريق دمج المستودعات في تسلسل هرمي لمجموعة من العمق الديناميكي. وفقًا للمبدأ الموضح في المقالة المقابلة ، لذلك سأقدم فقط رمزًا يعلن المجموعات ويقوم بإنشاء نماذج لتحريرها:

إعلان مجموعة المستودعات
CLASS Group ' ' ;
name '' = DATA ISTRING [ 50 ] (Group);

parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

FORM group ' '
OBJECTS g = Group PANEL
PROPERTIES (g) name, nameParent

EDIT Group OBJECT g
;

FORM groups ' '
OBJECTS g = Group
PROPERTIES (g) READONLY name, nameParent
PROPERTIES (g) NEWSESSION NEW , EDIT , DELETE

LIST Group OBJECT g
;

NAVIGATOR {
NEW groups;
}

مثال على التسلسل الهرمي للمجموعة
صورة


بعد ذلك ، قم بالإعلان عن المستودعات التي يمكن ربطها بأي من المجموعات:

إعلان مستودع
CLASS Stock '' ;
name '' = DATA ISTRING [ 50 ] (Stock);

group '' = DATA Group (Stock);
nameGroup '' (Stock st) = name(group(st));

FORM stock ''
OBJECTS s = Stock PANEL
PROPERTIES (s) name, nameGroup

EDIT Stock OBJECT s
;

FORM stocks ''
OBJECTS s = Stock
PROPERTIES (s) READONLY name, nameGroup
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Stock OBJECT s
;

NAVIGATOR {
NEW stocks;
}


مثال مستودع
صورة


وأخيرا ، أعلن منطق البضاعة:

إعلان المنتج
CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p
;

NAVIGATOR {
NEW products;
}

مثال المنتج
صورة


ننتقل مباشرة إلى إنشاء منطق قوائم الأسعار. أولاً ، قمنا بتعيين فئة قائمة الأسعار نفسها ، بالإضافة إلى فترة صلاحيتها:
CLASS PriceList '-' ;
fromDate ' ' = DATA DATE (PriceList);
toDate ' ' = DATA DATE (PriceList);
نعتقد أنه إذا لم يتم تعيين تاريخ ، فإن قائمة الأسعار لا حصر لها.
نضيف حدثًا ، عند إنشاء قائمة الأسعار ، سيضع تلقائيًا التاريخ الحالي الذي سيبدأ من خلاله العمل.
WHEN LOCAL SET (PriceList p IS PriceList) DO
fromDate(p) <- currentDate();
تعني الكلمة الأساسية المحلية أن الحدث لن يتم إطلاقه عند تطبيق الحفظ على قاعدة البيانات ، ولكن فور إجراء التغيير.

ثم أضف المستخدم الذي قام بإنشائه ووقت الإنشاء:
createdTime ' ' = DATA DATETIME (PriceList);
createdUser = DATA User (PriceList);
nameCreatedUser '' (PriceList p) = name(createdUser(p));
أنشئ الآن حدثًا سيملأها تلقائيًا:
WHEN SET (PriceList p IS PriceList) DO {
createdTime(p) <- currentDateTime();
createdUser(p) <- currentUser();
}
هذا الحدث ، بخلاف الحدث السابق ، لن يتم تشغيله إلا عند النقر فوق الزر "حفظ". وهذا هو ، خلال عملية حفظ إلى قاعدة البيانات.

بعد ذلك ، قم بإنشاء سطور قائمة الأسعار التي سيتم فيها تعيين البضائع والأسعار:
CLASS PriceListDetail ' -' ;
priceList = DATA PriceList (PriceListDetail) NONULL DELETE ;

product = DATA Product (PriceListDetail);
nameProduct '' (PriceListDetail d) = name(product(d));

price '' = DATA NUMERIC [ 10 , 2 ] (PriceListDetail);
تشير السمة NONULL إلى أنه يجب دائمًا تعيين خاصية قائمة الأسعار ، ويشير DELETE إلى أنه عندما تكون قيمة الخاصية صفراً (على سبيل المثال ، عند حذف قائمة الأسعار) ، يجب حذف السطر المقابل تلقائيًا.

للاستخدام المستقبلي ، سنقوم بإنشاء خصائص تحدد فترة صلاحية خطوط قائمة الأسعار:
fromDate ' ' (PriceListDetail d) = fromDate(priceList(d));
toDate ' ' (PriceListDetail d) = toDate(priceList(d));
سنقوم الآن بربط قائمة الأسعار بالمستودعات التي ستعمل من أجلها. أولاً ، أضف العقار الأساسي ، والذي سيكون صحيحًا إذا تم تضمين المجموعة الكاملة للمستودعات في قائمة الأسعار:
dataIn '' = DATA BOOLEAN (PriceList, Group);
نحسب "تضمين" المجموعة مع مراعاة الآباء المختارين (كما هو موضح في مقالة حول التسلسلات الهرمية):
in ' ()' (PriceList p, Group child) =
GROUP LAST dataIn(p, Group parent) ORDER DESC level(child, parent) WHERE dataIn(p, parent);
أضف الخاصية الأساسية ، والتي يمكنك من خلالها تحديد أن قائمة الأسعار تعمل على مستودع معين:
dataIn '' = DATA BOOLEAN (PriceList, Stock);
نحسب الخاصية النهائية ، والتي ستحدد أن قائمة الأسعار تغير الأسعار في المستودع المقابل ، مع مراعاة المجموعات:
in '' (PriceList p, Stock s) = dataIn(p, s) OR in(p, group(s));
قم بإنشاء خاصية تعرض أسماء جميع المجموعات والمستودعات المحددة في قائمة الأسعار ، للمستخدم الأكثر ملاءمة لعرض قائمة قوائم الأسعار:
stocks '' (PriceList p) = CONCAT ' / ' ,
GROUP CONCAT name(Group g) IF dataIn(p, g), ',' ORDER g,
GROUP CONCAT name(Stock s) IF dataIn(p, s), ',' ORDER s
CHARWIDTH 30 ;
الخطوة الأخيرة في وصف منطق المجال ستحسب مباشرة السعر الحالي للبضائع في المستودع. للقيام بذلك ، قم بإنشاء خاصية تعثر على آخر سطر حسب تاريخ قائمة الأسعار مع البضائع والمستودعات وفترة الصلاحية المطلوبة:
priceListDetail (Product p, Stock s, DATE dt) =
GROUP LAST PriceListDetail d
ORDER fromDate(d), d
WHERE product(d) = p AND in(priceList(d), s) AND
fromDate(d) <= dt AND NOT toDate(d) < dt;
في منطق حساب هذه الخاصية ، هناك أشكال مختلفة ممكنة. يمكنك تغيير كلاً من مرشح ضرب الصفوف (على سبيل المثال ، إضافة شرط في مكان نشر قائمة الأسعار) والترتيب. تجدر الإشارة إلى أن الكائن نفسه ، أو بالأحرى معرفه الداخلي ، قد تمت إضافته إلى ترتيب التحديد بواسطة المعلمة الثانية. هذا ضروري حتى يتم تحديد قيمة السعر دائمًا بطريقة فريدة.

بناءً على سطر قائمة الأسعار المستلمة ، نحدد قيمة السعر وفترة صلاحيتها:
price '' (Product p, Stock s, DATE dt) = price(priceListDetail(p, s, dt));
fromDate ' ' (Product p, Stock s, DATE dt) = fromDate(priceListDetail(p, s, dt));
toDate ' ' (Product p, Stock s, DATE dt) = toDate(priceListDetail(p, s, dt));
سيتم استخدامها مرة أخرى في جداول واجهة المستخدم.

بعد ذلك ، ننتقل إلى بناء واجهة المستخدم. أولاً ، نرسم نموذجًا لتحرير قائمة الأسعار. قم بإنشاء نموذج وإضافة "رأس" المستند هناك:
FORM priceList '-'
OBJECTS p = PriceList PANEL
PROPERTIES (p) fromDate, toDate

EDIT PriceList OBJECT p
;
أضف سطر قائمة الأسعار إلى النموذج:
EXTEND FORM priceList
OBJECTS d = PriceListDetail
PROPERTIES (d) nameProduct, price
PROPERTIES (d) NEW , DELETE
FILTERS priceList(d) = p
;
بعد ذلك ، أضف شجرة تحتوي على مجموعات ومستودعات:
EXTEND FORM priceList
TREE stocks g = Group PARENT parent, s = Stock
PROPERTIES READONLY name(g), name(s)
PROPERTIES dataIn(p, g), in(p, g)
PROPERTIES dataIn(p, s), in(p, s)
FILTERS group(s) = g
;
تتم إضافة خصائص المجموعات والمستودعات إلى الشجرة في نفس الوقت. سيقوم النظام الأساسي ، بناءً على الكائن ، بإظهار هذه الخاصية أو تلك بالترتيب الذي تتم إضافته إلى النموذج.

نقوم بتخصيص تصميم النموذج بحيث يتم رسم البضائع والمستودعات في علامات تبويب منفصلة:
DESIGN priceList {
OBJECTS {
NEW pane {
fill = 1 ;
type = TABBED ;
MOVE BOX (d) { caption = '' ; }
MOVE BOX ( TREE stocks) { caption = '' ; }
}
}
}
سيبدو نموذج التعديل كما يلي:

صورة

صورة

يبقى لبناء الشكل الأساسي لإدارة الأسعار. وسوف تتكون من علامتي تبويب. ستعرض القائمة الأولى قائمة بجميع قوائم الأسعار (على غرار قائمة التعهدات). تعرض علامة التبويب الثانية الأسعار الحالية لمستودع معين في التاريخ المحدد.

لتنفيذ علامة التبويب الأولى ، أضف إلى القائمة قائمة قوائم الأسعار مع خطوط لمعاينة سريعة:
FORM managePrices ' '
OBJECTS p = PriceList
PROPERTIES (p) READONLY fromDate, toDate, stocks, createdTime, nameCreatedUser
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

OBJECTS d = PriceListDetail
PROPERTIES (d) READONLY nameProduct, price
FILTERS priceList(d) = p
;
بالنسبة إلى علامة التبويب الثانية ، نضيف أولاً تاريخ عرض الأسعار وشجرة مجموعات المستودعات والمستودعات نفسها:
EXTEND FORM managePrices
OBJECTS dt = DATE PANEL
PROPERTIES VALUE (dt)

TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)

OBJECTS s = Stock
PROPERTIES (s) READONLY name, nameGroup
FILTERS level(group(s), g)
;
ستعرض قائمة المستودعات جميع المستودعات التي هي أحفاد المجموعة المحددة في الأعلى.

بعد ذلك ، أضف إلى النموذج قائمة بالسلع التي توجد بها أسعار صالحة للمستودع في التاريخ المحدد:
EXTEND FORM managePrices
OBJECTS pr = Product
PROPERTIES READONLY name(pr), price(pr, s, dt), fromDate(pr, s, dt), toDate(pr, s, dt)
FILTERS price(pr, s, dt)
;
تتم إضافة كل من السعر نفسه وفترة الصلاحية إلى الأعمدة. يمكنك أيضًا إضافة رقم قائمة الأسعار - ثم يشبه هذا الجدول منطق التعليقات التوضيحية في أنظمة التحكم في الإصدار.

لكي يفهم المستخدم من أين جاء هذا السعر ، نضيف إلى قائمة خطوط قائمة الأسعار مع البضائع والمستودعات المناسبة:
EXTEND FORM managePrices
OBJECTS prd = PriceListDetail
PROPERTIES READONLY BACKGROUND (priceListDetail(pr, s, dt) = prd)
fromDate(prd), toDate(prd), '' = stocks(priceList(prd)), price(prd)
FILTERS product(prd) = pr AND in(priceList(prd), s)
;
باستخدام السمة BACKGROUND ، قم بتمييز الصف الذي يحدد السعر الموضح في الجدول.

أيضًا ، من أجل راحة المستخدم ، سنضيف القدرة على فتح نموذج تحرير قائمة الأسعار المقابلة في جلسة جديدة فورًا من هذه القصة:
edit (PriceListDetail d) + { edit(priceList(d)); }
EXTEND FORM managePrices
PROPERTIES (prd) NEWSESSION EDIT
;
لتحقيق ذلك ، تحتاج إلى تحديد الإجراء الذي سيتم تنفيذه عند محاولة تحرير خط من خلال تنفيذ إجراء التحرير المضمن. ثم ، يتم إضافة زر قياسي لتحرير كائن من خلال مكالمة حوار إلى النموذج بالطريقة القياسية.

وأخيراً ، نقوم بتشكيل التصميم النهائي للنموذج:
DESIGN managePrices {
OBJECTS {
NEW pane {
fill = 1 ;
type = TABBED ;
NEW priceLists {
caption = '-' ;
MOVE BOX (p);
MOVE BOX (d);
}
NEW prices {
caption = '' ;
fill = 1 ;
type = SPLITH ;
NEW leftPane {
MOVE BOX (dt);
MOVE BOX ( TREE groups);
MOVE BOX (s);
}
NEW rightPane {
fill = 3 ;
type = SPLITV ;
MOVE BOX (pr) { fill = 3 ; }
MOVE BOX (prd);
}
}
}
}
}
هنا ، تتم إضافة حاوية الجزء أولاً ، والتي تتكون من علامتي تبويب: قوائم الأسعار والأسعار . أولهم فقط يضيف قائمة قوائم الأسعار وخطوط. في الثانية ، يتم إنشاء لوحتين : leftPane و rightPane . تحتوي اللوحة اليمنى على التاريخ والمستودعات ، بينما تحتوي اللوحة اليمنى على تاريخ البضائع والسعر.

يؤدي


النظر في الخيارات الرئيسية لاستخدام المنطق الناتج.

افترض أن لدينا قائمتين منفصلتين للأسعار لمجموعات مختلفة من السلع. بعد ذلك ، وفقًا للمستودع المحدد ، في علامة التبويب بالأسعار ، سيتم عرض المنتجات من قوائم الأسعار المطابقة فقط:

صورة

الآن قم بإنشاء قائمة أسعار جديدة مع فترة صلاحية محدودة ، وقائمة مستودعات منخفضة السعر وسعر جديد. في علامة التبويب الثانية ، إذا حددنا تاريخًا في نطاق قائمة الأسعار الجديدة ، فسنحصل على سعر جديد منه. بمجرد انتهاء فترة الصلاحية ، سيعود السعر القديم مرة أخرى من السعر الأصلي:

صورة

باستخدام نفس الآلية ، يمكنك "إلغاء" إجراء أسعار محددة من تاريخ معين. على سبيل المثال ، إذا أدخلت سعرًا جديدًا دون تحديد سعر ، فقد تبين أنه سيتم إعادة تعيين السعر وستختفي البضائع من الفلتر. في هذه الحالة ، عند حذف المستند الذي تم إدخاله ، يعود كل شيء إلى الحالة القديمة:

صورة

يمكن استخدام الخاصية الناتجة مع سعر البضاعة بواسطة المستودع في التاريخ في أحداث مختلفة أو أشكال أخرى. على سبيل المثال ، يمكنك إجراء التسعير التلقائي في ترتيب بناءً على منطق التسعير هذا:
WHEN LOCAL CHANGED (sku(UserOrderDetail d)) OR CHANGED (stock(d)) OR CHANGED (dateTime(d)) DO
price(d) <- price(sku(d), stock(d), dateTime(d));
ومن المزايا الجيدة في هذا المنطق أنه عندما تضيف مستودعًا جديدًا إلى المجموعة ، فإن الأسعار من قوائم الأسعار التي تم إنشاؤها بالفعل ستنطبق عليها تلقائيًا. سيحدث نفس الشيء عند تغيير المجموعة للمستودع.

إذا كنت ترغب في ذلك ، يمكنك جعل العمود بالسعر في علامة التبويب مع الأسعار الحالية قابلة للتعديل وإضافة زر من شأنه إنشاء التزام جديد للأسعار المتغيرة.

استنتاج


في الحل على مستوى النظام الأساسي ، لا يتم استخدام الكتب المرجعية ، أو الوثائق ذات السلاسل ، ولا السجلات ، ولا التقارير وغيرها من التجريدات غير الضرورية. كل شيء يتم حصرا على مفاهيم الطبقات والخصائص. لاحظ أن هذا المنطق المعقد إلى حد ما تم تنفيذه في حوالي 150 سطرًا مفيدًا من الكود على lsFusion. إن تنفيذه بنفس الصيغة في المنصات الأخرى (على سبيل المثال ، 1C) هي مهمة أكثر صعوبة بكثير.

يستخدم المخطط الموصوف أعلاه على نطاق واسع في حل تخطيط موارد المؤسسات القائم على lsFusion. استخدامه ، مع التعديلات المختلفة ، ويدعم قوائم أسعار الموردين ، وأسعار البيع بالتجزئة الإدارة والأسهم والعديد من معلمات الإدارة الأخرى.

يمكن تعقيد القالب عن طريق إضافة عدة كيانات إلى المستند (على سبيل المثال ، يمكن إضافة مورد إلى المستودع) ، وكذلك تحديد العديد من السمات في وثيقة واحدة في وقت واحد. على وجه الخصوص ، يمكنك إضافة الكيان "نوع السعر" ، وفي السطر "المستند" ، قم بتعيين السعر الخاص بتدرج السطر ونوع السعر المقابل. في المنطق الموصوف أعلاه ، تحتاج فقط إلى إضافة بعض المعلمات الإضافية لبعض الخصائص.

بمساعدة عدة سطور إضافية من التعليمات البرمجية ، من الممكن إلغاء تسمية جميع سجلات التغيير في جدول واحد يمكن بناء الفهرس المقابل عليه. ثم سيتم اختيار أي قيمة لأي تاريخ في وقت لوغاريتمي. هذا التحسين ضروري عندما يكون هناك عدة مئات من ملايين السجلات في هذا الجدول.

يمكنك تجربة المثال المدمج عبر الإنترنت على الصفحة المقابلة للموقع (قسم النظام الأساسي). إليك شفرة المصدر الكاملة التي تحتاج إلى لصقها في الحقل المطلوب:

شفرة المصدر
REQUIRE Authentication, Time;

CLASS Group ' ' ;
name '' = DATA ISTRING [ 50 ] (Group);

parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

FORM group ' '
OBJECTS g = Group PANEL
PROPERTIES (g) name, nameParent

EDIT Group OBJECT g
;

FORM groups ' '
OBJECTS g = Group
PROPERTIES (g) READONLY name, nameParent
PROPERTIES (g) NEWSESSION NEW , EDIT , DELETE

LIST Group OBJECT g
;

NAVIGATOR {
NEW groups;
}

CLASS Stock '' ;
name '' = DATA ISTRING [ 50 ] (Stock);

group '' = DATA Group (Stock);
nameGroup '' (Stock st) = name(group(st));

FORM stock ''
OBJECTS s = Stock PANEL
PROPERTIES (s) name, nameGroup

EDIT Stock OBJECT s
;

FORM stocks ''
OBJECTS s = Stock
PROPERTIES (s) READONLY name, nameGroup
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Stock OBJECT s
;

NAVIGATOR {
NEW stocks;
}

CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p
;

NAVIGATOR {
NEW products;
}

CLASS PriceList '-' ;
fromDate ' ' = DATA DATE (PriceList);
toDate ' ' = DATA DATE (PriceList);

createdTime ' ' = DATA DATETIME (PriceList);
createdUser = DATA User (PriceList);
nameCreatedUser '' (PriceList p) = name(createdUser(p));

WHEN LOCAL SET (PriceList p IS PriceList) DO
fromDate(p) <- currentDate();

WHEN SET (PriceList p IS PriceList) DO {
createdTime(p) <- currentDateTime();
createdUser(p) <- currentUser();
}

CLASS PriceListDetail ' -' ;
priceList = DATA PriceList (PriceListDetail) NONULL DELETE ;

product = DATA Product (PriceListDetail);
nameProduct '' (PriceListDetail d) = name(product(d));

price '' = DATA NUMERIC [ 10 , 2 ] (PriceListDetail);

fromDate ' ' (PriceListDetail d) = fromDate(priceList(d));
toDate ' ' (PriceListDetail d) = toDate(priceList(d));

dataIn '' = DATA BOOLEAN (PriceList, Group);

in ' ()' (PriceList p, Group child) =
GROUP LAST dataIn(p, Group parent) ORDER DESC level(child, parent) WHERE dataIn(p, parent);

dataIn '' = DATA BOOLEAN (PriceList, Stock);
in '' (PriceList p, Stock s) = dataIn(p, s) OR in(p, group(s));

stocks '' (PriceList p) = CONCAT ' / ' ,
GROUP CONCAT name(Group g) IF dataIn(p, g), ',' ORDER g,
GROUP CONCAT name(Stock s) IF dataIn(p, s), ',' ORDER s
CHARWIDTH 30 ;

priceListDetail (Product p, Stock s, DATE dt) =
GROUP LAST PriceListDetail d
ORDER fromDate(d), d
WHERE product(d) = p AND in(priceList(d), s) AND
fromDate(d) <= dt AND NOT toDate(d) < dt;

price '' (Product p, Stock s, DATE dt) = price(priceListDetail(p, s, dt));
fromDate ' ' (Product p, Stock s, DATE dt) = fromDate(priceListDetail(p, s, dt));
toDate ' ' (Product p, Stock s, DATE dt) = toDate(priceListDetail(p, s, dt));

FORM priceList '-'
OBJECTS p = PriceList PANEL
PROPERTIES (p) fromDate, toDate

EDIT PriceList OBJECT p
;

EXTEND FORM priceList
OBJECTS d = PriceListDetail
PROPERTIES (d) nameProduct, price
PROPERTIES (d) NEW , DELETE
FILTERS priceList(d) = p
;

EXTEND FORM priceList
TREE stocks g = Group PARENT parent, s = Stock
PROPERTIES READONLY name(g), name(s)
PROPERTIES dataIn(p, g), in(p, g)
PROPERTIES dataIn(p, s), in(p, s)
FILTERS group(s) = g
;

DESIGN priceList {
OBJECTS {
NEW pane {
fill = 1 ;
type = TABBED ;
MOVE BOX (d) { caption = '' ; }
MOVE BOX ( TREE stocks) { caption = '' ; }
}
}
}

FORM managePrices ' '
OBJECTS p = PriceList
PROPERTIES (p) READONLY fromDate, toDate, stocks, createdTime, nameCreatedUser
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

OBJECTS d = PriceListDetail
PROPERTIES (d) READONLY nameProduct, price
FILTERS priceList(d) = p
;

EXTEND FORM managePrices
OBJECTS dt = DATE PANEL
PROPERTIES VALUE (dt)

TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)

OBJECTS s = Stock
PROPERTIES (s) READONLY name, nameGroup
FILTERS level(group(s), g)
;

EXTEND FORM managePrices
OBJECTS pr = Product
PROPERTIES READONLY name(pr), price(pr, s, dt), fromDate(pr, s, dt), toDate(pr, s, dt)
FILTERS price(pr, s, dt)
;

EXTEND FORM managePrices
OBJECTS prd = PriceListDetail
PROPERTIES READONLY BACKGROUND (priceListDetail(pr, s, dt) = prd)
fromDate(prd), toDate(prd), '' = stocks(priceList(prd)), price(prd)
FILTERS product(prd) = pr AND in(priceList(prd), s)
;

edit (PriceListDetail d) + { edit(priceList(d)); }
EXTEND FORM managePrices
PROPERTIES (prd) NEWSESSION EDIT
;

DESIGN managePrices {
OBJECTS {
NEW pane {
fill = 1 ;
type = TABBED ;
NEW priceLists {
caption = '-' ;
MOVE BOX (p);
MOVE BOX (d);
}
NEW prices {
caption = '' ;
fill = 1 ;
type = SPLITH ;
NEW leftPane {
MOVE BOX (dt);
MOVE BOX ( TREE groups);
MOVE BOX (s);
}
NEW rightPane {
fill = 3 ;
type = SPLITV ;
MOVE BOX (pr) { fill = 3 ; }
MOVE BOX (prd);
}
}
}
}
}

NAVIGATOR {
NEW managePrices;
}

Source: https://habr.com/ru/post/ar468553/


All Articles