commit 5d73821fd6f9ce2c4283f684a05b3d9550684b7d
parent 5cdc847a2b76ea20f27679b0f9100e02c856ec10
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 10 Nov 2025 23:12:33 +0100
add Typst template for VQF_902_11
Diffstat:
1 file changed, 240 insertions(+), 0 deletions(-)
diff --git a/contrib/typst/VQF_902_11.typ b/contrib/typst/VQF_902_11.typ
@@ -0,0 +1,239 @@
+// VQF 902.11 Establishing of the controlling person (K)
+// Pass JSON data as content dictionary
+
+#let form(data) = {
+ set page(
+ paper: "a4",
+ margin: (left: 2cm, right: 2cm, top: 2cm, bottom: 2.5cm),
+ footer: context [
+ #grid(
+ columns: (1fr, 1fr),
+ align: (left, right),
+ text(size: 8pt)[
+ VQF doc. Nr. 902.11#linebreak()
+ Version of 1 December 2015
+ ],
+ text(size: 8pt)[
+ Page #here().page() of #counter(page).final().first()
+ ]
+ )
+ ]
+ )
+
+ set text(font: "Liberation Sans", size: 10pt)
+ set par(justify: false, leading: 0.65em)
+
+ // Helper function to get value or empty string
+ let get(key, default: "") = {
+ data.at(key, default: default)
+ }
+
+ // Helper function for checkbox
+ let checkbox(checked) = {
+ box(
+ width: 3mm,
+ height: 3mm,
+ stroke: 0.5pt + black,
+ inset: 0.3mm,
+ if checked == true or checked == "true" {
+ place(center + horizon, text(size: 8pt, sym.checkmark))
+ }
+ )
+ }
+
+ // Header
+ align(center, text(size: 11pt, weight: "bold")[CONFIDENTIAL])
+
+ v(0.5em)
+
+ grid(
+ columns: (50%, 50%),
+ gutter: 1em,
+ image("vss_vqf_verein.png", width: 80%),
+ align(right)[
+ #table(
+ columns: (1fr, 1fr),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ align: (left, left),
+ [VQF member no.], [AMLA File No.],
+ [#get("VQF_MEMBER_NUMBER")], [#get("FILE_NUMBER")]
+ )
+ ]
+ )
+
+ v(1em)
+
+ align(left, text(size: 14pt, weight: "bold")[Establishing of the controlling person of operating legal entities and partnerships both not quoted on the stock exchange (K)])
+
+ v(0.3em)
+
+ text(size: 9pt, style: "italic")[
+ (for operating legal entities and partnership that are contracting partner as well as analogously for operating legal entities and partnership that are beneficial owners)
+ ]
+
+ v(-1em)
+ line(length:100%)
+
+ v(1em)
+
+ // Section 1: Contracting Partner
+ text(size: 11pt, weight: "bold")[Contracting partner:]
+
+ v(0.5em)
+
+ table(
+ columns: (1fr),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [#get("IDENTITY_CONTRACTING_PARTNER")]
+ )
+
+ v(1em)
+
+ // Section 2: Declaration
+ let control_reason = get("CONTROL_REASON")
+
+ text()[The contracting partner hereby declares that (tick the appropriate box):]
+
+ block(breakable: false)[
+ #grid(
+ columns: (auto, 1fr),
+ gutter: 0.5em,
+ row-gutter: 0.8em,
+ checkbox(control_reason == "HAS_25_MORE_RIGHTS"),
+ [the person(s) listed below is/are *holding 25% or more of the contracting partner's shares (capital shares or voting rights)*; or],
+
+ checkbox(control_reason == "OTHER_WAY"),
+ [if the capital shares or voting rights cannot be determined or in case there are no capital shares or voting rights 25% or more, the contracting partner hereby declares that the person(s) listed below *is/are controlling the contracting partner in other ways*; or],
+
+ checkbox(control_reason == "DIRECTOR"),
+ [in case this/these person(s) cannot be determined or this/these person(s) does/do not exist, the contracting partner hereby declares that the person(s) listed below is/are the *managing director(s)*]
+ )
+ ]
+
+ v(1em)
+
+ // Section 3: Controlling Persons
+ let persons = get("IDENTITY_LIST", default: ())
+ let has_persons = type(persons) == array and persons.len() > 0
+
+ if has_persons {
+ for person in persons {
+ let get_person(key) = {
+ person.at(key, default: "")
+ }
+
+ block(breakable: false)[
+ #v(0.5em)
+ #table(
+ columns: (35%, 65%),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [Full name:], [#get_person("FULL_NAME")],
+ [Actual address of domicile:], [#get_person("DOMICILE_ADDRESS")]
+ )
+ #v(0.5em)
+ ]
+ }
+ } else {
+ block(breakable: false)[
+ #v(0.5em)
+ #table(
+ columns: (35%, 65%),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [Last name(s):], [],
+ [First name(s):], [],
+ [Actual address of domicile:], []
+ )
+ #v(0.5em)
+ ]
+ }
+
+ v(1.5em)
+
+ // Section 4: Fiduciary Holding
+ text(size: 11pt, weight: "bold")[Fiduciary holding assets]
+
+ v(0.5em)
+
+ let third_party = get("THIRD_PARTY_OWNERSHIP")
+
+ block(breakable: false)[
+ #text()[Is a third person the beneficial owner of the assets held in the account/securities account?]
+
+ #grid(
+ columns: (auto, 1fr),
+ gutter: 0.5em,
+ row-gutter: 0.5em,
+ checkbox(not third_party), [No.],
+ checkbox(third_party), [Yes. $=>$ The relevant information regarding the beneficial owner has to be obtained by filling in a separate VQF doc. No. 902.9.]
+ )
+ ]
+
+ v(0.5em)
+
+ text()[The contracting partner hereby undertakes to automatically inform of any changes to the information contained herein.]
+
+ v(0.5em)
+
+ // Signature Section
+ let submitted_by = get("SUBMITTED_BY")
+
+ if submitted_by == "CUSTOMER" {
+ table(
+ columns: (40%, 10%, 50%),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [Date:],
+ [],
+ [Signature(s):],
+ [#get("SIGN_DATE")],
+ [],
+ [#get("SIGNATURE")]
+ )
+
+ v(1em)
+
+ text(size: 9pt, style: "italic")[
+ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, documents forgery).
+ ]
+ } else if submitted_by == "AML_OFFICER" {
+ text(weight: "bold")[Signed declaration by the customer]
+
+ v(0.5em)
+
+ text(size: 9pt)[The attachment contains the customer's signature on the beneficial owner declaration.]
+
+ v(0.5em)
+
+ table(
+ columns: (1fr),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [Signed Document:],
+ [#if get("ATTACHMENT_SIGNED_DOCUMENT") != "" [Document attached] else [No document]]
+ )
+ } else {
+ text(weight: "bold")[Invalid submitter (#submitted_by)]
+ }
+}
+
+// Example usage:
+#form((
+ "VQF_MEMBER_NUMBER": "12345",
+ "FILE_NUMBER": "42",
+ "IDENTITY_CONTRACTING_PARTNER": "Example Company AG\nBahnhofstrasse 1\n8001 Zurich\nSwitzerland",
+ "CONTROL_REASON": "HAS_25_MORE_RIGHTS",
+ "IDENTITY_LIST": (
+ (
+ "FULL_NAME": "Jane Smith",
+ "DOMICILE_ADDRESS": "Teststrasse 456\n8001 Zurich"
+ ),
+ ),
+ "THIRD_PARTY_OWNERSHIP": false,
+ "SUBMITTED_BY": "CUSTOMER",
+ "SIGNATURE": "Jane Smith",
+ "SIGN_DATE": "10.11.2025",
+))
+\ No newline at end of file