@fieldia/core 0.26.1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -3
- package/package.json +1 -1
- package/schema/page.schema.json +227 -23
- package/src/index.d.mts +8 -2
- package/src/index.d.ts +8 -2
- package/src/index.js +22 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +9 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/expression/app-functions.d.mts +47 -0
- package/src/lib/expression/app-functions.d.ts +47 -0
- package/src/lib/expression/app-functions.js +64 -0
- package/src/lib/expression/app-functions.js.map +1 -0
- package/src/lib/expression/app-functions.mjs +58 -0
- package/src/lib/expression/app-functions.mjs.map +1 -0
- package/src/lib/expression/dates.d.mts +21 -0
- package/src/lib/expression/dates.d.ts +21 -0
- package/src/lib/expression/dates.js +73 -0
- package/src/lib/expression/dates.js.map +1 -0
- package/src/lib/expression/dates.mjs +67 -0
- package/src/lib/expression/dates.mjs.map +1 -0
- package/src/lib/expression/evaluator.js +12 -6
- package/src/lib/expression/evaluator.js.map +1 -1
- package/src/lib/expression/evaluator.mjs +12 -6
- package/src/lib/expression/evaluator.mjs.map +1 -1
- package/src/lib/expression/expression.d.mts +7 -3
- package/src/lib/expression/expression.d.ts +7 -3
- package/src/lib/expression/expression.js +8 -2
- package/src/lib/expression/expression.js.map +1 -1
- package/src/lib/expression/expression.mjs +7 -2
- package/src/lib/expression/expression.mjs.map +1 -1
- package/src/lib/expression/functions-part.d.mts +7 -0
- package/src/lib/expression/functions-part.d.ts +7 -0
- package/src/lib/expression/functions-part.js +13 -0
- package/src/lib/expression/functions-part.js.map +1 -0
- package/src/lib/expression/functions-part.mjs +8 -0
- package/src/lib/expression/functions-part.mjs.map +1 -0
- package/src/lib/expression/functions.d.mts +60 -3
- package/src/lib/expression/functions.d.ts +60 -3
- package/src/lib/expression/functions.js +109 -15
- package/src/lib/expression/functions.js.map +1 -1
- package/src/lib/expression/functions.mjs +103 -16
- package/src/lib/expression/functions.mjs.map +1 -1
- package/src/lib/expression/kinds.d.mts +31 -0
- package/src/lib/expression/kinds.d.ts +31 -0
- package/src/lib/expression/kinds.js +159 -0
- package/src/lib/expression/kinds.js.map +1 -0
- package/src/lib/expression/kinds.mjs +151 -0
- package/src/lib/expression/kinds.mjs.map +1 -0
- package/src/lib/expression/modifier.d.mts +4 -3
- package/src/lib/expression/modifier.d.ts +4 -3
- package/src/lib/expression/modifier.js +4 -3
- package/src/lib/expression/modifier.js.map +1 -1
- package/src/lib/expression/modifier.mjs +4 -3
- package/src/lib/expression/modifier.mjs.map +1 -1
- package/src/lib/expression/more-functions.d.mts +8 -0
- package/src/lib/expression/more-functions.d.ts +8 -0
- package/src/lib/expression/more-functions.js +235 -0
- package/src/lib/expression/more-functions.js.map +1 -0
- package/src/lib/expression/more-functions.mjs +230 -0
- package/src/lib/expression/more-functions.mjs.map +1 -0
- package/src/lib/expression/parser.d.mts +4 -1
- package/src/lib/expression/parser.d.ts +4 -1
- package/src/lib/expression/parser.js +6 -3
- package/src/lib/expression/parser.js.map +1 -1
- package/src/lib/expression/parser.mjs +6 -3
- package/src/lib/expression/parser.mjs.map +1 -1
- package/src/lib/expression/reads.d.mts +2 -2
- package/src/lib/expression/reads.d.ts +2 -2
- package/src/lib/expression/reads.js +6 -4
- package/src/lib/expression/reads.js.map +1 -1
- package/src/lib/expression/reads.mjs +6 -4
- package/src/lib/expression/reads.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +16 -16
- package/src/lib/format/actions.d.ts +16 -16
- package/src/lib/format/check-page.d.mts +7 -2
- package/src/lib/format/check-page.d.ts +7 -2
- package/src/lib/format/check-page.js +10 -4
- package/src/lib/format/check-page.js.map +1 -1
- package/src/lib/format/check-page.mjs +10 -4
- package/src/lib/format/check-page.mjs.map +1 -1
- package/src/lib/format/choice-options.d.mts +34 -0
- package/src/lib/format/choice-options.d.ts +34 -0
- package/src/lib/format/choice-options.js +60 -0
- package/src/lib/format/choice-options.js.map +1 -0
- package/src/lib/format/choice-options.mjs +51 -0
- package/src/lib/format/choice-options.mjs.map +1 -0
- package/src/lib/format/endings-checks.d.mts +8 -0
- package/src/lib/format/endings-checks.d.ts +8 -0
- package/src/lib/format/endings-checks.js +23 -0
- package/src/lib/format/endings-checks.js.map +1 -0
- package/src/lib/format/endings-checks.mjs +20 -0
- package/src/lib/format/endings-checks.mjs.map +1 -0
- package/src/lib/format/endings.d.mts +40 -0
- package/src/lib/format/endings.d.ts +40 -0
- package/src/lib/format/endings.js +15 -0
- package/src/lib/format/endings.js.map +1 -0
- package/src/lib/format/endings.mjs +12 -0
- package/src/lib/format/endings.mjs.map +1 -0
- package/src/lib/format/field.d.mts +151 -16
- package/src/lib/format/field.d.ts +151 -16
- package/src/lib/format/field.js +64 -11
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +63 -10
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/head-checks.d.mts +2 -0
- package/src/lib/format/head-checks.d.ts +2 -0
- package/src/lib/format/head-checks.js +2 -59
- package/src/lib/format/head-checks.js.map +1 -1
- package/src/lib/format/head-checks.mjs +1 -58
- package/src/lib/format/head-checks.mjs.map +1 -1
- package/src/lib/format/later-checks.d.mts +17 -0
- package/src/lib/format/later-checks.d.ts +17 -0
- package/src/lib/format/later-checks.js +20 -0
- package/src/lib/format/later-checks.js.map +1 -0
- package/src/lib/format/later-checks.mjs +17 -0
- package/src/lib/format/later-checks.mjs.map +1 -0
- package/src/lib/format/layout.d.mts +241 -188
- package/src/lib/format/layout.d.ts +241 -188
- package/src/lib/format/layout.js +33 -28
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +13 -3
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +2 -1
- package/src/lib/format/layout.types.d.ts +2 -1
- package/src/lib/format/limits-checks.d.mts +14 -0
- package/src/lib/format/limits-checks.d.ts +14 -0
- package/src/lib/format/limits-checks.js +36 -0
- package/src/lib/format/limits-checks.js.map +1 -0
- package/src/lib/format/limits-checks.mjs +33 -0
- package/src/lib/format/limits-checks.mjs.map +1 -0
- package/src/lib/format/limits.d.mts +24 -0
- package/src/lib/format/limits.d.ts +24 -0
- package/src/lib/format/limits.js +35 -0
- package/src/lib/format/limits.js.map +1 -0
- package/src/lib/format/limits.mjs +32 -0
- package/src/lib/format/limits.mjs.map +1 -0
- package/src/lib/format/list-checks.d.mts +44 -0
- package/src/lib/format/list-checks.d.ts +44 -0
- package/src/lib/format/list-checks.js +340 -0
- package/src/lib/format/list-checks.js.map +1 -0
- package/src/lib/format/list-checks.mjs +336 -0
- package/src/lib/format/list-checks.mjs.map +1 -0
- package/src/lib/format/map-checks.d.mts +33 -0
- package/src/lib/format/map-checks.d.ts +33 -0
- package/src/lib/format/map-checks.js +180 -0
- package/src/lib/format/map-checks.js.map +1 -0
- package/src/lib/format/map-checks.mjs +175 -0
- package/src/lib/format/map-checks.mjs.map +1 -0
- package/src/lib/format/mapchecks-part.d.mts +5 -0
- package/src/lib/format/mapchecks-part.d.ts +5 -0
- package/src/lib/format/mapchecks-part.js +11 -0
- package/src/lib/format/mapchecks-part.js.map +1 -0
- package/src/lib/format/mapchecks-part.mjs +6 -0
- package/src/lib/format/mapchecks-part.mjs.map +1 -0
- package/src/lib/format/option-checks.d.mts +16 -0
- package/src/lib/format/option-checks.d.ts +16 -0
- package/src/lib/format/option-checks.js +39 -0
- package/src/lib/format/option-checks.js.map +1 -0
- package/src/lib/format/option-checks.mjs +36 -0
- package/src/lib/format/option-checks.mjs.map +1 -0
- package/src/lib/format/options-part.d.mts +6 -0
- package/src/lib/format/options-part.d.ts +6 -0
- package/src/lib/format/options-part.js +11 -0
- package/src/lib/format/options-part.js.map +1 -0
- package/src/lib/format/options-part.mjs +7 -0
- package/src/lib/format/options-part.mjs.map +1 -0
- package/src/lib/format/page.d.mts +147 -71
- package/src/lib/format/page.d.ts +147 -71
- package/src/lib/format/page.js +5 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +5 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/part-checks.d.mts +20 -0
- package/src/lib/format/part-checks.d.ts +20 -0
- package/src/lib/format/part-checks.js +3 -0
- package/src/lib/format/part-checks.js.map +1 -0
- package/src/lib/format/part-checks.mjs +2 -0
- package/src/lib/format/part-checks.mjs.map +1 -0
- package/src/lib/format/references.d.mts +71 -65
- package/src/lib/format/references.d.ts +71 -65
- package/src/lib/format/references.js +194 -631
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +194 -631
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/seats-checks.d.mts +3 -0
- package/src/lib/format/seats-checks.d.ts +3 -0
- package/src/lib/format/seats-checks.js +13 -0
- package/src/lib/format/seats-checks.js.map +1 -0
- package/src/lib/format/seats-checks.mjs +10 -0
- package/src/lib/format/seats-checks.mjs.map +1 -0
- package/src/lib/format/sheet-head-checks.d.mts +4 -0
- package/src/lib/format/sheet-head-checks.d.ts +4 -0
- package/src/lib/format/sheet-head-checks.js +68 -0
- package/src/lib/format/sheet-head-checks.js.map +1 -0
- package/src/lib/format/sheet-head-checks.mjs +65 -0
- package/src/lib/format/sheet-head-checks.mjs.map +1 -0
- package/src/lib/format/signature-checks.d.mts +7 -0
- package/src/lib/format/signature-checks.d.ts +7 -0
- package/src/lib/format/signature-checks.js +32 -0
- package/src/lib/format/signature-checks.js.map +1 -0
- package/src/lib/format/signature-checks.mjs +29 -0
- package/src/lib/format/signature-checks.mjs.map +1 -0
- package/src/lib/format/signature-options.d.mts +24 -0
- package/src/lib/format/signature-options.d.ts +24 -0
- package/src/lib/format/signature-options.js +30 -0
- package/src/lib/format/signature-options.js.map +1 -0
- package/src/lib/format/signature-options.mjs +26 -0
- package/src/lib/format/signature-options.mjs.map +1 -0
- package/src/lib/format/slots-checks.d.mts +4 -0
- package/src/lib/format/slots-checks.d.ts +4 -0
- package/src/lib/format/slots-checks.js +21 -0
- package/src/lib/format/slots-checks.js.map +1 -0
- package/src/lib/format/slots-checks.mjs +18 -0
- package/src/lib/format/slots-checks.mjs.map +1 -0
- package/src/lib/format/statusbar-checks.d.mts +8 -0
- package/src/lib/format/statusbar-checks.d.ts +8 -0
- package/src/lib/format/statusbar-checks.js +35 -0
- package/src/lib/format/statusbar-checks.js.map +1 -0
- package/src/lib/format/statusbar-checks.mjs +32 -0
- package/src/lib/format/statusbar-checks.mjs.map +1 -0
- package/src/lib/format/table-checks.d.mts +21 -0
- package/src/lib/format/table-checks.d.ts +21 -0
- package/src/lib/format/table-checks.js +86 -0
- package/src/lib/format/table-checks.js.map +1 -0
- package/src/lib/format/table-checks.mjs +82 -0
- package/src/lib/format/table-checks.mjs.map +1 -0
- package/src/lib/format/translate.js +2 -2
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +2 -2
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/upgrade.d.mts +6 -3
- package/src/lib/format/upgrade.d.ts +6 -3
- package/src/lib/format/upgrade.js +6 -3
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +6 -3
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/validate.d.mts +18 -2
- package/src/lib/format/validate.d.ts +18 -2
- package/src/lib/format/validate.js +36 -3
- package/src/lib/format/validate.js.map +1 -1
- package/src/lib/format/validate.mjs +35 -3
- package/src/lib/format/validate.mjs.map +1 -1
- package/src/lib/format/version.d.mts +2 -2
- package/src/lib/format/version.d.ts +2 -2
- package/src/lib/format/version.js +2 -2
- package/src/lib/format/version.js.map +1 -1
- package/src/lib/format/version.mjs +2 -2
- package/src/lib/format/version.mjs.map +1 -1
- package/src/lib/format/words.d.mts +6 -2
- package/src/lib/format/words.d.ts +6 -2
- package/src/lib/format/words.js +13 -7
- package/src/lib/format/words.js.map +1 -1
- package/src/lib/format/words.mjs +13 -7
- package/src/lib/format/words.mjs.map +1 -1
- package/src/lib/parts.d.mts +45 -0
- package/src/lib/parts.d.ts +45 -0
- package/src/lib/parts.js +60 -0
- package/src/lib/parts.js.map +1 -0
- package/src/lib/parts.mjs +53 -0
- package/src/lib/parts.mjs.map +1 -0
- package/src/lib/record/cells.d.mts +3 -3
- package/src/lib/record/cells.d.ts +3 -3
- package/src/lib/record/cells.js +16 -15
- package/src/lib/record/cells.js.map +1 -1
- package/src/lib/record/cells.mjs +16 -15
- package/src/lib/record/cells.mjs.map +1 -1
- package/src/lib/record/compute.d.mts +16 -5
- package/src/lib/record/compute.d.ts +16 -5
- package/src/lib/record/compute.js +57 -16
- package/src/lib/record/compute.js.map +1 -1
- package/src/lib/record/compute.mjs +57 -16
- package/src/lib/record/compute.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +140 -3
- package/src/lib/record/data-source.d.ts +140 -3
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/drafts.d.mts +19 -0
- package/src/lib/record/drafts.d.ts +19 -0
- package/src/lib/record/drafts.js +35 -0
- package/src/lib/record/drafts.js.map +1 -0
- package/src/lib/record/drafts.mjs +31 -0
- package/src/lib/record/drafts.mjs.map +1 -0
- package/src/lib/record/env.d.mts +5 -1
- package/src/lib/record/env.d.ts +5 -1
- package/src/lib/record/env.js +9 -1
- package/src/lib/record/env.js.map +1 -1
- package/src/lib/record/env.mjs +8 -1
- package/src/lib/record/env.mjs.map +1 -1
- package/src/lib/record/form-limits.d.mts +81 -0
- package/src/lib/record/form-limits.d.ts +81 -0
- package/src/lib/record/form-limits.js +231 -0
- package/src/lib/record/form-limits.js.map +1 -0
- package/src/lib/record/form-limits.mjs +224 -0
- package/src/lib/record/form-limits.mjs.map +1 -0
- package/src/lib/record/form.d.mts +173 -4
- package/src/lib/record/form.d.ts +173 -4
- package/src/lib/record/form.js +792 -88
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +792 -88
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/limits-part.d.mts +6 -0
- package/src/lib/record/limits-part.d.ts +6 -0
- package/src/lib/record/limits-part.js +12 -0
- package/src/lib/record/limits-part.js.map +1 -0
- package/src/lib/record/limits-part.mjs +7 -0
- package/src/lib/record/limits-part.mjs.map +1 -0
- package/src/lib/record/line-choices.d.mts +30 -0
- package/src/lib/record/line-choices.d.ts +30 -0
- package/src/lib/record/line-choices.js +42 -0
- package/src/lib/record/line-choices.js.map +1 -0
- package/src/lib/record/line-choices.mjs +36 -0
- package/src/lib/record/line-choices.mjs.map +1 -0
- package/src/lib/record/locales/ar.js +10 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +10 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +10 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +10 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +10 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +10 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +44 -3
- package/src/lib/record/memory-data-source.d.ts +44 -3
- package/src/lib/record/memory-data-source.js +102 -40
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +101 -40
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-kept.d.mts +20 -0
- package/src/lib/record/memory-kept.d.ts +20 -0
- package/src/lib/record/memory-kept.js +46 -0
- package/src/lib/record/memory-kept.js.map +1 -0
- package/src/lib/record/memory-kept.mjs +43 -0
- package/src/lib/record/memory-kept.mjs.map +1 -0
- package/src/lib/record/memory-later.d.mts +19 -0
- package/src/lib/record/memory-later.d.ts +19 -0
- package/src/lib/record/memory-later.js +39 -0
- package/src/lib/record/memory-later.js.map +1 -0
- package/src/lib/record/memory-later.mjs +35 -0
- package/src/lib/record/memory-later.mjs.map +1 -0
- package/src/lib/record/memory-part.d.mts +7 -0
- package/src/lib/record/memory-part.d.ts +7 -0
- package/src/lib/record/memory-part.js +13 -0
- package/src/lib/record/memory-part.js.map +1 -0
- package/src/lib/record/memory-part.mjs +8 -0
- package/src/lib/record/memory-part.mjs.map +1 -0
- package/src/lib/record/messages.d.mts +17 -0
- package/src/lib/record/messages.d.ts +17 -0
- package/src/lib/record/messages.js +10 -0
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +10 -0
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/option-rules.d.mts +50 -0
- package/src/lib/record/option-rules.d.ts +50 -0
- package/src/lib/record/option-rules.js +137 -0
- package/src/lib/record/option-rules.js.map +1 -0
- package/src/lib/record/option-rules.mjs +134 -0
- package/src/lib/record/option-rules.mjs.map +1 -0
- package/src/lib/record/rules.d.mts +2 -2
- package/src/lib/record/rules.d.ts +2 -2
- package/src/lib/record/rules.js +3 -3
- package/src/lib/record/rules.js.map +1 -1
- package/src/lib/record/rules.mjs +3 -3
- package/src/lib/record/rules.mjs.map +1 -1
- package/src/lib/record/run.js +9 -7
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +9 -7
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/search.d.mts +12 -2
- package/src/lib/record/search.d.ts +12 -2
- package/src/lib/record/search.js +29 -4
- package/src/lib/record/search.js.map +1 -1
- package/src/lib/record/search.mjs +29 -4
- package/src/lib/record/search.mjs.map +1 -1
- package/src/lib/record/set-when.d.mts +3 -3
- package/src/lib/record/set-when.d.ts +3 -3
- package/src/lib/record/set-when.js +9 -9
- package/src/lib/record/set-when.js.map +1 -1
- package/src/lib/record/set-when.mjs +9 -9
- package/src/lib/record/set-when.mjs.map +1 -1
- package/src/lib/record/signed.d.mts +21 -0
- package/src/lib/record/signed.d.ts +21 -0
- package/src/lib/record/signed.js +62 -0
- package/src/lib/record/signed.js.map +1 -0
- package/src/lib/record/signed.mjs +57 -0
- package/src/lib/record/signed.mjs.map +1 -0
- package/src/lib/record/taking.d.mts +60 -0
- package/src/lib/record/taking.d.ts +60 -0
- package/src/lib/record/taking.js +58 -0
- package/src/lib/record/taking.js.map +1 -0
- package/src/lib/record/taking.mjs +50 -0
- package/src/lib/record/taking.mjs.map +1 -0
- package/src/lib/record/value-map.js +11 -4
- package/src/lib/record/value-map.js.map +1 -1
- package/src/lib/record/value-map.mjs +11 -4
- package/src/lib/record/value-map.mjs.map +1 -1
- package/src/lib/record/values.d.mts +20 -0
- package/src/lib/record/values.d.ts +20 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +6 -1
- package/src/lib/record/waits.d.ts +6 -1
- package/src/lib/record/waits.js +2 -2
- package/src/lib/record/waits.js.map +1 -1
- package/src/lib/record/waits.mjs +2 -2
- package/src/lib/record/waits.mjs.map +1 -1
- package/src/runtime.d.mts +16 -5
- package/src/runtime.d.ts +16 -5
- package/src/runtime.js +41 -3
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +13 -0
- package/src/runtime.mjs.map +1 -1
|
@@ -1,37 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReferenceCheck = void 0;
|
|
4
|
+
const choice_options_1 = require("./choice-options");
|
|
4
5
|
const built_ins_1 = require("../expression/built-ins");
|
|
5
6
|
const modifier_1 = require("../expression/modifier");
|
|
6
7
|
const expression_1 = require("../expression/expression");
|
|
8
|
+
const app_functions_1 = require("../expression/app-functions");
|
|
7
9
|
const order_1 = require("../expression/order");
|
|
8
10
|
const words_1 = require("./words");
|
|
9
11
|
const hotkeys_1 = require("./hotkeys");
|
|
10
12
|
const head_checks_1 = require("./head-checks");
|
|
11
13
|
const section_checks_1 = require("./section-checks");
|
|
12
14
|
const pattern_1 = require("./pattern");
|
|
13
|
-
const bulk_edit_1 = require("./bulk-edit");
|
|
14
15
|
const duplicate_kinds_1 = require("./duplicate-kinds");
|
|
15
|
-
const import_fields_1 = require("./import-fields");
|
|
16
16
|
const zones_1 = require("../record/zones");
|
|
17
17
|
const messages_1 = require("../record/messages");
|
|
18
18
|
const autofill_group_1 = require("./autofill-group");
|
|
19
|
+
const limits_checks_1 = require("./limits-checks");
|
|
20
|
+
const seats_checks_1 = require("./seats-checks");
|
|
21
|
+
const list_checks_1 = require("./list-checks");
|
|
22
|
+
const slots_checks_1 = require("./slots-checks");
|
|
23
|
+
const signature_checks_1 = require("./signature-checks");
|
|
24
|
+
const endings_checks_1 = require("./endings-checks");
|
|
25
|
+
const map_checks_1 = require("./map-checks");
|
|
26
|
+
const table_checks_1 = require("./table-checks");
|
|
27
|
+
const statusbar_checks_1 = require("./statusbar-checks");
|
|
28
|
+
const option_checks_1 = require("./option-checks");
|
|
19
29
|
const has = (record, key) => Object.prototype.hasOwnProperty.call(record, key);
|
|
20
30
|
/** The kinds of field an expression can give a value to: the rest hold records, lines or files. */
|
|
21
31
|
const EXPRESSIBLE = new Set(['char', 'text', 'html', 'integer', 'float', 'monetary', 'boolean', 'date', 'datetime', 'selection', 'json']);
|
|
22
32
|
const EXPRESSIBLE_WORDS = 'text, numbers, yes or no, dates, choices and json can';
|
|
23
|
-
/** The kinds of field a list's filter bar can list the values of. */
|
|
24
|
-
const LISTS_ITS_VALUES = new Set(['selection', 'many2one', 'many2many', 'boolean', 'char', 'integer']);
|
|
25
|
-
/** Kinds of field a list's card shows as its picture: a picture held, or text holding a picture's address. */
|
|
26
|
-
const PICTURES = new Set(['image', 'binary', 'char']);
|
|
27
|
-
/** What a calendar's start and end can be: a day, or a day and a time. */
|
|
28
|
-
const CALENDAR_TIMES = new Set(['date', 'datetime']);
|
|
29
|
-
/** What a record's words on a calendar can be. */
|
|
30
|
-
const CALENDAR_WORDS = new Set(['char', 'text', 'selection', 'many2one', 'integer', 'float', 'monetary', 'date', 'datetime']);
|
|
31
|
-
/** What a calendar's columns come from: a value that repeats. */
|
|
32
|
-
const CALENDAR_COLUMNS = new Set(['many2one', 'selection', 'char', 'integer']);
|
|
33
|
-
/** What a record's colour comes from. */
|
|
34
|
-
const CALENDAR_COLOURS = new Set(['many2one', 'selection', 'char', 'integer', 'boolean']);
|
|
35
33
|
/** Names quoted and joined as a person lists them: "a", "b" and "c". */
|
|
36
34
|
const listed = (names) => {
|
|
37
35
|
const quoted = names.map((name) => `"${name}"`);
|
|
@@ -41,7 +39,12 @@ const listed = (names) => {
|
|
|
41
39
|
class ReferenceCheck {
|
|
42
40
|
page;
|
|
43
41
|
line;
|
|
42
|
+
options;
|
|
44
43
|
issues = [];
|
|
44
|
+
/** What does not stop the page, said beside it: the kinds an expression hands its functions (`CheckOptions.kinds`). */
|
|
45
|
+
warnings = [];
|
|
46
|
+
/** The parts whose checks are owed, not come yet (the one-tag script's: later-checks.ts): run once they have. */
|
|
47
|
+
later = new Set();
|
|
45
48
|
ids = new Map();
|
|
46
49
|
/** Where the answers of each saved form placed here go, by name: the path of the part that took it first. */
|
|
47
50
|
formNames = new Map();
|
|
@@ -50,11 +53,23 @@ class ReferenceCheck {
|
|
|
50
53
|
goingTo = [];
|
|
51
54
|
/** The names of the slots the layout places. */
|
|
52
55
|
slots = new Set();
|
|
53
|
-
|
|
56
|
+
/** The app's functions, as the page's expressions call them; or why the names given cannot be. */
|
|
57
|
+
table;
|
|
58
|
+
tableProblem;
|
|
59
|
+
constructor(page, line, options = {}) {
|
|
54
60
|
this.page = page;
|
|
55
61
|
this.line = line;
|
|
62
|
+
this.options = options;
|
|
63
|
+
try {
|
|
64
|
+
this.table = (0, app_functions_1.functionTable)(options.functions);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
this.tableProblem = error.message;
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
70
|
run() {
|
|
71
|
+
if (this.tableProblem)
|
|
72
|
+
this.report('(functions)', this.tableProblem);
|
|
58
73
|
if (this.page.timeZone !== undefined)
|
|
59
74
|
this.checkZone(this.page.timeZone, 'timeZone');
|
|
60
75
|
this.checkFields(this.page.fields, 'fields', this.line?.name, this.line?.parent);
|
|
@@ -66,9 +81,10 @@ class ReferenceCheck {
|
|
|
66
81
|
walkNode: (node, path) => this.walkNode(node, path),
|
|
67
82
|
checkModifiers: (owner, path, keys) => this.checkModifiers(owner, path, keys),
|
|
68
83
|
checkSteps: (steps, path) => this.checkSteps(steps, path),
|
|
69
|
-
checkPress: (button, path) => this.checkPress(button, path),
|
|
84
|
+
checkPress: (button, path) => this.checkPress(button, path, true),
|
|
70
85
|
isPart: (id) => this.ids.has(id),
|
|
71
86
|
isPlace: (id) => this.places.has(id),
|
|
87
|
+
owe: (part) => this.later.add(part),
|
|
72
88
|
});
|
|
73
89
|
if (this.page.on)
|
|
74
90
|
this.checkEvents(this.page.on, 'on');
|
|
@@ -78,6 +94,16 @@ class ReferenceCheck {
|
|
|
78
94
|
this.checkDuplicates(this.page);
|
|
79
95
|
if (this.page.records !== undefined && !this.line)
|
|
80
96
|
this.checkRecords(this.page.records);
|
|
97
|
+
if (this.page.endings !== undefined && !this.line) {
|
|
98
|
+
if (this.page.data.kind !== 'responses')
|
|
99
|
+
this.report('endings', 'endings are for a page of responses, shown once it is sent');
|
|
100
|
+
// Each ending: in the one-tag script, its checks come with the answers part, which a page with any asks for.
|
|
101
|
+
if (Array.isArray(this.page.endings) && this.page.endings.length)
|
|
102
|
+
(0, endings_checks_1.checkEndings)(this.page, { ...this.fieldTools(), checkModifiers: (owner, path, keys) => this.checkModifiers(owner, path, keys), checkWords: (owner, path, keys, sent) => this.checkWords(owner, path, keys, sent) });
|
|
103
|
+
}
|
|
104
|
+
// A page's limits and Continue later: checked where it has either (in the one-tag script, by the limits part it asks for).
|
|
105
|
+
if (!this.line && (this.page.limits !== undefined || this.page.continueLater !== undefined))
|
|
106
|
+
(0, limits_checks_1.checkLimits)(this.page, { report: (path, message) => this.report(path, message), owe: (part) => this.later.add(part) });
|
|
81
107
|
this.checkGoingTo();
|
|
82
108
|
return this.issues;
|
|
83
109
|
}
|
|
@@ -142,7 +168,7 @@ class ReferenceCheck {
|
|
|
142
168
|
if (!Array.isArray(children))
|
|
143
169
|
return this.report(`${path}.lineForm`, 'a line’s form holds its parts: { "children": [...] }');
|
|
144
170
|
const page = { ...this.page, fields: def.fields, layout: { type: 'sections', id: `${node.id}#line`, children }, on: undefined };
|
|
145
|
-
const inside = new ReferenceCheck(page, { name: node.field, parent: this.page.fields });
|
|
171
|
+
const inside = new ReferenceCheck(page, { name: node.field, parent: this.page.fields }, this.options);
|
|
146
172
|
inside.walkChildren(children, `${path}.lineForm`);
|
|
147
173
|
inside.checkGoingTo();
|
|
148
174
|
this.issues.push(...inside.issues);
|
|
@@ -164,8 +190,10 @@ class ReferenceCheck {
|
|
|
164
190
|
this.checkSteps(steps, `${path}.show.${target}`);
|
|
165
191
|
}
|
|
166
192
|
}
|
|
167
|
-
/** A press: steps, an app action, or both — and its steps checked. */
|
|
168
|
-
checkPress(button, path) {
|
|
193
|
+
/** A press: steps, an app action, or both — and its steps checked; on a button drawn on the record (`drawn`, not a list's row's), the `{field}`s its words show. */
|
|
194
|
+
checkPress(button, path, drawn = false) {
|
|
195
|
+
if (drawn && 'type' in button)
|
|
196
|
+
this.checkWords(button, path, ['label', 'pressedLabel']);
|
|
169
197
|
// A button with a menu of its own and nothing else is a menu button.
|
|
170
198
|
const menu = 'menu' in button && !!button.menu?.length;
|
|
171
199
|
if (!button.steps && button.action === undefined && !menu)
|
|
@@ -232,7 +260,7 @@ class ReferenceCheck {
|
|
|
232
260
|
for (const [name, value] of Object.entries(step.into ?? {})) {
|
|
233
261
|
this.need(name, `${at}.into.${name}`);
|
|
234
262
|
try {
|
|
235
|
-
(0, expression_1.compileExpression)(value);
|
|
263
|
+
(0, expression_1.compileExpression)(value, this.table);
|
|
236
264
|
}
|
|
237
265
|
catch (error) {
|
|
238
266
|
this.report(`${at}.into.${name}`, `cannot read "${value}": ${error.message}`);
|
|
@@ -249,10 +277,22 @@ class ReferenceCheck {
|
|
|
249
277
|
}
|
|
250
278
|
});
|
|
251
279
|
}
|
|
252
|
-
/**
|
|
253
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Words that show fields' values: each `{name}` a field of this page — in an ending's (`sent`), or `id`, the id the
|
|
282
|
+
* app's source gave the response.
|
|
283
|
+
*/
|
|
284
|
+
checkValuesIn(words, path, sent = false) {
|
|
254
285
|
for (const name of (0, words_1.valuesIn)(words))
|
|
255
|
-
this.
|
|
286
|
+
if (!(sent && name === 'id' && !has(this.page.fields, 'id')))
|
|
287
|
+
this.need(name, path);
|
|
288
|
+
}
|
|
289
|
+
/** The words of a part that may show the answers given (`keys` of `owner`): each `{name}` a field of the page. */
|
|
290
|
+
checkWords(owner, path, keys, sent = false) {
|
|
291
|
+
for (const key of keys) {
|
|
292
|
+
const words = owner[key];
|
|
293
|
+
if (typeof words === 'string')
|
|
294
|
+
this.checkValuesIn(words, `${path}.${key}`, sent);
|
|
295
|
+
}
|
|
256
296
|
}
|
|
257
297
|
report(path, message) {
|
|
258
298
|
this.issues.push({ path, message });
|
|
@@ -310,13 +350,14 @@ class ReferenceCheck {
|
|
|
310
350
|
continue;
|
|
311
351
|
let compiled;
|
|
312
352
|
try {
|
|
313
|
-
compiled = (0, modifier_1.compileModifier)(value);
|
|
353
|
+
compiled = (0, modifier_1.compileModifier)(value, this.table);
|
|
314
354
|
}
|
|
315
355
|
catch (error) {
|
|
316
356
|
this.report(`${path}.${key}`, `cannot read "${value}": ${error.message}`);
|
|
317
357
|
continue;
|
|
318
358
|
}
|
|
319
359
|
this.checkReads(value, compiled, `${path}.${key}`, scope);
|
|
360
|
+
this.checkKinds(value, `${path}.${key}`, scope);
|
|
320
361
|
}
|
|
321
362
|
}
|
|
322
363
|
/**
|
|
@@ -332,7 +373,7 @@ class ReferenceCheck {
|
|
|
332
373
|
continue;
|
|
333
374
|
let fields;
|
|
334
375
|
try {
|
|
335
|
-
fields = (0, modifier_1.compileModifier)(where.condition).fields;
|
|
376
|
+
fields = (0, modifier_1.compileModifier)(where.condition, this.table).fields;
|
|
336
377
|
}
|
|
337
378
|
catch (error) {
|
|
338
379
|
this.report(path, `cannot read the condition "${where.condition}": ${error.message}`);
|
|
@@ -363,6 +404,14 @@ class ReferenceCheck {
|
|
|
363
404
|
this.report(path, `"${source}" reads "${name}", which is not a field ${where}`);
|
|
364
405
|
}
|
|
365
406
|
}
|
|
407
|
+
/** The kinds of value an expression hands its functions, where the check reads them (`CheckOptions.kinds`). */
|
|
408
|
+
checkKinds(source, path, scope) {
|
|
409
|
+
if (!this.options.kinds)
|
|
410
|
+
return;
|
|
411
|
+
const parent = scope.lines === undefined ? undefined : (scope.parent ?? this.page.fields);
|
|
412
|
+
for (const words of this.options.kinds(source, { fields: scope.fields, ...(parent ? { parent } : {}) }))
|
|
413
|
+
this.warnings.push({ path, message: `"${source}": ${words}` });
|
|
414
|
+
}
|
|
366
415
|
/**
|
|
367
416
|
* An expression for a value must read, read only fields where it is worked
|
|
368
417
|
* out, and add up only line fields that exist. Returns the fields it reads,
|
|
@@ -371,13 +420,14 @@ class ReferenceCheck {
|
|
|
371
420
|
checkExpression(source, path, scope) {
|
|
372
421
|
let compiled;
|
|
373
422
|
try {
|
|
374
|
-
compiled = (0, expression_1.compileExpression)(source);
|
|
423
|
+
compiled = (0, expression_1.compileExpression)(source, this.table);
|
|
375
424
|
}
|
|
376
425
|
catch (error) {
|
|
377
426
|
this.report(path, `cannot read "${source}": ${error.message}`);
|
|
378
427
|
return null;
|
|
379
428
|
}
|
|
380
429
|
this.checkReads(source, compiled, path, scope);
|
|
430
|
+
this.checkKinds(source, path, scope);
|
|
381
431
|
for (const sum of compiled.sums) {
|
|
382
432
|
if (!has(scope.fields, sum.lines))
|
|
383
433
|
continue;
|
|
@@ -421,11 +471,30 @@ class ReferenceCheck {
|
|
|
421
471
|
this.report(`${base}.${cycle[0]}.compute`, message);
|
|
422
472
|
}
|
|
423
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* A choice's options, a page's own and a table line's alike: each condition on one reads the record — a line's, its
|
|
476
|
+
* line and `parent.` (`options[].invisible`); its list's `dependsOn` names fields (`checkDependsOn`); an earlier
|
|
477
|
+
* question it carries them from (`optionsFrom.field`) is another choice of the page, never one carrying its own from
|
|
478
|
+
* this one — and never for a line, whose choices are its own or one of the app's lists.
|
|
479
|
+
*/
|
|
480
|
+
checkChoices(name, def, path, fields, scope) {
|
|
481
|
+
(0, choice_options_1.optionsList)(def.optionsFrom)?.dependsOn?.forEach((other, i) => this.checkDependsOn(name, other, `${path}.optionsFrom.dependsOn[${i}]`, fields, scope.lines, scope.parent));
|
|
482
|
+
// Options by condition, or carried from an earlier question: in the one-tag script, checked by the options part a
|
|
483
|
+
// page with any asks for.
|
|
484
|
+
if ((0, choice_options_1.changesOptions)(def))
|
|
485
|
+
(0, option_checks_1.checkOptionRules)(name, def, path, fields, scope, { ...this.fieldTools(), checkModifiers: (owner, at, keys, inside) => this.checkModifiers(owner, at, keys, inside) });
|
|
486
|
+
}
|
|
424
487
|
checkFields(fields, base, lines, parent) {
|
|
425
488
|
const scope = { fields, lines, ...(parent ? { parent } : {}) };
|
|
426
489
|
this.checkWorkedOut(fields, base, scope);
|
|
427
490
|
for (const [name, def] of Object.entries(fields)) {
|
|
428
491
|
const path = `${base}.${name}`;
|
|
492
|
+
// A field's label, its help and its choices' words may show the answers given — the page's own fields', not a line's.
|
|
493
|
+
if (lines === undefined) {
|
|
494
|
+
this.checkWords(def, path, ['label', 'help']);
|
|
495
|
+
if (def.type === 'selection' && Array.isArray(def.options))
|
|
496
|
+
def.options.forEach((option, i) => this.checkWords(option ?? {}, `${path}.options[${i}]`, ['label']));
|
|
497
|
+
}
|
|
429
498
|
if (def.type === 'monetary' && def.currencyField !== undefined)
|
|
430
499
|
this.checkCurrency(def.currencyField, `${path}.currencyField`, fields, lines, parent);
|
|
431
500
|
// What an amount is for, by a condition on the record it is on: "/month" on a rent.
|
|
@@ -465,7 +534,8 @@ class ReferenceCheck {
|
|
|
465
534
|
this.checkZone(linked.timeZone, `${path}.fields.${column}.timeZone`);
|
|
466
535
|
}
|
|
467
536
|
if (def.type === 'selection')
|
|
468
|
-
|
|
537
|
+
this.checkChoices(name, def, path, fields, scope);
|
|
538
|
+
(0, seats_checks_1.checkSeats)(def, path, lines ?? this.line?.name, (at, message) => this.report(at, message));
|
|
469
539
|
if (def.type === 'properties')
|
|
470
540
|
def.definitionsFrom?.dependsOn?.forEach((other, i) => this.need(other, `${path}.definitionsFrom.dependsOn[${i}]`, fields));
|
|
471
541
|
if (def.type === 'properties' && def.definitions) {
|
|
@@ -483,7 +553,7 @@ class ReferenceCheck {
|
|
|
483
553
|
// Its lines read the record they are on — on a line, that line — as parent.
|
|
484
554
|
this.checkFields(def.fields, `${path}.fields`, name, fields);
|
|
485
555
|
if ('lineKinds' in def && def.lineKinds)
|
|
486
|
-
|
|
556
|
+
(0, table_checks_1.checkLineKinds)(name, def.lineKinds, def.fields, `${path}.lineKinds`, this.fieldTools());
|
|
487
557
|
if (def.sequenceField !== undefined) {
|
|
488
558
|
const sequence = def.fields[def.sequenceField];
|
|
489
559
|
if (!sequence)
|
|
@@ -495,6 +565,21 @@ class ReferenceCheck {
|
|
|
495
565
|
}
|
|
496
566
|
}
|
|
497
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* A field a choice's list changes with: one beside it, or on a line `parent.` and a field of the record it is on. A
|
|
570
|
+
* line's list never changes with its own column: choosing would ask for its choices again.
|
|
571
|
+
*/
|
|
572
|
+
checkDependsOn(own, name, path, fields, lines, parent = this.page.fields) {
|
|
573
|
+
if (!name.startsWith('parent.')) {
|
|
574
|
+
if (lines !== undefined && name === own)
|
|
575
|
+
return this.report(path, `"${own}" cannot change with itself`);
|
|
576
|
+
return void this.need(name, path, fields);
|
|
577
|
+
}
|
|
578
|
+
if (lines === undefined)
|
|
579
|
+
return this.report(path, `"${name}": only a line’s choices read parent, the record it is on`);
|
|
580
|
+
if (!has(parent, name.slice('parent.'.length)))
|
|
581
|
+
this.report(path, `"${name}": "${name.slice('parent.'.length)}" is not a field of this page`);
|
|
582
|
+
}
|
|
498
583
|
/** A money field's currency: a field beside it, or on a line `parent.` and a field of the record — one that holds a currency. */
|
|
499
584
|
checkCurrency(name, path, fields, lines, parent = this.page.fields) {
|
|
500
585
|
let currency;
|
|
@@ -510,33 +595,55 @@ class ReferenceCheck {
|
|
|
510
595
|
this.report(path, `"${name}" is a ${currency.type}; a currency field must be a many2one, selection or char`);
|
|
511
596
|
}
|
|
512
597
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
this.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
598
|
+
/** The tools a table of lines' checks are handed (table-checks.ts). */
|
|
599
|
+
tableTools() {
|
|
600
|
+
return {
|
|
601
|
+
...this.fieldTools(),
|
|
602
|
+
claim: (id, path) => this.claim(id, path),
|
|
603
|
+
checkModifiers: (owner, path, keys, scope) => this.checkModifiers(owner, path, keys, scope),
|
|
604
|
+
checkPress: (button, path) => this.checkPress(button, path),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
/** The tools a part's checks of a field are handed (part-checks.ts). */
|
|
608
|
+
fieldTools() {
|
|
609
|
+
return { page: this.page, need: (name, path, scope) => this.need(name, path, scope), report: (path, message) => this.report(path, message), owe: (part) => this.later.add(part) };
|
|
610
|
+
}
|
|
611
|
+
/** The tools a map's checks are handed (map-checks.ts). */
|
|
612
|
+
mapTools() {
|
|
613
|
+
return {
|
|
614
|
+
page: this.page,
|
|
615
|
+
need: (name, path, scope) => this.need(name, path, scope),
|
|
616
|
+
report: (path, message) => this.report(path, message),
|
|
617
|
+
checkModifiers: (owner, path, keys, scope) => this.checkModifiers(owner, path, keys, scope),
|
|
618
|
+
checkSteps: (steps, path) => this.checkSteps(steps, path),
|
|
619
|
+
here: () => this.here(),
|
|
620
|
+
owe: (part) => this.later.add(part),
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/** The tools a list page's own checks are handed (list-checks.ts). */
|
|
624
|
+
listTools() {
|
|
625
|
+
return {
|
|
626
|
+
page: this.page,
|
|
627
|
+
need: (name, path, scope) => this.need(name, path, scope),
|
|
628
|
+
report: (path, message) => this.report(path, message),
|
|
629
|
+
claim: (id, path) => this.claim(id, path),
|
|
630
|
+
checkModifiers: (owner, path, keys, scope) => this.checkModifiers(owner, path, keys, scope),
|
|
631
|
+
checkPress: (button, path) => this.checkPress(button, path),
|
|
632
|
+
checkPlace: (at, path, scope, pin, card) => (0, map_checks_1.checkPlace)(at, path, scope, pin, card, this.mapTools()),
|
|
633
|
+
here: () => this.here(),
|
|
634
|
+
onLine: !!this.line,
|
|
635
|
+
checkCurrency: (name, path, fields, lines, parent) => this.checkCurrency(name, path, fields, lines, parent),
|
|
636
|
+
checkZone: (zone, path) => this.checkZone(zone, path),
|
|
637
|
+
checkFilterItem: (item, at, scope) => this.checkFilterItem(item, at, scope),
|
|
638
|
+
owe: (part) => this.later.add(part),
|
|
639
|
+
};
|
|
533
640
|
}
|
|
534
641
|
/** A page's own buttons at its foot: buttons as any. */
|
|
535
642
|
walkFooter(footer, path) {
|
|
536
643
|
footer?.forEach((button, i) => {
|
|
537
644
|
this.claim(button.id, `${path}.footer[${i}]`);
|
|
538
645
|
this.checkModifiers(button, `${path}.footer[${i}]`);
|
|
539
|
-
this.checkPress(button, `${path}.footer[${i}]
|
|
646
|
+
this.checkPress(button, `${path}.footer[${i}]`, true);
|
|
540
647
|
});
|
|
541
648
|
}
|
|
542
649
|
walkRoot(root, path) {
|
|
@@ -550,405 +657,18 @@ class ReferenceCheck {
|
|
|
550
657
|
if (root.type === 'sections')
|
|
551
658
|
return this.walkChildren(root.children, path);
|
|
552
659
|
if (root.type === 'list')
|
|
553
|
-
return
|
|
660
|
+
return (0, list_checks_1.checkList)(root, path, this.listTools());
|
|
661
|
+
// A wizard's own buttons' words, and each step's title and words, may show the answers given: "Your visit to {branch}".
|
|
662
|
+
this.checkWords(root, path, ['nextLabel', 'backLabel', 'finishLabel']);
|
|
554
663
|
root.children.forEach((step, i) => {
|
|
555
664
|
const stepPath = `${path}.children[${i}]`;
|
|
556
665
|
this.places.add(step.id);
|
|
557
666
|
this.claim(step.id, stepPath);
|
|
558
667
|
this.checkModifiers(step, stepPath);
|
|
668
|
+
this.checkWords(step, stepPath, ['label', 'description']);
|
|
559
669
|
this.walkChildren(step.children, stepPath);
|
|
560
670
|
});
|
|
561
671
|
}
|
|
562
|
-
walkList(list, path) {
|
|
563
|
-
if (this.page.data.kind !== 'record')
|
|
564
|
-
this.report('data', 'a list shows records: its data needs kind "record" and a model');
|
|
565
|
-
this.checkListed(list, this.page.fields, path);
|
|
566
|
-
list.groupBy?.forEach((name, i) => this.need(name, `${path}.groupBy[${i}]`));
|
|
567
|
-
// A list's filters are about its own records: their fields are the page's, and there is no record to take a value from.
|
|
568
|
-
const walk = (item, at) => {
|
|
569
|
-
if ('any' in item)
|
|
570
|
-
return item.any.forEach((inner, i) => walk(inner, `${at}.any[${i}]`));
|
|
571
|
-
if ('all' in item)
|
|
572
|
-
return item.all.forEach((inner, i) => walk(inner, `${at}.all[${i}]`));
|
|
573
|
-
this.need(item.field, `${at}.field`);
|
|
574
|
-
if (item.valueFrom !== undefined)
|
|
575
|
-
this.report(`${at}.valueFrom`, "a list's filter compares with values, not with another field");
|
|
576
|
-
};
|
|
577
|
-
const named = new Set();
|
|
578
|
-
list.filters?.forEach((filter, i) => {
|
|
579
|
-
this.claim(filter.id, `${path}.filters[${i}]`);
|
|
580
|
-
named.add(filter.id);
|
|
581
|
-
filter.filter?.forEach((item, j) => walk(item, `${path}.filters[${i}].filter[${j}]`));
|
|
582
|
-
// A period is of a date, or a date and time.
|
|
583
|
-
const dated = filter.period ? this.need(filter.period.field, `${path}.filters[${i}].period.field`) : undefined;
|
|
584
|
-
if (dated && dated.type !== 'date' && dated.type !== 'datetime')
|
|
585
|
-
this.report(`${path}.filters[${i}].period.field`, `"${filter.period?.field}" is a ${dated.type}; a period is of a date, or a date and time`);
|
|
586
|
-
});
|
|
587
|
-
list.defaultFilters?.forEach((id, i) => {
|
|
588
|
-
if (!named.has(id))
|
|
589
|
-
this.report(`${path}.defaultFilters[${i}]`, `no filter "${id}" in this list`);
|
|
590
|
-
});
|
|
591
|
-
list.actions?.forEach((button, i) => {
|
|
592
|
-
this.claim(button.id, `${path}.actions[${i}]`);
|
|
593
|
-
this.checkPress(button, `${path}.actions[${i}]`);
|
|
594
|
-
});
|
|
595
|
-
if (list.map)
|
|
596
|
-
this.walkListMap(list.map, `${path}.map`);
|
|
597
|
-
if (list.calendar)
|
|
598
|
-
this.walkListCalendar(list, `${path}.calendar`);
|
|
599
|
-
this.checkListCells(list, path);
|
|
600
|
-
this.checkListRows(list, path);
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* A list's cards, gallery, columns people choose and rows' own rules: a
|
|
604
|
-
* card's fields the page's, its picture one that can hold a picture; the
|
|
605
|
-
* optional columns among the columns; a row's tones, bold and buttons
|
|
606
|
-
* reading the list's record (its fields, `id`, `user`); and the list
|
|
607
|
-
* opening on one thing.
|
|
608
|
-
*/
|
|
609
|
-
checkListRows(list, path) {
|
|
610
|
-
if (list.card)
|
|
611
|
-
this.checkListCard(list.card, `${path}.card`, this.page.fields);
|
|
612
|
-
for (const column of Object.keys(list.optionalColumns ?? {})) {
|
|
613
|
-
if (!list.columns.includes(column))
|
|
614
|
-
this.report(`${path}.optionalColumns.${column}`, `"${column}" is no column of this list: a person shows or hides its columns`);
|
|
615
|
-
}
|
|
616
|
-
// A column a person may hide, every one hidden at first: the list would open with no column but its own.
|
|
617
|
-
if (list.optionalColumns && list.columns.every((column) => list.optionalColumns?.[column] === 'hide'))
|
|
618
|
-
this.report(`${path}.optionalColumns`, 'every column starts hidden: show one at least, as the list opens');
|
|
619
|
-
if (list.gallery?.opens === 'gallery') {
|
|
620
|
-
if (list.map?.view && list.map.view !== 'list')
|
|
621
|
-
this.report(`${path}.gallery.opens`, 'the list opens on its map (map.view) and on its gallery: say one');
|
|
622
|
-
if (list.calendar?.opens === 'calendar')
|
|
623
|
-
this.report(`${path}.gallery.opens`, 'the list opens on its calendar and on its gallery: say one');
|
|
624
|
-
}
|
|
625
|
-
list.rowTones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.rowTones[${i}]`, ['when']));
|
|
626
|
-
this.checkModifiers(list, path, ['rowBold']);
|
|
627
|
-
list.rowButtons?.forEach((button, i) => {
|
|
628
|
-
this.claim(button.id, `${path}.rowButtons[${i}]`);
|
|
629
|
-
this.checkPress(button, `${path}.rowButtons[${i}]`);
|
|
630
|
-
// Shown by a condition on each row's record.
|
|
631
|
-
this.checkModifiers(button, `${path}.rowButtons[${i}]`, ['invisible']);
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* What a list's card shows — a list page's, or a related list's of its own records' fields: its title, subtitle,
|
|
636
|
-
* status and lines each a field, its picture one that can hold a picture.
|
|
637
|
-
*/
|
|
638
|
-
checkListCard(card, at, fields) {
|
|
639
|
-
for (const key of ['title', 'subtitle', 'status'])
|
|
640
|
-
if (card[key] !== undefined)
|
|
641
|
-
this.need(card[key], `${at}.${key}`, fields);
|
|
642
|
-
if (card.image !== undefined) {
|
|
643
|
-
const def = this.need(card.image, `${at}.image`, fields);
|
|
644
|
-
if (def && !PICTURES.has(def.type))
|
|
645
|
-
this.report(`${at}.image`, `"${card.image}" is a ${def.type}; a card's picture is an image or binary field, or text holding a picture's address`);
|
|
646
|
-
}
|
|
647
|
-
card.fields?.forEach((line, i) => (Array.isArray(line) ? line : [line]).forEach((part, j) => this.need(typeof part === 'string' ? part : part.field, `${at}.fields[${i}]${Array.isArray(line) ? `[${j}]` : ''}${typeof part === 'string' ? '' : '.field'}`, fields)));
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* A list's calendar: when each record starts (a date, or a date and time)
|
|
651
|
-
* and ends (the same kind), its words, its colour and its columns, each
|
|
652
|
-
* from a field that can; the view it opens on is one it offers, its hours
|
|
653
|
-
* run forward, and the list opens on one thing.
|
|
654
|
-
*/
|
|
655
|
-
walkListCalendar(list, path) {
|
|
656
|
-
const calendar = list.calendar;
|
|
657
|
-
const typed = (name, key, ok, says) => {
|
|
658
|
-
if (name === undefined)
|
|
659
|
-
return undefined;
|
|
660
|
-
const def = this.need(name, `${path}.${key}`);
|
|
661
|
-
if (def && !ok.has(def.type))
|
|
662
|
-
this.report(`${path}.${key}`, `"${name}" is a ${def.type}; ${says}`);
|
|
663
|
-
return def;
|
|
664
|
-
};
|
|
665
|
-
const start = typed(calendar.start, 'start', CALENDAR_TIMES, "a calendar's start is a date, or a date and time");
|
|
666
|
-
const end = typed(calendar.end, 'end', CALENDAR_TIMES, "a calendar's end is a date, or a date and time");
|
|
667
|
-
if (start && end && CALENDAR_TIMES.has(start.type) && CALENDAR_TIMES.has(end.type) && start.type !== end.type) {
|
|
668
|
-
this.report(`${path}.end`, `"${calendar.end}" is a ${end.type}; an end is the same kind of field as its start, a ${start.type}`);
|
|
669
|
-
}
|
|
670
|
-
typed(calendar.title, 'title', CALENDAR_WORDS, "a record's words on a calendar are a field that can be said");
|
|
671
|
-
typed(calendar.colour, 'colour', CALENDAR_COLOURS, 'a colour comes from a link, a choice, text, a whole number or a yes or no');
|
|
672
|
-
typed(calendar.resource, 'resource', CALENDAR_COLUMNS, 'a column for each comes from a link, a choice, text or a whole number');
|
|
673
|
-
const views = calendar.views ?? ['day', 'week', 'month', 'agenda'];
|
|
674
|
-
views.forEach((view, i) => {
|
|
675
|
-
if (views.indexOf(view) !== i)
|
|
676
|
-
this.report(`${path}.views[${i}]`, `"${view}" is offered twice`);
|
|
677
|
-
});
|
|
678
|
-
if (calendar.view && !views.includes(calendar.view))
|
|
679
|
-
this.report(`${path}.view`, `"${calendar.view}" is not one of its views (${views.join(', ')})`);
|
|
680
|
-
if (calendar.hours && calendar.hours.end <= calendar.hours.start)
|
|
681
|
-
this.report(`${path}.hours`, `the hours end at ${calendar.hours.end}, before they start at ${calendar.hours.start}`);
|
|
682
|
-
if (calendar.opens === 'calendar' && list.map?.view && list.map.view !== 'list')
|
|
683
|
-
this.report(`${path}.opens`, 'the list opens on its map (map.view) and on its calendar: say one');
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* A list drawn as a backlog: each column's cells are a column's, a lead's
|
|
687
|
-
* icon is by a field's values; the filter bar's fields can list their
|
|
688
|
-
* values; rows nest under a link to the list's own records; and buttons for
|
|
689
|
-
* the rows chosen need a box to choose one.
|
|
690
|
-
*/
|
|
691
|
-
checkListCells(list, path) {
|
|
692
|
-
if (list.filterFields && list.search !== 'fields')
|
|
693
|
-
this.report(`${path}.filterFields`, 'the filter fields are for the "fields" search: add "search": "fields"');
|
|
694
|
-
list.filterFields?.forEach((name, i) => {
|
|
695
|
-
const def = this.need(name, `${path}.filterFields[${i}]`);
|
|
696
|
-
if (def && !LISTS_ITS_VALUES.has(def.type))
|
|
697
|
-
this.report(`${path}.filterFields[${i}]`, `"${name}" is a ${def.type}; a filter lists the values of a choice, a link, links, a yes or no, text or a whole number`);
|
|
698
|
-
});
|
|
699
|
-
// A filter's own name is for a filter the bar offers.
|
|
700
|
-
for (const name of Object.keys(list.filterLabels ?? {})) {
|
|
701
|
-
if (!list.filterFields?.includes(name))
|
|
702
|
-
this.report(`${path}.filterLabels.${name}`, `"${name}" is no field of filterFields: its name is for a filter the bar offers`);
|
|
703
|
-
}
|
|
704
|
-
if (list.parentField !== undefined) {
|
|
705
|
-
const def = this.need(list.parentField, `${path}.parentField`);
|
|
706
|
-
const model = this.page.data.kind === 'record' ? this.page.data.model : '';
|
|
707
|
-
if (def && (def.type !== 'many2one' || def.relation !== model)) {
|
|
708
|
-
this.report(`${path}.parentField`, `"${list.parentField}" ${def.type === 'many2one' ? `links to ${def.relation}` : `is a ${def.type}`}; a parent is a many2one to the list’s own model, ${model}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
if (list.choose === false && list.actions?.length)
|
|
712
|
-
this.report(`${path}.actions`, 'a list’s buttons act on the rows chosen: with "choose": false there is no way to choose one');
|
|
713
|
-
this.checkBulkEdit(list, path);
|
|
714
|
-
this.checkImport(list, path);
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* What a list shows of its records — a list page's, or a related list's of its own records' fields: its columns, its
|
|
718
|
-
* order and the fields its search looks in, each a field; each column's cells for a column of it, a sum, an average,
|
|
719
|
-
* the lowest and the highest of a number column (any column's count), a lead's icon by a field's values.
|
|
720
|
-
*/
|
|
721
|
-
checkListed(list, fields, path) {
|
|
722
|
-
list.columns.forEach((name, i) => this.need(name, `${path}.columns[${i}]`, fields));
|
|
723
|
-
list.sort?.forEach((order, i) => this.need(order.field, `${path}.sort[${i}].field`, fields));
|
|
724
|
-
list.searchFields?.forEach((name, i) => this.need(name, `${path}.searchFields[${i}]`, fields));
|
|
725
|
-
for (const [column, cell] of Object.entries(list.cells ?? {})) {
|
|
726
|
-
const at = `${path}.cells.${column}`;
|
|
727
|
-
if (!list.columns.includes(column)) {
|
|
728
|
-
this.report(at, `"${column}" is no column of this list: its cells are for its columns`);
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
const kind = has(fields, column) ? fields[column].type : undefined;
|
|
732
|
-
if (cell.total && cell.total !== 'count' && kind && kind !== 'integer' && kind !== 'float' && kind !== 'monetary') {
|
|
733
|
-
this.report(`${at}.total`, `"${column}" is a ${kind}; its total "${cell.total}" is of numbers — an integer, a float or an amount — and any column's is "count"`);
|
|
734
|
-
}
|
|
735
|
-
if (!cell.lead)
|
|
736
|
-
continue;
|
|
737
|
-
const def = this.need(cell.lead.field, `${at}.lead.field`, fields);
|
|
738
|
-
if (def?.type !== 'selection')
|
|
739
|
-
continue;
|
|
740
|
-
const values = new Set(def.options.map((option) => String(option.value)));
|
|
741
|
-
for (const value of Object.keys(cell.lead.looks))
|
|
742
|
-
if (!values.has(value))
|
|
743
|
-
this.report(`${at}.lead.looks.${value}`, `"${value}" is no value of "${cell.lead.field}"`);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
/** A list's Edit: each field it names one value can set in many records, and rows to choose to set it on. */
|
|
747
|
-
checkBulkEdit(list, path) {
|
|
748
|
-
if (!list.bulkEdit)
|
|
749
|
-
return;
|
|
750
|
-
if (list.choose === false)
|
|
751
|
-
this.report(`${path}.bulkEdit`, 'Edit changes the rows chosen: with "choose": false there is no way to choose one');
|
|
752
|
-
if (list.bulkEdit === true) {
|
|
753
|
-
if (!list.columns.some((name) => (0, bulk_edit_1.bulkSettable)(this.page.fields[name])))
|
|
754
|
-
this.report(`${path}.bulkEdit`, 'Edit offers the columns one value can set, and none of these can: name the fields it offers');
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
const named = list.bulkEdit;
|
|
758
|
-
named.forEach((name, i) => {
|
|
759
|
-
const at = `${path}.bulkEdit[${i}]`;
|
|
760
|
-
if (named.indexOf(name) !== i)
|
|
761
|
-
this.report(at, `"${name}" is offered twice`);
|
|
762
|
-
const def = this.need(name, at);
|
|
763
|
-
if (!def)
|
|
764
|
-
return;
|
|
765
|
-
if (!bulk_edit_1.BULK_KINDS.has(def.type))
|
|
766
|
-
this.report(at, `"${name}" is a ${def.type}; one value set in many records is words, a number, money, a yes or no, a date, a choice or a link`);
|
|
767
|
-
else if (def.readonly === true || def.compute)
|
|
768
|
-
this.report(at, `"${name}" is ${def.compute ? 'worked out from other fields' : 'read-only'}; Edit sets a field people can change`);
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
/** A list's Import: each field it names one a file's cell can fill, and at least one such field on the page. */
|
|
772
|
-
checkImport(list, path) {
|
|
773
|
-
if (!list.import)
|
|
774
|
-
return;
|
|
775
|
-
if (list.import === true) {
|
|
776
|
-
if (!Object.values(this.page.fields).some((def) => (0, import_fields_1.importable)(def)))
|
|
777
|
-
this.report(`${path}.import`, 'Import fills the fields a file’s cell can, and this page has none: words, a number, money, yes or no, a date, a choice or a link people can change');
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
const named = list.import;
|
|
781
|
-
named.forEach((name, i) => {
|
|
782
|
-
const at = `${path}.import[${i}]`;
|
|
783
|
-
if (named.indexOf(name) !== i)
|
|
784
|
-
this.report(at, `"${name}" is offered twice`);
|
|
785
|
-
const def = this.need(name, at);
|
|
786
|
-
if (!def)
|
|
787
|
-
return;
|
|
788
|
-
if (!import_fields_1.IMPORT_KINDS.has(def.type))
|
|
789
|
-
this.report(at, `"${name}" is a ${def.type}; a file's cell fills words, a number, money, a yes or no, a date, a choice, or a link or links by name`);
|
|
790
|
-
else if (def.readonly === true || def.compute)
|
|
791
|
-
this.report(at, `"${name}" is ${def.compute ? 'worked out from other fields' : 'read-only'}; Import fills a field people can change`);
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
/** A list's map: where each record is, and the fields its pin and its card show, each of a type that can. */
|
|
795
|
-
walkListMap(map, path) {
|
|
796
|
-
const pair = map.latitude !== undefined || map.longitude !== undefined;
|
|
797
|
-
if (map.point !== undefined && pair)
|
|
798
|
-
this.report(path, 'a map takes point, or latitude and longitude, not both');
|
|
799
|
-
else if (map.point === undefined && (map.latitude === undefined || map.longitude === undefined))
|
|
800
|
-
this.report(path, 'a map needs where each record is: point, or latitude and longitude');
|
|
801
|
-
this.checkPlace(map, path, this.here(), map.pin, map.card);
|
|
802
|
-
if (map.regions)
|
|
803
|
-
this.need(map.regions.field, `${path}.regions.field`);
|
|
804
|
-
if (typeof map.heat === 'object' && map.heat.weight !== undefined) {
|
|
805
|
-
const def = this.need(map.heat.weight, `${path}.heat.weight`);
|
|
806
|
-
if (def && def.type !== 'float' && def.type !== 'integer' && def.type !== 'monetary')
|
|
807
|
-
this.report(`${path}.heat.weight`, `"${map.heat.weight}" is a ${def.type}; a weight is a number field`);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Where a place is kept, and the fields its pin and its card show, each of a
|
|
812
|
-
* type that can: a page's fields, or a line's when the place is a line's.
|
|
813
|
-
*/
|
|
814
|
-
checkPlace(at, path, scope, pin, card) {
|
|
815
|
-
const typed = (name, where, ok, says) => {
|
|
816
|
-
if (name === undefined)
|
|
817
|
-
return;
|
|
818
|
-
const def = this.need(name, where, scope.fields);
|
|
819
|
-
if (def && !ok(def.type))
|
|
820
|
-
this.report(where, `"${name}" is a ${def.type}; ${says}`);
|
|
821
|
-
};
|
|
822
|
-
const number = (type) => type === 'float' || type === 'integer';
|
|
823
|
-
const picture = (type) => type === 'image' || type === 'binary';
|
|
824
|
-
typed(at.point, `${path}.point`, (type) => type === 'json', 'a point is kept in a json field');
|
|
825
|
-
typed(at.latitude, `${path}.latitude`, number, 'a latitude is a number field (float or integer)');
|
|
826
|
-
typed(at.longitude, `${path}.longitude`, number, 'a longitude is a number field (float or integer)');
|
|
827
|
-
if (pin) {
|
|
828
|
-
typed(pin.image, `${path}.pin.image`, picture, "a pin's photo is an image or binary field");
|
|
829
|
-
typed(pin.turn, `${path}.pin.turn`, number, 'a turn is a number field, in degrees');
|
|
830
|
-
if (pin.iconField !== undefined)
|
|
831
|
-
this.need(pin.iconField, `${path}.pin.iconField`, scope.fields);
|
|
832
|
-
if (pin.label !== undefined)
|
|
833
|
-
this.need(pin.label, `${path}.pin.label`, scope.fields);
|
|
834
|
-
pin.tones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.pin.tones[${i}]`, ['when'], scope));
|
|
835
|
-
pin.money?.per?.forEach((item, i) => this.checkModifiers(item, `${path}.pin.money.per[${i}]`, ['when'], scope));
|
|
836
|
-
}
|
|
837
|
-
if (card) {
|
|
838
|
-
if (card.title !== undefined)
|
|
839
|
-
this.need(card.title, `${path}.card.title`, scope.fields);
|
|
840
|
-
typed(card.image, `${path}.card.image`, picture, "a card's picture is an image or binary field");
|
|
841
|
-
// A line is a part, or several on one line; a part a field's name, or a field with its words.
|
|
842
|
-
card.fields?.forEach((line, i) => (Array.isArray(line) ? line : [line]).forEach((part, j) => {
|
|
843
|
-
const at = `${path}.card.fields[${i}]${Array.isArray(line) ? `[${j}]` : ''}${typeof part === 'string' ? '' : '.field'}`;
|
|
844
|
-
this.need(typeof part === 'string' ? part : part.field, at, scope.fields);
|
|
845
|
-
}));
|
|
846
|
-
typed(card.href, `${path}.card.href`, (type) => type === 'char' || type === 'text', "a card's link is a text field holding a web address");
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* A form's map: each layer one kind — the record's place, a place for each
|
|
851
|
-
* of its lines, a route between layers, or the reader's own place — its
|
|
852
|
-
* fields where that kind keeps them, and a route's ends layers it can join.
|
|
853
|
-
*/
|
|
854
|
-
walkMap(node, path) {
|
|
855
|
-
const layers = new Map();
|
|
856
|
-
node.layers.forEach((layer, i) => {
|
|
857
|
-
const at = `${path}.layers[${i}]`;
|
|
858
|
-
if (layers.has(layer.id))
|
|
859
|
-
this.report(`${at}.id`, `two layers are called "${layer.id}"`);
|
|
860
|
-
const pair = layer.latitude !== undefined || layer.longitude !== undefined;
|
|
861
|
-
const placed = layer.point !== undefined || pair;
|
|
862
|
-
const kinds = [placed, !!layer.route, !!layer.myLocation, !!layer.regions, !!layer.reachable].filter(Boolean).length;
|
|
863
|
-
if (kinds !== 1)
|
|
864
|
-
this.report(at, 'a layer is one of: a place (point, or latitude and longitude, for the record or for each of its lines), a route, myLocation, regions, or reachable');
|
|
865
|
-
if (layer.regions?.choose !== undefined)
|
|
866
|
-
this.need(layer.regions.choose, `${at}.regions.choose`);
|
|
867
|
-
if (layer.point !== undefined && pair)
|
|
868
|
-
this.report(at, 'a layer takes point, or latitude and longitude, not both');
|
|
869
|
-
else if (layer.point === undefined && pair && (layer.latitude === undefined || layer.longitude === undefined))
|
|
870
|
-
this.report(at, 'a layer needs both latitude and longitude');
|
|
871
|
-
if (layer.lines !== undefined && !placed)
|
|
872
|
-
this.report(`${at}.lines`, "a layer of lines needs where each line is: point, or latitude and longitude, of the line's fields");
|
|
873
|
-
if (layer.live && (!placed || layer.lines !== undefined))
|
|
874
|
-
this.report(`${at}.live`, "live is for a layer of the record's own place: it moves as its fields change");
|
|
875
|
-
this.checkModifiers(layer, at, ['invisible']);
|
|
876
|
-
layers.set(layer.id, { lines: layer.lines !== undefined, place: placed || !!layer.myLocation });
|
|
877
|
-
if (!placed)
|
|
878
|
-
return;
|
|
879
|
-
if (layer.lines === undefined)
|
|
880
|
-
return this.checkPlace(layer, at, this.here(), layer.pin, layer.card);
|
|
881
|
-
const def = this.need(layer.lines, `${at}.lines`);
|
|
882
|
-
if (!def)
|
|
883
|
-
return;
|
|
884
|
-
if (def.type !== 'one2many')
|
|
885
|
-
return this.report(`${at}.lines`, `"${layer.lines}" is a ${def.type}; a layer of lines is a one2many field`);
|
|
886
|
-
this.checkPlace(layer, at, { fields: def.fields, lines: layer.lines, parent: this.page.fields }, layer.pin, layer.card);
|
|
887
|
-
});
|
|
888
|
-
node.layers.forEach((layer, i) => {
|
|
889
|
-
const route = layer.route;
|
|
890
|
-
if (!route)
|
|
891
|
-
return;
|
|
892
|
-
const at = `${path}.layers[${i}].route`;
|
|
893
|
-
const end = (id, key) => {
|
|
894
|
-
if (id === undefined)
|
|
895
|
-
return;
|
|
896
|
-
const found = layers.get(id);
|
|
897
|
-
if (!found)
|
|
898
|
-
this.report(`${at}.${key}`, `no layer "${id}" on this map`);
|
|
899
|
-
else if (!found.place || found.lines)
|
|
900
|
-
this.report(`${at}.${key}`, `a route's ${key} is a layer of one place, or the reader's own; "${id}" is not`);
|
|
901
|
-
};
|
|
902
|
-
end(route.from, 'from');
|
|
903
|
-
end(route.to, 'to');
|
|
904
|
-
if (route.through !== undefined) {
|
|
905
|
-
const found = layers.get(route.through);
|
|
906
|
-
if (!found)
|
|
907
|
-
this.report(`${at}.through`, `no layer "${route.through}" on this map`);
|
|
908
|
-
else if (!found.lines)
|
|
909
|
-
this.report(`${at}.through`, `a route goes through a layer of lines; "${route.through}" is not one`);
|
|
910
|
-
}
|
|
911
|
-
if (route.to === undefined && route.through === undefined)
|
|
912
|
-
this.report(at, 'a route needs where it goes: to, or through');
|
|
913
|
-
const written = (name, key, ok, says) => {
|
|
914
|
-
if (name === undefined)
|
|
915
|
-
return;
|
|
916
|
-
const def = this.need(name, `${at}.write.${key}`);
|
|
917
|
-
if (def && !ok(def.type))
|
|
918
|
-
this.report(`${at}.write.${key}`, `"${name}" is a ${def.type}; ${says}`);
|
|
919
|
-
};
|
|
920
|
-
const number = (type) => type === 'float' || type === 'integer';
|
|
921
|
-
written(route.write?.seconds, 'seconds', number, 'seconds are a number field');
|
|
922
|
-
written(route.write?.metres, 'metres', number, 'metres are a number field');
|
|
923
|
-
written(route.write?.line, 'line', (type) => type === 'json', 'a line is kept in a json field');
|
|
924
|
-
});
|
|
925
|
-
node.layers.forEach((layer, i) => {
|
|
926
|
-
if (!layer.reachable)
|
|
927
|
-
return;
|
|
928
|
-
const found = layers.get(layer.reachable.from);
|
|
929
|
-
const at = `${path}.layers[${i}].reachable.from`;
|
|
930
|
-
if (!found)
|
|
931
|
-
this.report(at, `no layer "${layer.reachable.from}" on this map`);
|
|
932
|
-
else if (!found.place || found.lines)
|
|
933
|
-
this.report(at, `a reachable area is worked out from a layer of one place, or the reader's own; "${layer.reachable.from}" is not`);
|
|
934
|
-
});
|
|
935
|
-
node.fences?.forEach((fence, i) => {
|
|
936
|
-
const at = `${path}.fences[${i}]`;
|
|
937
|
-
const watched = node.layers.find((layer) => layer.id === fence.layer);
|
|
938
|
-
if (!watched)
|
|
939
|
-
this.report(`${at}.layer`, `no layer "${fence.layer}" on this map`);
|
|
940
|
-
else if (!watched.live && !watched.myLocation)
|
|
941
|
-
this.report(`${at}.layer`, `a fence watches a place that moves: a live layer, or the reader's own; "${fence.layer}" is not`);
|
|
942
|
-
if (fence.area !== undefined) {
|
|
943
|
-
const def = this.need(fence.area, `${at}.area`);
|
|
944
|
-
if (def && def.type !== 'json')
|
|
945
|
-
this.report(`${at}.area`, `"${fence.area}" is a ${def.type}; an area is kept in a json field`);
|
|
946
|
-
}
|
|
947
|
-
if (fence.within !== undefined && !node.layers.some((layer) => layer.id === fence.within && layer.reachable))
|
|
948
|
-
this.report(`${at}.within`, `a fence is within a reachable area of this map; "${fence.within}" is not one`);
|
|
949
|
-
this.checkSteps(fence.steps, `${at}.steps`);
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
672
|
walkChildren(children, path) {
|
|
953
673
|
children.forEach((child, i) => this.walkNode(child, `${path}.children[${i}]`));
|
|
954
674
|
}
|
|
@@ -980,13 +700,13 @@ class ReferenceCheck {
|
|
|
980
700
|
case 'form':
|
|
981
701
|
return this.checkFormNode(node, path);
|
|
982
702
|
case 'button':
|
|
983
|
-
return this.checkPress(node, path);
|
|
703
|
+
return this.checkPress(node, path, true);
|
|
984
704
|
case 'text':
|
|
985
705
|
return this.checkValuesIn(node.text, `${path}.text`);
|
|
986
706
|
case 'map':
|
|
987
|
-
return
|
|
707
|
+
return (0, map_checks_1.checkMap)(node, path, this.mapTools());
|
|
988
708
|
case 'related':
|
|
989
|
-
return
|
|
709
|
+
return (0, list_checks_1.checkRelated)(node, path, this.listTools());
|
|
990
710
|
case 'slot':
|
|
991
711
|
this.slots.add(node.name);
|
|
992
712
|
return;
|
|
@@ -1011,48 +731,6 @@ class ReferenceCheck {
|
|
|
1011
731
|
else
|
|
1012
732
|
this.formNames.set(node.name, path);
|
|
1013
733
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* A related list: on a record's page, its columns, order, search, cells, card and rows' rules of its own records'
|
|
1016
|
-
* fields, its link to this record a link of theirs to this model where its fields name it, and its filter's
|
|
1017
|
-
* `valueFrom` this record's.
|
|
1018
|
-
*/
|
|
1019
|
-
checkRelated(node, path) {
|
|
1020
|
-
// The quick check reads no shapes: a part with nothing to go by is said here.
|
|
1021
|
-
if (typeof node.model !== 'string' || typeof node.field !== 'string' || !Array.isArray(node.columns) || !node.fields || typeof node.fields !== 'object') {
|
|
1022
|
-
return this.report(path, 'a related list needs the model of its records, their link to this record (field), their fields and its columns');
|
|
1023
|
-
}
|
|
1024
|
-
const model = this.page.data.kind === 'record' ? this.page.data.model : null;
|
|
1025
|
-
if (!model || this.line)
|
|
1026
|
-
this.report(path, 'a related list shows the records that point at a record: it belongs on a record’s page');
|
|
1027
|
-
const fields = node.fields;
|
|
1028
|
-
this.checkListed(node, fields, path);
|
|
1029
|
-
// On a phone its rows are cards, as a list's: of its own records' fields.
|
|
1030
|
-
if (node.card)
|
|
1031
|
-
this.checkListCard(node.card, `${path}.card`, fields);
|
|
1032
|
-
// Its rows' own rules, as a list's, each read on the row's record: its own fields, its id, the person, the context.
|
|
1033
|
-
const row = { fields };
|
|
1034
|
-
node.rowTones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.rowTones[${i}]`, ['when'], row));
|
|
1035
|
-
this.checkModifiers(node, path, ['rowBold'], row);
|
|
1036
|
-
node.rowButtons?.forEach((button, i) => {
|
|
1037
|
-
this.claim(button.id, `${path}.rowButtons[${i}]`);
|
|
1038
|
-
this.checkPress(button, `${path}.rowButtons[${i}]`);
|
|
1039
|
-
this.checkModifiers(button, `${path}.rowButtons[${i}]`, ['invisible'], row);
|
|
1040
|
-
});
|
|
1041
|
-
const link = has(fields, node.field) ? fields[node.field] : undefined;
|
|
1042
|
-
if (link && model && (link.type !== 'many2one' || link.relation !== model)) {
|
|
1043
|
-
this.report(`${path}.field`, `"${node.field}" ${link.type === 'many2one' ? `links to ${link.relation}` : `is a ${link.type}`}; the records listed point at this one by a many2one to ${model}`);
|
|
1044
|
-
}
|
|
1045
|
-
for (const [name, def] of Object.entries(fields)) {
|
|
1046
|
-
if (def.type === 'monetary' && def.currencyField !== undefined)
|
|
1047
|
-
this.checkCurrency(def.currencyField, `${path}.fields.${name}.currencyField`, fields, undefined, fields);
|
|
1048
|
-
if (def.type === 'datetime' && def.timeZone !== undefined)
|
|
1049
|
-
this.checkZone(def.timeZone, `${path}.fields.${name}.timeZone`);
|
|
1050
|
-
}
|
|
1051
|
-
// Its filter is about its records, by their fields; what it compares with may be read from this record.
|
|
1052
|
-
node.filter?.forEach((item, i) => this.checkFilterItem(item, `${path}.filter[${i}]`, this.here()));
|
|
1053
|
-
if (node.viewAll && node.viewAll.page === this.page.id)
|
|
1054
|
-
this.report(`${path}.viewAll.page`, 'View all opens a list of the records related: this page is the record they point at');
|
|
1055
|
-
}
|
|
1056
734
|
walkTabs(node, path) {
|
|
1057
735
|
node.children.forEach((tab, i) => {
|
|
1058
736
|
const tabPath = `${path}.children[${i}]`;
|
|
@@ -1071,36 +749,13 @@ class ReferenceCheck {
|
|
|
1071
749
|
this.need(sheet.title.avatarField, `${path}.title.avatarField`);
|
|
1072
750
|
sheet.title.placeholderWhen?.forEach((item, i) => this.checkModifiers(item, `${path}.title.placeholderWhen[${i}]`, ['when']));
|
|
1073
751
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
this.checkModifiers(sheet.statusbar, at);
|
|
1078
|
-
if (durationsField !== undefined) {
|
|
1079
|
-
const times = this.need(durationsField, `${at}.durationsField`);
|
|
1080
|
-
if (times && times.type !== 'json')
|
|
1081
|
-
this.report(`${at}.durationsField`, `"${durationsField}" is a ${times.type}; time per step comes from a json field`);
|
|
1082
|
-
}
|
|
1083
|
-
if (saves && !clickable)
|
|
1084
|
-
this.report(`${at}.saves`, 'a click saves only where a step can be clicked (clickable)');
|
|
1085
|
-
const def = this.need(field, `${path}.statusbar.field`);
|
|
1086
|
-
if (fold && def && def.type !== 'many2one')
|
|
1087
|
-
this.report(`${at}.fold`, 'steps fold by their record (a stage), so only a link’s steps fold; a choice keeps the steps it shows with visibleStates');
|
|
1088
|
-
if (def && def.type !== 'selection' && def.type !== 'many2one') {
|
|
1089
|
-
this.report(`${path}.statusbar.field`, `"${field}" is a ${def.type}; a statusbar needs a selection or many2one`);
|
|
1090
|
-
}
|
|
1091
|
-
if (def?.type === 'selection' && visibleStates) {
|
|
1092
|
-
const values = new Set(def.options.map((o) => o.value));
|
|
1093
|
-
visibleStates.forEach((state, i) => {
|
|
1094
|
-
if (!values.has(state)) {
|
|
1095
|
-
this.report(`${path}.statusbar.visibleStates[${i}]`, `"${state}" is not an option of "${field}"`);
|
|
1096
|
-
}
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
752
|
+
// A statusbar, drawn by the business part, whose checks come with it in the one-tag script.
|
|
753
|
+
if (sheet.statusbar)
|
|
754
|
+
(0, statusbar_checks_1.checkStatusbar)(sheet.statusbar, path, { ...this.fieldTools(), checkModifiers: (owner, at, keys) => this.checkModifiers(owner, at, keys) });
|
|
1100
755
|
sheet.buttons?.forEach((button, i) => {
|
|
1101
756
|
this.claim(button.id, `${path}.buttons[${i}]`);
|
|
1102
757
|
this.checkModifiers(button, `${path}.buttons[${i}]`);
|
|
1103
|
-
this.checkPress(button, `${path}.buttons[${i}]
|
|
758
|
+
this.checkPress(button, `${path}.buttons[${i}]`, true);
|
|
1104
759
|
});
|
|
1105
760
|
sheet.statButtons?.forEach((stat, i) => {
|
|
1106
761
|
this.claim(stat.id, `${path}.statButtons[${i}]`);
|
|
@@ -1138,7 +793,7 @@ class ReferenceCheck {
|
|
|
1138
793
|
alert.buttons?.forEach((button, j) => {
|
|
1139
794
|
this.claim(button.id, `${at}.buttons[${j}]`);
|
|
1140
795
|
this.checkModifiers(button, `${at}.buttons[${j}]`);
|
|
1141
|
-
this.checkPress(button, `${at}.buttons[${j}]
|
|
796
|
+
this.checkPress(button, `${at}.buttons[${j}]`, true);
|
|
1142
797
|
});
|
|
1143
798
|
});
|
|
1144
799
|
// The gear menu: buttons of the record, each doing something — its steps, an action, or a built-in — and named.
|
|
@@ -1166,46 +821,6 @@ class ReferenceCheck {
|
|
|
1166
821
|
this.checkModifiers(sheet.sidePanel, `${path}.sidePanel`);
|
|
1167
822
|
}
|
|
1168
823
|
}
|
|
1169
|
-
/** An address with suggestions: a char field, its place kept as a place field keeps one, its parts in a json field. */
|
|
1170
|
-
checkAddressSearch(node, def, path) {
|
|
1171
|
-
const options = node.options ?? {};
|
|
1172
|
-
const kind = (key) => {
|
|
1173
|
-
const name = options[key];
|
|
1174
|
-
return typeof name === 'string' && has(this.page.fields, name) ? this.page.fields[name].type : undefined;
|
|
1175
|
-
};
|
|
1176
|
-
if (def && def.type !== 'char')
|
|
1177
|
-
this.report(`${path}.widget`, `address-search is for a char field; "${node.field}" is a ${def.type}`);
|
|
1178
|
-
const place = kind('placeField');
|
|
1179
|
-
if (place && place !== 'json' && place !== 'float')
|
|
1180
|
-
this.report(`${path}.options.placeField`, `"${options['placeField']}" is a ${place}; a place is kept in a json field, or in a float field (its latitude) with longitudeField`);
|
|
1181
|
-
if (place === 'float' && options['longitudeField'] === undefined)
|
|
1182
|
-
this.report(`${path}.options.placeField`, `"${options['placeField']}" keeps a latitude: name the float field for its longitude in longitudeField`);
|
|
1183
|
-
if (options['longitudeField'] !== undefined && place !== 'float')
|
|
1184
|
-
this.report(`${path}.options.longitudeField`, 'longitudeField goes with a placeField that is a float field, its latitude');
|
|
1185
|
-
const longitude = kind('longitudeField');
|
|
1186
|
-
if (longitude && longitude !== 'float')
|
|
1187
|
-
this.report(`${path}.options.longitudeField`, `"${options['longitudeField']}" is a ${longitude}; a longitude is kept in a float field`);
|
|
1188
|
-
const parts = kind('partsField');
|
|
1189
|
-
if (parts && parts !== 'json')
|
|
1190
|
-
this.report(`${path}.options.partsField`, `"${options['partsField']}" is a ${parts}; an address's parts are kept in a json field`);
|
|
1191
|
-
}
|
|
1192
|
-
/** Booking slots: on a date and time, its end written in another, asked again as the fields it depends on change. */
|
|
1193
|
-
checkSlots(node, def, path) {
|
|
1194
|
-
const options = node.options ?? {};
|
|
1195
|
-
if (def && def.type !== 'datetime')
|
|
1196
|
-
this.report(`${path}.widget`, `slots is for a datetime field; "${node.field}" is a ${def.type}`);
|
|
1197
|
-
const end = options['endField'];
|
|
1198
|
-
if (typeof end === 'string' && has(this.page.fields, end)) {
|
|
1199
|
-
const type = this.page.fields[end].type;
|
|
1200
|
-
if (end === node.field)
|
|
1201
|
-
this.report(`${path}.options.endField`, 'a booking ends in another field than the one it starts in');
|
|
1202
|
-
else if (type !== 'datetime')
|
|
1203
|
-
this.report(`${path}.options.endField`, `"${end}" is a ${type}; a booking's end is kept in a datetime field`);
|
|
1204
|
-
}
|
|
1205
|
-
const on = options['dependsOn'];
|
|
1206
|
-
if (Array.isArray(on))
|
|
1207
|
-
on.forEach((name, i) => typeof name === 'string' && this.need(name, `${path}.options.dependsOn[${i}]`));
|
|
1208
|
-
}
|
|
1209
824
|
/** A picker's own settings, on the kind of field each is for: a path on a link, Fieldia's own list on a choice, a button adding tags, a colour for each state. */
|
|
1210
825
|
checkPicker(node, def, path) {
|
|
1211
826
|
const options = node.options ?? {};
|
|
@@ -1227,6 +842,8 @@ class ReferenceCheck {
|
|
|
1227
842
|
}
|
|
1228
843
|
checkFieldNode(node, path) {
|
|
1229
844
|
const def = this.need(node.field, `${path}.field`);
|
|
845
|
+
// Its own label and help, as the field's: words that may show the answers given.
|
|
846
|
+
this.checkWords(node, path, ['label', 'help']);
|
|
1230
847
|
// Answer rules: each condition and rule across fields reads, each pattern is a regular expression.
|
|
1231
848
|
node.validate?.forEach((rule, i) => {
|
|
1232
849
|
const at = `${path}.validate[${i}]`;
|
|
@@ -1258,39 +875,31 @@ class ReferenceCheck {
|
|
|
1258
875
|
if (problem)
|
|
1259
876
|
this.report(`${path}.autocomplete`, problem);
|
|
1260
877
|
}
|
|
1261
|
-
if (def && node.totals)
|
|
1262
|
-
|
|
1263
|
-
this.report(`${path}.totals`, `totals only apply to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1264
|
-
}
|
|
1265
|
-
else {
|
|
1266
|
-
node.totals.forEach((column, i) => {
|
|
1267
|
-
const sub = has(def.fields, column) ? def.fields[column] : undefined;
|
|
1268
|
-
if (!sub)
|
|
1269
|
-
this.report(`${path}.totals[${i}]`, `"${column}" is not a field of the lines of "${node.field}"`);
|
|
1270
|
-
else if (!['integer', 'float', 'monetary'].includes(sub.type)) {
|
|
1271
|
-
this.report(`${path}.totals[${i}]`, `"${column}" is a ${sub.type}; only integer, float and monetary columns add up`);
|
|
1272
|
-
}
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
878
|
+
if (def && node.totals && def.type !== 'one2many')
|
|
879
|
+
this.report(`${path}.totals`, `totals only apply to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1276
880
|
// Options that point at another field: by convention their names end in "Field".
|
|
1277
881
|
for (const [key, value] of Object.entries(node.options ?? {})) {
|
|
1278
882
|
if (key.endsWith('Field') && typeof value === 'string')
|
|
1279
883
|
this.need(value, `${path}.options.${key}`);
|
|
1280
884
|
}
|
|
885
|
+
// An address with suggestions, drawn by the app's maps: its checks come with a map's in the one-tag script.
|
|
1281
886
|
if (node.widget === 'address-search')
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
887
|
+
(0, map_checks_1.checkAddressSearch)(node, def, path, this.mapTools());
|
|
888
|
+
// Booking slots on a date and time, a signature on a file: drawn by their parts, whose checks of their settings come
|
|
889
|
+
// with them in the one-tag script.
|
|
890
|
+
if (node.widget === 'slots' && def && def.type !== 'datetime')
|
|
891
|
+
this.report(`${path}.widget`, `slots is for a datetime field; "${node.field}" is a ${def.type}`);
|
|
892
|
+
else if (node.widget === 'slots')
|
|
893
|
+
(0, slots_checks_1.checkSlots)(node, path, this.fieldTools());
|
|
894
|
+
if (node.widget === 'signature' && def && def.type !== 'binary')
|
|
895
|
+
this.report(`${path}.widget`, `signature is for a binary field; "${node.field}" is a ${def.type}`);
|
|
896
|
+
else if (node.widget === 'signature')
|
|
897
|
+
(0, signature_checks_1.checkSignature)(node, path, this.fieldTools());
|
|
1285
898
|
if (def)
|
|
1286
899
|
this.checkPicker(node, def, path);
|
|
1287
900
|
// The order lines load in: fields of its lines.
|
|
1288
|
-
if (def && node.order)
|
|
1289
|
-
|
|
1290
|
-
this.report(`${path}.order`, `order only applies to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1291
|
-
else
|
|
1292
|
-
node.order.forEach((item, i) => !has(def.fields, item.field) && this.report(`${path}.order[${i}].field`, `"${item.field}" is not a field of the lines of "${node.field}"`));
|
|
1293
|
-
}
|
|
901
|
+
if (def && node.order && def.type !== 'one2many')
|
|
902
|
+
this.report(`${path}.order`, `order only applies to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1294
903
|
// A line's own form: its parts read the line's fields, the record as parent.
|
|
1295
904
|
if (def && node.lineForm) {
|
|
1296
905
|
if (def.type !== 'one2many')
|
|
@@ -1308,36 +917,8 @@ class ReferenceCheck {
|
|
|
1308
917
|
// A table's own rules: a cell's read on its line (with parent), a column's `hidden` on the record.
|
|
1309
918
|
if (def && node.lineDelete !== undefined && def.type !== 'one2many')
|
|
1310
919
|
this.report(`${path}.lineDelete`, `lineDelete only applies to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1311
|
-
if (def && (node.cells || node.rowTones || node.rowBold !== undefined || node.rowButtons
|
|
1312
|
-
|
|
1313
|
-
if (node.cells || node.rowTones || node.rowBold !== undefined || node.rowButtons)
|
|
1314
|
-
this.report(path, `cells, rowTones, rowBold and rowButtons only apply to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1315
|
-
}
|
|
1316
|
-
else {
|
|
1317
|
-
const line = { fields: def.fields, lines: node.field };
|
|
1318
|
-
for (const [column, rules] of Object.entries(node.cells ?? {})) {
|
|
1319
|
-
if (!has(def.fields, column)) {
|
|
1320
|
-
this.report(`${path}.cells.${column}`, `"${column}" is not a field of the lines of "${node.field}"`);
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
this.checkModifiers(rules, `${path}.cells.${column}`, ['invisible', 'readonly', 'required', 'bold'], line);
|
|
1324
|
-
this.checkModifiers(rules, `${path}.cells.${column}`, ['hidden']);
|
|
1325
|
-
rules.tones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.cells.${column}.tones[${i}]`, ['when'], line));
|
|
1326
|
-
// Its widget's options that point at a field: one of the line's.
|
|
1327
|
-
for (const [key, value] of Object.entries(rules.options ?? {})) {
|
|
1328
|
-
if (key.endsWith('Field') && typeof value === 'string')
|
|
1329
|
-
this.need(value, `${path}.cells.${column}.options.${key}`, def.fields);
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
node.rowTones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.rowTones[${i}]`, ['when'], line));
|
|
1333
|
-
this.checkModifiers(node, path, ['rowBold', 'lineDelete'], line);
|
|
1334
|
-
node.rowButtons?.forEach((button, i) => {
|
|
1335
|
-
this.claim(button.id, `${path}.rowButtons[${i}]`);
|
|
1336
|
-
this.checkModifiers(button, `${path}.rowButtons[${i}]`, ['invisible'], line);
|
|
1337
|
-
this.checkPress(button, `${path}.rowButtons[${i}]`);
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
920
|
+
if (def && def.type !== 'one2many' && (node.cells || node.rowTones || node.rowBold !== undefined || node.rowButtons))
|
|
921
|
+
this.report(path, `cells, rowTones, rowBold and rowButtons only apply to one2many fields; "${node.field}" is a ${def.type}`);
|
|
1341
922
|
// Buttons for the chosen lines and in the control row: buttons of the record, as any.
|
|
1342
923
|
for (const key of ['selectedButtons', 'controlButtons']) {
|
|
1343
924
|
if (!node[key])
|
|
@@ -1350,32 +931,14 @@ class ReferenceCheck {
|
|
|
1350
931
|
this.checkPress(button, `${path}.${key}[${i}]`);
|
|
1351
932
|
});
|
|
1352
933
|
}
|
|
1353
|
-
if (def && node.optionalColumns)
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
// The columns the table shows: those named, or every line field but the structural ones.
|
|
1359
|
-
const shown = node.columns ?? Object.keys(def.fields).filter((c) => c !== ('lineKinds' in def ? def.lineKinds?.field : undefined) && c !== def.sequenceField);
|
|
1360
|
-
for (const column of Object.keys(node.optionalColumns)) {
|
|
1361
|
-
if (!shown.includes(column))
|
|
1362
|
-
this.report(`${path}.optionalColumns.${column}`, `"${column}" is not a column of "${node.field}"`);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
934
|
+
if (def && node.optionalColumns && def.type !== 'one2many')
|
|
935
|
+
this.report(`${path}.optionalColumns`, `optional columns only apply to one2many fields; "${node.field}" is a ${def.type}`);
|
|
936
|
+
// A table of lines' own settings, drawn by the lines part: its columns, totals, order, cells and rows' rules.
|
|
937
|
+
if (def?.type === 'one2many')
|
|
938
|
+
return (0, table_checks_1.checkTable)(node, def, path, this.tableTools());
|
|
1366
939
|
if (!def || !node.columns)
|
|
1367
940
|
return;
|
|
1368
|
-
if (def.type === '
|
|
1369
|
-
node.columns.forEach((column, i) => {
|
|
1370
|
-
if (!has(def.fields, column)) {
|
|
1371
|
-
this.report(`${path}.columns[${i}]`, `"${column}" is not a field of the lines of "${node.field}"`);
|
|
1372
|
-
}
|
|
1373
|
-
else if (def.fields[column].type === 'one2many') {
|
|
1374
|
-
this.report(`${path}.columns[${i}]`, `"${column}" holds lines of its own: they are edited in the line's form (lineForm), not drawn in its row`);
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
else if (def.type === 'many2many') {
|
|
941
|
+
if (def.type === 'many2many') {
|
|
1379
942
|
// A table of the records linked: its columns are fields of those records.
|
|
1380
943
|
node.columns.forEach((column, i) => {
|
|
1381
944
|
if (!def.fields || !has(def.fields, column))
|