206 lines
6.0 KiB
Typst
206 lines
6.0 KiB
Typst
// ==========================================
|
||
// PROFILE: ДОГОВОР
|
||
// ==========================================
|
||
#import "../../domain/document.typ": document-profile, fail
|
||
#import "../../domain/attachments.typ": attachment-set
|
||
#import "../../domain/parties.typ": validate-parties, signer
|
||
#import "../../shared/numbering.typ": attachment_numbering
|
||
#import "../foundation.typ": apply-foundation
|
||
#import "../components.typ": appendix-heading, multi-party-signing, requisites-table
|
||
|
||
#let contract-section(
|
||
id,
|
||
title,
|
||
body,
|
||
number: auto,
|
||
level: 1,
|
||
) = {
|
||
if type(id) != str or id.trim() == "" {
|
||
fail("contract-section.id", "нужна непустая строка")
|
||
}
|
||
if type(title) not in (str, content) {
|
||
fail("contract-section.title", "ожидались str или content")
|
||
}
|
||
if type(body) not in (content, function) {
|
||
fail("contract-section.body", "ожидались content или function")
|
||
}
|
||
if type(level) != int or level < 1 {
|
||
fail("contract-section.level", "ожидалось положительное целое число")
|
||
}
|
||
(
|
||
kind: "contract-section",
|
||
id: id,
|
||
title: title,
|
||
body: body,
|
||
number: number,
|
||
level: level,
|
||
)
|
||
}
|
||
|
||
#let validate-sections(sections) = {
|
||
if type(sections) != array {
|
||
fail("contract.sections", "ожидался array")
|
||
}
|
||
let seen = ()
|
||
for section in sections {
|
||
if type(section) != dictionary or section.at("kind", default: none) != "contract-section" {
|
||
fail("contract.sections", "каждый элемент должен быть contract-section")
|
||
}
|
||
if section.id in seen { fail("contract.sections", "повторяющийся id " + repr(section.id)) }
|
||
seen.push(section.id)
|
||
}
|
||
sections
|
||
}
|
||
|
||
#let normalize-contract(metadata) = {
|
||
if type(metadata.attachments) == array {
|
||
metadata.insert("attachments", attachment-set(items: metadata.attachments))
|
||
}
|
||
metadata
|
||
}
|
||
|
||
#let validate-contract(metadata) = {
|
||
if type(metadata.title) not in (str, content) {
|
||
fail("contract.title", "ожидались str или content")
|
||
}
|
||
if metadata.number == none {
|
||
fail("contract.number", "номер договора обязателен")
|
||
}
|
||
let _ = validate-parties(metadata.parties, minimum: 2)
|
||
let _ = validate-sections(metadata.sections)
|
||
if type(metadata.attachments) != dictionary or metadata.attachments.at("kind", default: none) != "attachment-set" {
|
||
fail("contract.attachments", "ожидался attachment-set")
|
||
}
|
||
metadata
|
||
}
|
||
|
||
#let contract-preamble(data) = {
|
||
if data.preamble == auto {
|
||
[Стороны, указанные в разделе «Реквизиты и подписи сторон», заключили настоящий договор о нижеследующем.]
|
||
} else {
|
||
data.preamble
|
||
}
|
||
}
|
||
|
||
#let render-contract-sections(sections) = {
|
||
for (index, section) in sections.enumerate() {
|
||
let number = if section.number == auto { index + 1 } else { section.number }
|
||
heading(level: section.level, numbering: none)[#number. #upper(section.title)]
|
||
if type(section.body) == function { (section.body)() } else { section.body }
|
||
}
|
||
}
|
||
|
||
#let signing-parties(parties, include-media: true) = {
|
||
parties.map(item => {
|
||
if item.representative == none or include-media {
|
||
item
|
||
} else {
|
||
let person = item.representative
|
||
let clean-person = signer(
|
||
person.name,
|
||
person.title,
|
||
basis: person.basis,
|
||
signature: none,
|
||
stamp: none,
|
||
)
|
||
let clean-item = item
|
||
clean-item.insert("representative", clean-person)
|
||
clean-item
|
||
}
|
||
})
|
||
}
|
||
|
||
#let render-contract-attachments(attachments) = {
|
||
for (index, item) in attachments.items.enumerate() {
|
||
pagebreak()
|
||
let source-number = if item.number == auto { index + attachments.start } else { item.number }
|
||
let display-number = attachment_numbering(attachments.numbering, source-number)
|
||
appendix-heading(item, display-number)
|
||
v(0.5cm)
|
||
if type(item.body) == function { (item.body)() } else { item.body }
|
||
}
|
||
}
|
||
|
||
#let render-contract(body, ctx) = {
|
||
let data = ctx.metadata
|
||
let include-media = ctx.options.mode == "final"
|
||
let media-policy = if include-media { ctx.options.at("media-policy") } else { "reserve-space" }
|
||
let signing = signing-parties(data.parties, include-media: include-media)
|
||
let composed = [
|
||
#set heading(numbering: "1.1")
|
||
#show heading: it => {
|
||
set text(font: "Times New Roman", size: 11pt, weight: "bold", hyphenate: false)
|
||
set par(first-line-indent: 0pt, justify: false)
|
||
v(1em, weak: true)
|
||
block(sticky: true, width: 100%)[#it]
|
||
v(0.5em)
|
||
}
|
||
|
||
#align(center)[
|
||
#text(weight: "bold", size: 14pt)[#upper(data.title)] \
|
||
№ #data.number
|
||
]
|
||
#v(0.7cm)
|
||
#grid(
|
||
columns: (1fr, 1fr),
|
||
[#data.place],
|
||
align(right)[#data.date],
|
||
)
|
||
#v(0.8cm)
|
||
#contract-preamble(data)
|
||
#v(0.6cm)
|
||
#if data.sections.len() > 0 { render-contract-sections(data.sections) }
|
||
#body
|
||
|
||
#pagebreak(weak: true)
|
||
#heading(numbering: none)[РЕКВИЗИТЫ И ПОДПИСИ СТОРОН]
|
||
#requisites-table(data.parties)
|
||
#v(1cm)
|
||
#multi-party-signing(signing, media_policy: media-policy)
|
||
|
||
#if data.attachments.items.len() > 0 {
|
||
render-contract-attachments(data.attachments)
|
||
}
|
||
]
|
||
|
||
apply-foundation(
|
||
composed,
|
||
ctx: ctx,
|
||
tokens: (
|
||
text-font: "Times New Roman",
|
||
text-size: 11pt,
|
||
line-height: 1.15em,
|
||
page-margin: (left: 2.5cm, right: 2cm, top: 2cm, bottom: 2cm),
|
||
),
|
||
)
|
||
}
|
||
|
||
#let contract-profile(
|
||
number: none,
|
||
date: none,
|
||
place: "Екатеринбург",
|
||
title: "Договор",
|
||
parties: (),
|
||
preamble: auto,
|
||
sections: (),
|
||
attachments: attachment-set(),
|
||
) = {
|
||
document-profile(
|
||
"contract",
|
||
render-contract,
|
||
metadata: (
|
||
number: number,
|
||
date: date,
|
||
place: place,
|
||
title: title,
|
||
parties: parties,
|
||
preamble: preamble,
|
||
sections: sections,
|
||
attachments: attachments,
|
||
bibliographies: (),
|
||
),
|
||
normalize: normalize-contract,
|
||
validate: validate-contract,
|
||
)
|
||
}
|