Files
test/.template/lib/domain/parties.typ
T
2026-10-09 04:24:38 +00:00

125 lines
3.6 KiB
Typst

// ==========================================
// DOMAIN: СТОРОНЫ, АДРЕСАТЫ И ПОДПИСАНТЫ
// ==========================================
#import "document.typ": fail, ensure-dictionary
#let normalize-text(value, scope, required: false) = {
if value == none {
if required { fail(scope, "поле обязательно") }
none
} else if type(value) != str {
fail(scope, "ожидалась строка")
} else if value.trim() == "" {
if required { fail(scope, "поле не может быть пустым") }
none
} else {
value
}
}
#let signer(
name,
title,
basis: none,
signature: none,
stamp: none,
) = {
for (key, resource) in (("signature", signature), ("stamp", stamp)) {
if resource != none and type(resource) not in (path, content) {
fail("signer." + key, "ожидались none, path или content")
}
}
(
kind: "signer",
name: normalize-text(name, "signer.name", required: true),
title: normalize-text(title, "signer.title", required: true),
basis: normalize-text(basis, "signer.basis"),
signature: signature,
stamp: stamp,
)
}
#let recipient(
company: none,
title: none,
name: none,
address: none,
) = {
let resolved = (
kind: "recipient",
company: normalize-text(company, "recipient.company"),
title: normalize-text(title, "recipient.title"),
name: normalize-text(name, "recipient.name"),
address: normalize-text(address, "recipient.address"),
)
if resolved.company == none and resolved.name == none {
fail("recipient", "нужно указать организацию или имя адресата")
}
resolved
}
#let party(
id,
role,
name,
legal: (:),
contacts: (:),
banking: (:),
representative: none,
) = {
if representative != none and (
type(representative) != dictionary
or representative.at("kind", default: none) != "signer"
) {
fail("party.representative", "ожидались none или signer")
}
(
kind: "party",
id: normalize-text(id, "party.id", required: true),
role: normalize-text(role, "party.role", required: true),
name: normalize-text(name, "party.name", required: true),
legal: ensure-dictionary(legal, "party.legal"),
contacts: ensure-dictionary(contacts, "party.contacts"),
banking: ensure-dictionary(banking, "party.banking"),
representative: representative,
)
}
#let approval(signer, date: none, label: "УТВЕРЖДАЮ") = {
if type(signer) != dictionary or signer.at("kind", default: none) != "signer" {
fail("approval.signer", "ожидался signer")
}
(
kind: "approval",
signer: signer,
date: normalize-text(date, "approval.date"),
label: normalize-text(label, "approval.label", required: true),
)
}
#let validate-parties(parties, minimum: 0) = {
if type(parties) != array {
fail("parties", "ожидался array")
}
if parties.len() < minimum {
fail("parties", "нужно не менее " + str(minimum) + " сторон")
}
let seen = ()
for item in parties {
if type(item) != dictionary or item.at("kind", default: none) != "party" {
fail("parties", "каждый элемент должен быть party")
}
if item.id in seen {
fail("parties", "повторяющийся id " + repr(item.id))
}
if item.representative != none and (
type(item.representative) != dictionary
or item.representative.at("kind", default: none) != "signer"
) {
fail("parties", "representative должен быть signer")
}
seen.push(item.id)
}
parties
}