I designed an experiment to test configurator rule execution order in a BOM with configurable subassemblies. I wanted to see if a rule on Mtl 20 in the parent configurator would run before or after a rule on a material of a subassembly that is Mtl 10 in the parent. The purpose was to see if a subassembly configurator could pass a global to a peer with a later MtlSeq. What I found was neither of my expected outcomes, and so bizarre that I can’t puzzle out what’s really happening.
The parent and sub both have an input bound to the same global. If I set the value of that input in the parent, the input in the child is initialized with the value from the parent as expected. The child has this code in a rule on a material:
This rule does set the Description field… but sets it to the original value passed in from the parent, not to “chicken butt”. It behaves as if the first line weren’t there. Is it not possible for rules to assign values to inputs? If that’s the case, 1) it’s a bug/design flaw that the Value property has a setter that does nothing; this code should result in a syntax error; and 2) I’m going to have to rethink my whole configurator…
Heard back from support on this. Working as intended. They encourage duplicating business logic in other configurators rather than passing them computed values, and they encourage moving business logic out of subassembly configurators and up into the parent configurator’s UI designer where the input Value setter works as expected. I also cannot seem to get through to them that if an API doesn’t work, it shouldn’t exist. If it’s intentional that method rules can’t set input values, the class that represents inputs in method rules should not have a setter on its Value property.
So, I’m implementing configurator UD methods to read and write my own globals from a UD table, keyed by quote number, quote line, and variable name. Not only will this allow method rules to write globals, but I’ll be able to read them without binding them to hidden UI inputs.
The issue is that you need two different inputs bound to the same global variable. You can’t affect the parent input value from the sub, but you CAN set the global variable, provided you have an input in the current configurator bound to it. And once you set the variable, the parent’s input value is also indirectly set, because it’s bound to the same global. In my case I have taken the habit to create hidden VAR* inputs bound to whatever global variables I’ll need, and read and write always to the current configurator’s input.
You can only set the value of an input in UI Designer code like the On Page Leave expression. You cannot set it in a method rule. Support didn’t tell me this; I discovered and tested it myself. The code in my original post proves that it doesn’t work. You can’t set a global bound to the input because you can’t set the input!
This is because the configuration is always treated sequentially… By the time you save the configuration for your sub, the configuration for the parent has already been written to disk.
The rules, however, only run when you call GetDetails, after all the configuration is already written, so it wouldn’t even really make sense to try to set the global variable from the rules. The idea of global variables is to pass DOWN information in the sequence, not UP.
I’m not quite sure why you would want to change the parent config from a child to begin with…
Being unable to write globals in method rules means the only way to pass computed values down is by moving all your business logic to the UI.
We make skylights. Inputs include the dimensions of the rough opening and some values that determine what frame extrusions to use. Frame extrusions have different widths. The frame configurator knows how to look up the widths and compute the dimensions of the glazing that fits in the frame. It wants to pass this value to the glazing configurator. The glazing configurator should know nothing about extrusions; all it should know about is its own dimensions and materials. This keeps the configurators decoupled, composable, and easily maintainable. Basic development best practices.
I expected that I could bind a global to inputs in the frame and glazing configurators, set it in the frame configurator, and read it in the glazing configurator. But you cannot set an input in a method rule. The presence of a setter misleads you into thinking you can, so you get pretty far into your design before you discover that you can’t. You can set it in code hung on the UI, but the business logic doesn’t belong there. To keep things clean and maintainable, method rules absolutely must have a way to pass calculated values to other configurators.
There’s also a major limitation that MRP will not copy configurator inputs to a job for a Plan As Assembly material manufactured in another site, and will not create a job at all (or actually, creates a job and then deletes it) for a part that has a product configurator but no inputs. I found a way around this, too. MRP will create jobs in other sites for materials that have no-inputs configurators, and I found a way to pass them a pseudo-configuration. My reimplementation of globals may turn out to be an even cleaner way to do that.