# Screens POST-CHECK Validation

This document contains the complete executable validation code for screens.json files. Run this validation after writing ALL screens.json files for a module.

## Validation Levels

- **BLOCKING errors:** Must be fixed before proceeding to the next step
- **WARNING errors:** Should be reviewed but do not block progress

## Validation Script

```javascript
for (const mod of modules) {
  const screensPath = resolveModuleDir(mod.applicationCode, mod.code) + '/screens.json';
  const entitiesPath = resolveModuleDir(mod.applicationCode, mod.code) + '/entities.json';
  const permsPath = resolveModuleDir(mod.applicationCode, mod.code) + '/permissions.json';

  const screens = READ(screensPath);
  const entities = READ(entitiesPath);
  const perms = READ(permsPath);

  const entityNames = entities.entities.map(e => e.name);
  const attrMap = {};
  entities.entities.forEach(e => {
    attrMap[e.name] = e.attributes.map(a => a.name);
  });
  const computedAttrs = {};
  entities.entities.forEach(e => {
    computedAttrs[e.name] = e.attributes.filter(a => a.computed).map(a => a.name);
  });

  for (const section of screens.sections) {
    // CHECK 1: Every section has at least 1 resource
    ASSERT(section.resources.length > 0,
      `BLOCKING: ${mod.code}.${section.sectionCode} has 0 resources`);

    for (const res of section.resources) {
      // CHECK 2: Every resource has a valid entity
      ASSERT(entityNames.includes(res.entity),
        `BLOCKING: ${res.code} references unknown entity "${res.entity}"`);

      // CHECK 3: Every resource has a permission
      ASSERT(res.permission,
        `BLOCKING: ${res.code} missing permission`);

      // CHECK 4: SmartTable columns reference valid attributes
      if (res.type === 'SmartTable' && res.columns) {
        for (const col of res.columns) {
          const field = col.field.split('.')[0];
          ASSERT(attrMap[res.entity]?.includes(field),
            `BLOCKING: ${res.code} column "${col.field}" not in ${res.entity}`);
        }
      }

      // CHECK 5: SmartForm fields reference valid attributes
      if (res.type === 'SmartForm' && res.tabs) {
        for (const tab of res.tabs) {
          if (tab.fields) {
            for (const f of tab.fields) {
              ASSERT(attrMap[res.entity]?.includes(f.field),
                `BLOCKING: ${res.code} field "${f.field}" not in ${res.entity}`);
            }
          }
        }
      }

      // CHECK 6: Lookup fields have entity
      if (res.type === 'SmartForm' && res.tabs) {
        for (const tab of res.tabs) {
          if (tab.fields) {
            for (const f of tab.fields) {
              if (f.type === 'lookup') {
                ASSERT(f.entity,
                  `BLOCKING: ${res.code} lookup field "${f.field}" missing entity`);
              }
            }
          }
        }
      }

      // CHECK 7: Computed attributes are readonly or absent (WARNING)
      if (res.type === 'SmartForm' && res.tabs) {
        const compAttrs = computedAttrs[res.entity] || [];
        for (const tab of res.tabs) {
          if (tab.fields) {
            for (const f of tab.fields) {
              if (compAttrs.includes(f.field) && !f.readonly) {
                WARN(`${res.code} field "${f.field}" is computed but not readonly`);
              }
            }
          }
        }
      }

      // CHECK 8: SmartFilter filters reference valid fields + lookup entity
      if (res.type === 'SmartFilter' && res.filters) {
        for (const filter of res.filters) {
          if (filter.type === 'lookup') {
            ASSERT(filter.entity,
              `BLOCKING: ${res.code} filter "${filter.field}" type lookup missing entity`);
          }
        }
      }
    }
  }

  // CHECK 9: SmartFilter mandatory for list sections with SmartTable (BLOCKING)
  for (const section of screens.sections) {
    if (section.sectionCode === 'list') {
      const hasTable = section.resources.some(r => r.type === 'SmartTable');
      const hasFilter = section.resources.some(r => r.type === 'SmartFilter');
      if (hasTable && !hasFilter && !mod.code.includes('Portal')) {
        ASSERT(false, `BLOCKING: ${mod.code}.list has SmartTable but no SmartFilter`);
      }
    }
  }

  // CHECK 10: SmartKanban for workflow entities (WARNING)
  for (const e of entities.entities) {
    const statusAttr = e.attributes.find(a => a.name === 'status' && a.type === 'enum');
    if (statusAttr && (statusAttr.options || []).length >= 3 && !mod.code.includes('Portal')) {
      const hasKanban = screens.sections.some(s =>
        s.resources.some(r => r.type === 'SmartKanban' && r.entity === e.name)
      );
      if (!hasKanban) {
        WARN(`${mod.code}: entity "${e.name}" has ${statusAttr.options.length} workflow states but no SmartKanban`);
      }
    }
  }
}
```

## Validation Checklist

Run these checks after writing all screens.json files:

- [ ] Every resource has a valid `entity` referencing an entity from entities.json
- [ ] Every resource has a valid `permission` referencing permissions.json
- [ ] Every SmartTable column references a valid entity attribute
- [ ] Every SmartForm field references a valid entity attribute
- [ ] Every SmartTable `rowActions[]` has `action` and `permission` fields
- [ ] Every section has at least one resource
- [ ] Every module has at least one section
- [ ] Every lookup field (SmartForm + SmartFilter) has an `entity` property
- [ ] Computed attributes are `readonly: true` in detail forms or absent from create forms
- [ ] Every `list` section with SmartTable also has a SmartFilter (except Portal)
- [ ] Every entity with `status` enum >= 3 options has a SmartKanban resource (except Portal/config)

## Check Details

### CHECK 1: Section Resources
Every section MUST have at least one resource. Empty sections are invalid.

**Error Format:** `BLOCKING: {module}.{sectionCode} has 0 resources`

### CHECK 2: Entity References
Every resource MUST reference a valid entity name from entities.json.

**Error Format:** `BLOCKING: {resource.code} references unknown entity "{entity}"`

### CHECK 3: Permission References
Every resource MUST have a permission field.

**Error Format:** `BLOCKING: {resource.code} missing permission`

### CHECK 4: SmartTable Column Attributes
Every column in a SmartTable MUST reference a valid attribute from the entity. Nested attributes (e.g., `department.name`) are split on the first dot.

**Error Format:** `BLOCKING: {resource.code} column "{field}" not in {entity}`

### CHECK 5: SmartForm Field Attributes
Every field in a SmartForm tab MUST reference a valid attribute from the entity.

**Error Format:** `BLOCKING: {resource.code} field "{field}" not in {entity}`

### CHECK 6: Lookup Field Entity References
Every lookup field in a SmartForm MUST include an `entity` property identifying the target entity.

**Error Format:** `BLOCKING: {resource.code} lookup field "{field}" missing entity`

### CHECK 7: Computed Attribute Readonly (WARNING)
Computed attributes (marked `computed: true` in entities.json) appearing in SmartForm SHOULD have `readonly: true`. This is a warning, not blocking.

**Warning Format:** `{resource.code} field "{field}" is computed but not readonly`

### CHECK 8: SmartFilter Lookup Entity References
Every lookup filter in a SmartFilter MUST include an `entity` property.

**Error Format:** `BLOCKING: {resource.code} filter "{field}" type lookup missing entity`

### CHECK 9: SmartFilter Mandatory for Lists
Every `list` section with a SmartTable MUST also include a SmartFilter resource (except for Portal modules).

**Error Format:** `BLOCKING: {module}.list has SmartTable but no SmartFilter`

### CHECK 10: SmartKanban Workflow Detection (WARNING)
Every entity with a `status` enum attribute having 3 or more options (except Portal/config modules) SHOULD have a SmartKanban resource. This is a warning, not blocking.

**Warning Format:** `{module}: entity "{entity}" has {count} workflow states but no SmartKanban`

## Error Resolution

### BLOCKING Errors
Stop immediately and fix these errors before proceeding to step-02:

1. Add missing resources to empty sections
2. Verify entity names match entities.json exactly (case-sensitive)
3. Add permission properties to all resources
4. Fix column/field names to match entity attributes
5. Add entity properties to all lookup fields
6. Add SmartFilter to list sections with SmartTable

### WARNING Errors
Review and fix if applicable, but these do not block progress:

1. Add `readonly: true` to computed fields in forms
2. Consider adding SmartKanban for workflow entities with status enums

## Exit Criteria

All BLOCKING checks must pass before proceeding to step-02. WARNING checks should be reviewed and fixed where applicable.
