// ========================================== // 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 }