/**
   * Loads the ASI template to be used.
   *
   * @param fromAttributeSetInstanceId
   * @return
   *     <ul>
   *       <li>ASI template
   *       <li><code>null</code> if this is not a valid settings and we need to dispose the dialog
   *     </ul>
   *
   * @throws AdempiereException if something failed
   */
  private final MAttributeSetInstance loadASITemplate(final int fromAttributeSetInstanceId) {
    final Properties ctx = getCtx();
    final int productId = getM_Product_ID();
    final boolean isPureProductASI = isPureProductASI();

    //
    // If there is not product specified
    // and we need a pure product ASI (i.e. not the ASI that we configure on product level)
    // => this dialog does not make sense and we need to dispose it ASAP
    // TODO: in future we shall do this checking BEFORE we reach this point
    if (productId <= 0 && isPureProductASI) {
      return null;
    }

    final MAttributeSetInstance asiTemplate;

    //
    // Load/Create the ASI
    // Get the M_AttributeSet.
    MAttributeSet as = null;
    if (productId > 0) {
      // Get/Create the ASI
      asiTemplate = MAttributeSetInstance.get(ctx, fromAttributeSetInstanceId, productId);
      if (asiTemplate == null) {
        throw new AdempiereException(
            "@NotFound@ @M_AttributeSetInstance_ID@ (@M_Product_ID@=" + productId + ")");
      }
      Env.setContext(ctx, m_WindowNo, "M_AttributeSet_ID", asiTemplate.getM_AttributeSet_ID());

      // Get Attribute Set
      as = asiTemplate.getMAttributeSet();
    } else {
      final int M_AttributeSet_ID = attributeContext.getM_AttributeSet_ID();
      asiTemplate =
          new MAttributeSetInstance(ctx, 0, M_AttributeSet_ID, ITrx.TRXNAME_None); // new ASI
      as = asiTemplate.getMAttributeSet();
      if (as == null && M_AttributeSet_ID == 0) {
        // FIXME: workaround to deal with M_AttributeSet_ID=0 which is an existing record
        as =
            queryBL
                .createQueryBuilder(I_M_AttributeSet.class)
                .setContext(ctx, ITrx.TRXNAME_None)
                .addEqualsFilter(I_M_AttributeSet.COLUMNNAME_M_AttributeSet_ID, 0)
                .create()
                .firstOnlyNotNull(MAttributeSet.class);
        asiTemplate.setMAttributeSet(as);
      }
    }
    // Product has no Attribute Set
    if (as == null) {
      throw new AdempiereException("@PAttributeNoAttributeSet@");
    }
    // Product has no Instance Attributes
    if (isPureProductASI && !as.isInstanceAttribute()) {
      throw new AdempiereException("@PAttributeNoInstanceAttribute@");
    }

    return asiTemplate;
  }
  private MAttributeSetInstance saveSelection0() {
    log.info("");

    final MAttributeSet as = asiTemplate.getMAttributeSet();
    Check.assumeNotNull(as, "as not null");

    // Create a new ASI which is copying the existing one
    final MAttributeSetInstance asi =
        new MAttributeSetInstance(getCtx(), 0, ITrx.TRXNAME_ThreadInherited);
    InterfaceWrapperHelper.copyValues(
        asiTemplate, asi, false); // honorIsCalculated=false => copy everything
    asi.setM_AttributeSet(as); // make sure we have the right AttributeSet model set

    //
    boolean changed = false;
    final Set<String> mandatory = new LinkedHashSet<>();

    // Lot
    if (!m_productWindow && as.isLot()) {
      String text = fieldLotString.getText();
      asi.setLot(text);
      if (as.isLotMandatory() && (text == null || text.length() == 0)) {
        mandatory.add(msgBL.translate(getCtx(), "Lot"));
      }
      changed = true;
    } // Lot

    // Serial No
    if (!m_productWindow && as.isSerNo()) {
      final String serNo = fieldSerNo.getText();
      asi.setSerNo(serNo);
      if (as.isSerNoMandatory() && Check.isEmpty(serNo, true)) {
        mandatory.add(msgBL.translate(getCtx(), "SerNo"));
      }
      changed = true;
    } // SerNo

    //
    // Guarantee Date (if required)
    if (fieldGuaranteeDateDisplayed) {
      final Timestamp guaranteeDate = fieldGuaranteeDate.getValue();
      asi.setGuaranteeDate(guaranteeDate);
      if (as.isGuaranteeDate() && as.isGuaranteeDateMandatory() && guaranteeDate == null) {
        mandatory.add(msgBL.translate(getCtx(), I_M_AttributeSetInstance.COLUMNNAME_GuaranteeDate));
      }
      changed = true;
    }

    //
    // New Instance
    if (changed || asi.getM_AttributeSetInstance_ID() <= 0) {
      InterfaceWrapperHelper.save(asi);
      changed = true;
    }
    final int attributeSetInstanceId = asi.getM_AttributeSetInstance_ID();

    //
    // Save Instance Attributes
    for (final MAttribute attribute : m_attributes) {
      final CEditor editor = attributeId2editor.get(attribute.getM_Attribute_ID());

      if (MAttribute.ATTRIBUTEVALUETYPE_List.equals(attribute.getAttributeValueType())) {
        @SuppressWarnings("unchecked")
        final CComboBox<I_M_AttributeValue> editorCombo = (CComboBox<I_M_AttributeValue>) editor;
        final I_M_AttributeValue attributeValue = editorCombo.getSelectedItem();
        if (attribute.isMandatory() && attributeValue == null) {
          mandatory.add(attribute.getName());
        }
        attribute.setMAttributeInstance(attributeSetInstanceId, attributeValue);
      } else if (MAttribute.ATTRIBUTEVALUETYPE_Number.equals(attribute.getAttributeValueType())) {
        final VNumber editorNumber = (VNumber) editor;
        final BigDecimal value = (BigDecimal) editorNumber.getValue();
        if (attribute.isMandatory() && value == null) {
          mandatory.add(attribute.getName());
        }
        attribute.setMAttributeInstance(attributeSetInstanceId, value);
      } else if (MAttribute.ATTRIBUTEVALUETYPE_Date.equals(attribute.getAttributeValueType())) {
        final VDate editorDate = (VDate) editor;
        final Timestamp value = editorDate.getValue();
        if (attribute.isMandatory() && value == null) {
          mandatory.add(attribute.getName());
        }
        attribute.setMAttributeInstance(attributeSetInstanceId, value);
      } else {
        final VString editorString = (VString) editor;
        final String value = editorString.getText();
        if (attribute.isMandatory() && Check.isEmpty(value, false)) {
          mandatory.add(attribute.getName());
        }
        attribute.setMAttributeInstance(attributeSetInstanceId, value);
      }
      changed = true;
    } // for all attributes

    //
    // Throw exception if there are some mandatory fields which were not set
    if (!mandatory.isEmpty()) {
      throw new AdempiereException("@FillMandatory@ " + StringUtils.toString(mandatory, ", "));
    }

    // Save Model
    if (changed) {
      asi.setMAttributeSet(as); // NOTE: this is workaround for the case when M_AttributeSet_ID=0
      attributeSetInstanceBL.setDescription(asi);
      InterfaceWrapperHelper.save(asi);
    }

    return asi;
  }