Developing an application, we create Metadata Objects and specify their data structure. Working with the application, users fill this structure with their data. So, there is a seemingly apparent split of responsibilities: a developer uses Designer to specify what set of attributes is the best fit for the Metadata Object, and then a user uses 1C client to merely fill out these attributes.
Sounds way too simple to be true, doesn’t it?
Let’s take this ribeye steak for example. There are so many things I need to remember about it besides these attributes I’ve got in the app. What’s its best-before date? What marbling grade does it have? Was it previously frozen or not? So, how do I add these attributes to the picture? Should I include them in the Products catalog attribute, so that a user could input their values just like this?
So, let’s discuss this approach and some other similarly bad ideas. And then look at the solution called Characteristics that we recommend to use when you work with the Platform.
So, this is our Products catalog, and the most obvious idea is to just add all attributes we might need at the design time. But there is no such thing as marbling for Pringles chips or Bic pens, for that matter. Which, in their turn, have color and point size.
What else might we need? Everything: clock rate, nutrition value, screen size - the list goes on and on. And the worst part? You will need to run the Designer and change the application every time you want to add a new attribute.
OK, How about we leave the Products catalog alone, and store these miscellaneous attributes as table rows rather than columns? Then we just add the Product reference field to the table and point to a product each specific attribute belongs too.
It’s already so much better, isn’t it?
We can add new products with new attributes without changing the data structure. But we might also need to reuse the same attributes for different products. For this to work properly we need to separate the miscellaneous attribute descriptions from their values, so that this table stores all attribute names and this one - their values and references to the first table.
So, the idea behind this is that entering the product details a user selects an attribute from this table and then enters its value to this one. To set reasonable limits to users’ creativity and make the app more error-proof, I would also add the attribute’s type information to this table. For some attributes like Marbling or Color we might want to store expandable lists of values for users to fill out and to choose the values from.
This can be easily done with one more table, I’m going to call MiscAttributeValueLists. The idea is to store all available values of an attribute along with a reference to the attribute’s record in this table. So, when a user fills out product’s attributes, the AttributeValue table stores not a value itself but a reference to AttributeValueLists table instead. And, of course, we can easily store other attributes’ lists in the same table.
So, let’s try and implement all this beauty, shall we?
So, this is my application, and I’m gonna start with this MiscAttributes guy that stores the list of all miscellaneous attributes and their types. This is what we use these Charts of Characteristics for, so I’m creating a new one and calling it MiscAttributes like this.
Now we need to select all the types our characteristics might have. These are not the types we set for some specific characteristics (that we don’t have yet). These are all allowed types our users will be able to choose from when they start creating characteristics. So, I’m selecting all the primitive types here.
This setting over here tells the Chart of Characteristics where we store the lists of values like colors or marbling score. So this will be this guy down here. So, let’s go here and create a new Catalog for this task.
I’m naming it like this and now I need to set this relation between this Catalog and the Chart of Characteristics. The best option here is to make the Chart the owner of the Catalog. So, I’m doing it just like this, and now we need to get back to the Chart and tell it to allow values of this new Catalog reference type.
OK, now to this guy storing the values of the attributes for specific products. The most suitable Metadata Class for the task would be the Information Register, so I’m creating a new one,and naming it like this.
This register should allow to store a single value for each unique product-attribute pair. So, these two fellows are the registers’ dimensions, and this one here is the register’s resource. OK, then. Adding the Product reference dimension. Adding a second dimension referring to the Chart.
Now, let’s add the Value resource and the next question is: what type should it have? Well, it probably should be the same exact bunch of allowed types we set for the Chart of Characteristics over here. But instead of checking all the same boxes once again here, we can just inherit the same type list from the Chart just like this.
We are not done yet, but let me show how it works so far before we move on.
So, this is our app. I added all the new Metadata Objects to the Products subsystem, so here they are. This is our miscellaneous attributes list. So, let’s get in and create attributes for our ribeye. Starting with the Best Before attribute. Attribute name. Date type. Done.
Next one: marbling score. Adding a new attribute. And for this one I need a list of available values for a user to choose from so I’m selecting this catalog as an attribute type.
And now I can go here and fill out the list with the values, like this.
Back to the attributes. And the last one: the Previously Frozen flag. Previously frozen. Boolean. Done.
Now, let’s go to the Products catalog and set these new attributes values for this beautiful ribeye. So, this is the link that will bring us to this table, like this. And I can start setting miscellaneous attributes values. Create a new attribute value. Best before. And now the Platform expects me to specify the attribute type before I can enter the value itself. Which isn’t exceptionally convenient, because the attribute implies this specific type, and I want this type to be set automatically for the attribute’s value.
OK, let’s go and fix it. So, this is my Information Register, and I want this Value field type to be set according to this Attribute setting, that lives in this Chart of Characteristics and equals Date type.
And this is how it’s done. I’m opening the Values field properties, and setting this Link by Type property to the Attribute field. OK, let’s see if it worked. We are back to our ribeye (god, I’m so hungry), I’m adding the Best Before value, and now the Value field type is set to Date automatically. Great.
Let’s now set its value like this, and done.
Setting up the marbling score, and here is the list of values, living in this Catalog down here. Selecting Prime.
And the last attribute: the Previously Frozen flag. Setting it up like this. And done.
Looks good, isn’t it? But there is one more thing we need to fix. Let me show you.
Let’s get back to the list of miscellaneous attributes, and add the Color attribute, like this. Now to the list of values for this attribute. Adding some colors I might need. And now I want to set the color attribute for my Bic Pens.
So, these are my pens. Miscellaneous attributes. Adding the Color attribute,and…
Wait a second. How come the colors and marbling scores are mixed together in the same list? Looks like this list doesn’t get filtered depending on the attribute we selected. Let me show how we can fix this.
So, this is out application, and this is the field I need to fill out. Here is the same field on our diagram. And this is what I need to do. I need to look up the Attribute reference (that’s already specified by a user) and select all records from this list that are owned by this attribute.
And this is how it’s set up. I’m opening this field properties, and here is the setting I need. Going in, and inside are all the reference-type fields of this register. I’m selecting the one we need, and the Platform suggests it filters the list by this field as an owner.
OK, let’s see if it works. So, this is our Bic pen. Miscellaneous attributes. Adding the color attribute. And this is the list of available values. No more marbling scores, colors only. Great.
By the way, what happens if I select the marbling instead? Now I get the list of marbling scores here. Which is good. But I also just created a Bic pen with Prime marbling score. Which is not that good.
So, we have one more issue to deal with. What we’re missing here is some kind of a connection between the Product list and the miscellaneous attributes list. Every product has to know what attributes make sense for it, so a user is presented with meaningful attributes only, and we protect ourselves from well-marbled Bic pens and other monstrous products of our users’ creativity.
So, what we need here is some kind of a table that remembers what attributes make sense for what products, like this. To make things easier I’d also like to bind attributes with product groups, so that the products belonging to a certain group could inherit its attributes.
For example, I might want to assign the Best Before and PreFrozen attributes to the Meat group, so that any meat product will have them automatically, just because it’s in the group.
I just implemented this functionality behind the scenes, so let me show you what I did.
First of all, I decided to implement this table as a Products catalog tabular section. It has the only field called Attribute that stores a reference to our MiscAttributes Chart, and the tabular section is set to be available both for catalog items and folders.
I also created an explicit item form and placed the tabular section to the Attributes page. This is where I will specify what miscellaneous attributes make sense for this product or group.
Now. When I’m adding a new attribute value to this table, this is the form I’m using. So, I created its explicit version and wrote this code in the form’s module.
And this is what’s going on here. When the form gets created on the server (which happens before it’s opened) I check if this field is filled out with some specific product using this piece of code here. Then I create an array and fill it out with all attributes belonging to this specific product in this procedure we’ll look at in a moment. And then I just pass this array as a filter condition for this field, so a user selects not from the full list of miscellaneous attributes but from a filtered list that includes only some of them.
Now let’s see how we collect the attributes for a specific product. This happens in this procedure over here. It receives the product reference as a parameter and fills out this array for the caller procedure to use as a filter. First it checks if there is there any attributes in the tabular section belonging to this product directly.
And if so, it adds it to the array. Then the procedure takes a folder the product sits in and calls itself recursively to collect all attributes from this folder and folders this folder belongs to all the way up to the top of the tree. And this simple procedure down here just adds attributes to the array after checking if they are not already there.
And this is it.
Let’s now check out how it works. So, this is our products and I already added these two attributes to the Meat folder tabular section. The ribeye steak living inside of the folder has one additional attribute belonging to it personally rather than to the entire Meat group.
So, when I go here and specify the attribute values I can select from all three attributes over here. This Beef Short Ribs product, on the other hand, does not have the Marbling score or any other attributes in its tabular section. So, when I specify its attribute values, I see only these two attributes it inherited from the folder and nothing more.
And if I look at the Bic pen living in a different folder, I won’t see any of those attributes in its list. But I will see this Color attribute that was taken from this product’s tabular section.
This is how we allow users to add new attributes to objects without changing the data structure.