exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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:
Acontrib/typst/VQF_902_11.typ | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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