diff --git a/field-mapping.md b/field-mapping.md index a1f46b1..8c90ca9 100644 --- a/field-mapping.md +++ b/field-mapping.md @@ -80,11 +80,28 @@ Tab types that do not merge (only first location used or handled specially): `radioGroupTabs` — each location is one radio button within the group `signerAttachmentTabs` — each location is an independent attachment request +## Conditional Logic Mapping + +Adobe Sign `conditionalAction` → DocuSign `conditionalParentLabel` + `conditionalParentValue` on the dependent tab. + +| Adobe Sign | DocuSign | Notes | +|-----------------------------------|---------------------------------|-------| +| `predicates[].fieldName` | `conditionalParentLabel` | For radio groups, matches the group name | +| `predicates[].value` | `conditionalParentValue` | The value the trigger must equal to reveal the tab | +| `action: SHOW` | Supported | Tab is hidden until condition is met | +| `action: HIDE` | **Not supported** | No DocuSign equivalent — condition skipped, field always shown | +| `operator: EQUALS` | Supported | Only operator DocuSign supports | +| Other operators | **Not supported** | Condition skipped, warning logged | +| Multiple predicates (ANY/ALL) | **Partial** — first EQUALS only | Warning logged; remaining predicates ignored | + ## Known Gaps -- **Conditional logic**: Adobe Sign conditional show/hide rules are not mapped. - DocuSign supports conditional tabs but the logic structure differs — manual - rewrite required per template. +- **Conditional HIDE**: Adobe Sign can conditionally hide a field. DocuSign only supports + revealing hidden fields — there is no native way to hide a visible field conditionally. + Templates with HIDE conditions will have those fields always visible after migration. +- **Multi-predicate conditions**: Adobe Sign supports ANY/ALL of multiple predicates. + DocuSign only supports a single parent condition per tab. Only the first EQUALS + predicate is mapped; complex conditions require manual rework. - **DocuSign formula fields**: No Adobe Sign equivalent — flag for manual rewrite. - **Advanced field validation**: Adobe regex/custom script validation is not mapped; best-effort via standard DocuSign validation types only. diff --git a/src/compose_docusign_template.py b/src/compose_docusign_template.py index 36ef65a..7997806 100644 --- a/src/compose_docusign_template.py +++ b/src/compose_docusign_template.py @@ -23,6 +23,14 @@ Field type coverage: Partial: FILE_CHOOSER → signerAttachmentTabs (with warning) STAMP → stampTabs (requires stamp feature enabled on DocuSign account — warning logged) Skipped: INLINE_IMAGE, PARTICIPATION_STAMP (no DocuSign equivalent — warning logged) + +Conditional logic: + Mapped: Single-predicate SHOW conditions (EQUALS operator) → + conditionalParentLabel + conditionalParentValue on the dependent tab. + For radio groups the parentLabel matches the radio group name. + Warnings: Multi-predicate ANY/ALL conditions (only first EQUALS predicate mapped), + HIDE action (not supported in DocuSign — condition skipped), + non-EQUALS operators (skipped). """ import base64 @@ -142,6 +150,72 @@ def _sized_tabs(locations: list, label: str, extra: dict | None = None) -> list: return [_make_sized_tab(loc, label, extra) for loc in locations] +# --------------------------------------------------------------------------- +# Conditional logic +# --------------------------------------------------------------------------- + +def _apply_conditional_to_tabs(tabs: dict, field: dict, warnings: list) -> dict: + """ + Apply DocuSign conditionalParentLabel / conditionalParentValue to tabs based + on an Adobe Sign conditionalAction. + + Adobe Sign model: + conditionalAction.predicates[].{fieldName, operator, value}, action=SHOW|HIDE + + DocuSign model (on the dependent tab): + conditionalParentLabel — tabLabel or radioGroup groupName of the trigger tab + conditionalParentValue — value the trigger must have to reveal this tab + + Mapping limitations: + - Only SHOW action is supported. DocuSign has no native HIDE — condition skipped. + - Only EQUALS operator is supported. Others are skipped. + - Only one predicate is mapped. Multi-predicate ANY/ALL logic is not supported; + the first EQUALS predicate is used and a warning is logged. + """ + if not tabs: + return tabs + + ca = field.get("conditionalAction", {}) + predicates = ca.get("predicates", []) + if not predicates: + return tabs # No conditions — field is always visible + + label = field.get("name", "unnamed") + action = ca.get("action", "SHOW") + + if action != "SHOW": + warnings.append( + f"Conditional '{label}': action={action} is not supported in DocuSign " + f"(only SHOW is supported) — condition skipped" + ) + return tabs + + predicate = next((p for p in predicates if p.get("operator") == "EQUALS"), None) + if not predicate: + warnings.append( + f"Conditional '{label}': no EQUALS predicate found " + f"(operators: {[p.get('operator') for p in predicates]}) — condition skipped" + ) + return tabs + + if len(predicates) > 1: + warnings.append( + f"Conditional '{label}': {len(predicates)} predicates with " + f"anyOrAll={ca.get('anyOrAll')} — only first EQUALS predicate mapped, " + f"remaining conditions ignored" + ) + + parent_label = predicate["fieldName"] + parent_value = predicate["value"] + + for tab_list in tabs.values(): + for tab in tab_list: + tab["conditionalParentLabel"] = parent_label + tab["conditionalParentValue"] = parent_value + + return tabs + + # --------------------------------------------------------------------------- # Tab builder # --------------------------------------------------------------------------- @@ -309,6 +383,7 @@ def compose_template(template_dir: str, output_path: str) -> tuple[dict, list[st if idx >= len(signers): idx = 0 tabs = build_tabs_for_field(field, warnings) + tabs = _apply_conditional_to_tabs(tabs, field, warnings) signers[idx]["tabs"] = merge_tabs(signers[idx]["tabs"], tabs) template = {