410 lines
11 KiB
Typst
410 lines
11 KiB
Typst
// ==========================================
|
|
// PRESENTATION: ПЕРЕИСПОЛЬЗУЕМЫЕ КОМПОНЕНТЫ
|
|
// ==========================================
|
|
#import "../components.typ": corp_table, formula
|
|
#import "foundation.typ": render-media-slot
|
|
#import "../shared/numbering.typ": attachment_numbering
|
|
|
|
#let letter-accent = rgb("fbb20d")
|
|
|
|
// Большая информационная плашка для замечаний, пояснений и важных оговорок.
|
|
#let info-block(
|
|
body,
|
|
title: [ИНФОРМАЦИЯ],
|
|
fill: rgb("fbebec"),
|
|
accent: rgb("d73a49"),
|
|
text-fill: rgb("#402023"),
|
|
radius: 8pt,
|
|
inset: 8pt,
|
|
) = block(
|
|
width: 100%,
|
|
breakable: true,
|
|
fill: fill,
|
|
stroke: (left: 3pt + accent),
|
|
radius: radius,
|
|
inset: inset,
|
|
)[
|
|
#set par(first-line-indent: 0pt, justify: false)
|
|
#if title != none {
|
|
block(below: 0.35em)[
|
|
#text(fill: accent, weight: "bold", size: 9pt)[#title]
|
|
]
|
|
}
|
|
#text(fill: text-fill, size: 9pt)[#body]
|
|
]
|
|
|
|
#let footer-icon(name, color) = {
|
|
box(width: 20pt, height: 20pt, fill: color, radius: 2pt)[
|
|
#align(center + horizon)[
|
|
#image(
|
|
path("../assets/icons/" + name + ".png"),
|
|
width: 15pt,
|
|
height: 15pt,
|
|
fit: "contain",
|
|
)
|
|
]
|
|
]
|
|
}
|
|
|
|
#let company-logo(company, width: 4cm, height: 2.5cm, policy: "placeholder") = {
|
|
render-media-slot(
|
|
company.resources.logo,
|
|
policy: policy,
|
|
width: width,
|
|
height: height,
|
|
label: "ЛОГОТИП",
|
|
)
|
|
}
|
|
|
|
#let signing-media(stamp, signature, policy, show_stamp: true, offset: -1cm) = {
|
|
let has-missing-media = signature == none or (show_stamp and stamp == none)
|
|
let stamp-resource = if stamp == none and policy == "placeholder" {
|
|
path("../assets/placeholders/stamp.svg")
|
|
} else {
|
|
stamp
|
|
}
|
|
let signature-resource = if signature == none and policy == "placeholder" {
|
|
path("../assets/placeholders/signature.svg")
|
|
} else {
|
|
signature
|
|
}
|
|
if policy == "placeholder" and has-missing-media {
|
|
if show_stamp {
|
|
place(center, dx: -1.6cm, dy: offset)[
|
|
#render-media-slot(
|
|
stamp-resource,
|
|
policy: policy,
|
|
width: 2.8cm,
|
|
height: 2.8cm,
|
|
label: "ПЕЧАТЬ",
|
|
)
|
|
]
|
|
}
|
|
place(center, dx: if show_stamp { 1.6cm } else { 0cm }, dy: offset)[
|
|
#render-media-slot(
|
|
signature-resource,
|
|
policy: policy,
|
|
width: 2.8cm,
|
|
height: 1.4cm,
|
|
label: "ПОДПИСЬ",
|
|
)
|
|
]
|
|
} else {
|
|
if show_stamp {
|
|
place(center, dx: -0.5cm, dy: offset)[
|
|
#render-media-slot(
|
|
stamp-resource,
|
|
policy: policy,
|
|
width: 3.5cm,
|
|
height: 3.5cm,
|
|
label: "МЕСТО ПЕЧАТИ",
|
|
)
|
|
]
|
|
}
|
|
place(center, dx: if show_stamp { 1.5cm } else { 0cm }, dy: offset)[
|
|
#render-media-slot(
|
|
signature-resource,
|
|
policy: policy,
|
|
width: 3.5cm,
|
|
height: 2.5cm,
|
|
label: "МЕСТО ПОДПИСИ",
|
|
)
|
|
]
|
|
}
|
|
}
|
|
|
|
#let signature-block(
|
|
signer,
|
|
company: none,
|
|
media_policy: "placeholder",
|
|
show_stamp: true,
|
|
note: none,
|
|
) = {
|
|
let signature = signer.at(
|
|
"signature",
|
|
default: if company == none { none } else { company.resources.signature },
|
|
)
|
|
let stamp = signer.at(
|
|
"stamp",
|
|
default: if company == none { none } else { company.resources.stamp },
|
|
)
|
|
|
|
set par(first-line-indent: 0pt, justify: false, leading: 0.75em)
|
|
let left-content = [
|
|
#signer.title
|
|
#if company != none { [\ #company.legal.name] }
|
|
]
|
|
let right-content = [#signer.name]
|
|
let media-content = signing-media(
|
|
stamp,
|
|
signature,
|
|
media_policy,
|
|
show_stamp: show_stamp,
|
|
)
|
|
v(2cm)
|
|
layout(size => {
|
|
let left-width = measure(left-content).width
|
|
let right-width = measure(right-content).width
|
|
let roomy = left-width + right-width + 5.5cm <= size.width
|
|
if roomy {
|
|
grid(
|
|
columns: (auto, 1fr, auto),
|
|
align: (left + bottom, center + bottom, right + bottom),
|
|
left-content,
|
|
media-content,
|
|
right-content,
|
|
)
|
|
} else {
|
|
grid(
|
|
columns: (1fr, 6.5cm, 1fr),
|
|
column-gutter: 8pt,
|
|
align: (left + bottom, center + bottom, right + bottom),
|
|
left-content,
|
|
media-content,
|
|
right-content,
|
|
)
|
|
}
|
|
})
|
|
if note != none {
|
|
text(font: "Arial", size: 7.5pt, fill: luma(90))[#note]
|
|
}
|
|
}
|
|
|
|
#let approval-block(approval, company, media_policy: "placeholder") = {
|
|
let signer = approval.signer
|
|
let signature = signer.at("signature", default: company.resources.signature)
|
|
let signature-resource = if signature == none and media_policy == "placeholder" {
|
|
path("../assets/placeholders/signature.svg")
|
|
} else {
|
|
signature
|
|
}
|
|
set par(first-line-indent: 0pt, justify: false, leading: 0.75em)
|
|
box(width: 8cm)[
|
|
#align(right)[
|
|
#text(weight: "bold")[#approval.label] \
|
|
#signer.title \
|
|
#company.legal.name \
|
|
#v(0.2cm)
|
|
#render-media-slot(
|
|
signature-resource,
|
|
policy: media_policy,
|
|
width: 3cm,
|
|
height: 1.2cm,
|
|
label: "ПОДПИСЬ",
|
|
) \
|
|
#signer.name \
|
|
#if approval.date != none { approval.date }
|
|
]
|
|
]
|
|
}
|
|
|
|
#let company-footer(company) = {
|
|
let contacts = company.contacts
|
|
let banking = company.banking
|
|
let contact-lines = ()
|
|
if contacts.phone != none { contact-lines.push(contacts.phone) }
|
|
if contacts.email != none { contact-lines.push(contacts.email) }
|
|
if contacts.website != none { contact-lines.push(contacts.website) }
|
|
let id-lines = ()
|
|
for (key, label) in (
|
|
("inn", "ИНН"),
|
|
("kpp", "КПП"),
|
|
("ogrn", "ОГРН"),
|
|
("bin", "БИН"),
|
|
("kbe", "КБЕ"),
|
|
) {
|
|
let value = company.legal.at(key, default: none)
|
|
if value != none { id-lines.push([#label #value]) }
|
|
}
|
|
let banking-lines = ()
|
|
if banking.bank != none { banking-lines.push([Банк: #banking.bank]) }
|
|
if banking.at("correspondent-account", default: none) != none {
|
|
banking-lines.push([Кор. счёт: #banking.at("correspondent-account")])
|
|
}
|
|
if banking.bik != none { banking-lines.push([БИК: #banking.bik]) }
|
|
if contacts.at("address-en", default: none) != none {
|
|
banking-lines.push(contacts.at("address-en"))
|
|
}
|
|
set text(font: "Arial", size: 7.5pt, fill: luma(85))
|
|
set par(first-line-indent: 0pt, justify: false, leading: 0.2em)
|
|
grid(
|
|
columns: (1fr, 1.2fr),
|
|
column-gutter: 15pt,
|
|
[
|
|
#grid(
|
|
columns: (20pt, 1fr),
|
|
rows: (auto, auto),
|
|
column-gutter: 8pt,
|
|
row-gutter: 8pt,
|
|
if contact-lines.len() == 0 { none } else { footer-icon("email", company.brand.color) },
|
|
contact-lines.join([\ ]),
|
|
if contacts.address == none { none } else { footer-icon("location", company.brand.color) },
|
|
if contacts.address == none { none } else { contacts.address },
|
|
)
|
|
],
|
|
grid(
|
|
columns: (1fr, 20pt),
|
|
rows: (auto, auto),
|
|
column-gutter: 8pt,
|
|
row-gutter: 8pt,
|
|
align: (right, center + horizon),
|
|
align(right, id-lines.join([\ ])),
|
|
if id-lines.len() == 0 { none } else { footer-icon("card", company.brand.color) },
|
|
align(right, banking-lines.join([\ ])),
|
|
if banking-lines.len() == 0 { none } else { footer-icon("bank", company.brand.color) },
|
|
),
|
|
)
|
|
}
|
|
|
|
#let letter-header(
|
|
company,
|
|
recipient,
|
|
date: none,
|
|
reference: none,
|
|
title: none,
|
|
media_policy: "placeholder",
|
|
) = {
|
|
set par(first-line-indent: 0pt, justify: false, leading: 0.65em)
|
|
place(top + left, dx: 10cm, dy: -1.5cm)[
|
|
#rect(
|
|
width: 6.5cm,
|
|
height: 2cm,
|
|
fill: letter-accent,
|
|
radius: (bottom: 2pt),
|
|
)
|
|
]
|
|
v(30pt)
|
|
box(width: 4cm, height: 2.5cm)[
|
|
#company-logo(company, width: 4cm, height: 2.5cm, policy: media_policy)
|
|
]
|
|
v(-50pt)
|
|
v(2.5cm)
|
|
grid(
|
|
columns: (1.4fr, 1fr),
|
|
[
|
|
#if reference != none { [Исх. № #reference] }
|
|
#if date != none { [ от #date] }
|
|
],
|
|
[
|
|
#if recipient.company != none { [#recipient.company\ ] }
|
|
#if recipient.title != none { [#recipient.title\ ] }
|
|
#if recipient.name != none { recipient.name }
|
|
#if recipient.address != none { [\ #recipient.address] }
|
|
],
|
|
)
|
|
v(0.7cm)
|
|
if title != none {
|
|
align(center)[#text(weight: "bold")[#upper(title)]]
|
|
v(0.2cm)
|
|
}
|
|
}
|
|
|
|
#let attachment-list(attachments) = {
|
|
let items = if type(attachments) == dictionary { attachments.items } else { attachments }
|
|
if items.len() > 0 {
|
|
set par(first-line-indent: 0pt, justify: false)
|
|
for (index, item) in items.enumerate() {
|
|
let number = if item.number == auto { index + 1 } else { item.number }
|
|
[Приложение #number: #item.title]
|
|
if index < items.len() - 1 { linebreak() }
|
|
}
|
|
}
|
|
}
|
|
|
|
#let requisites-table(parties) = {
|
|
let field-labels = (
|
|
inn: "ИНН",
|
|
kpp: "КПП",
|
|
ogrn: "ОГРН",
|
|
bin: "БИН",
|
|
kbe: "КБе",
|
|
address: "Юридический адрес",
|
|
legal-address: "Юридический адрес",
|
|
phone: "Телефон",
|
|
email: "E-mail",
|
|
website: "Сайт",
|
|
bank: "Банк",
|
|
account: "Расчётный счёт",
|
|
settlement-account: "Расчётный счёт",
|
|
correspondent-account: "Корреспондентский счёт",
|
|
ks: "Корреспондентский счёт",
|
|
bik: "БИК",
|
|
iban: "IBAN",
|
|
)
|
|
let hidden-fields = (
|
|
"name",
|
|
"short-name",
|
|
"jurisdiction",
|
|
"summary",
|
|
"address-en",
|
|
"city",
|
|
)
|
|
let render-fields(fields) = {
|
|
for (key, value) in fields {
|
|
if value != none and key not in hidden-fields {
|
|
let label = field-labels.at(key, default: key)
|
|
[#label: #value\ ]
|
|
}
|
|
}
|
|
}
|
|
let columns = range(parties.len()).map(_ => 1fr)
|
|
table(
|
|
columns: columns,
|
|
stroke: 0.5pt,
|
|
inset: 6pt,
|
|
..parties.map(item => [
|
|
#set par(first-line-indent: 0pt, justify: false, leading: 0.75em)
|
|
#text(weight: "bold")[#item.role] \
|
|
#item.name \
|
|
#render-fields(item.legal)
|
|
#render-fields(item.contacts)
|
|
#render-fields(item.banking)
|
|
]),
|
|
)
|
|
}
|
|
|
|
#let multi-party-signing(parties, media_policy: "reserve-space") = {
|
|
let columns = range(parties.len()).map(_ => 1fr)
|
|
grid(
|
|
columns: columns,
|
|
column-gutter: 16pt,
|
|
..parties.map(item => {
|
|
let person = item.representative
|
|
[
|
|
#set par(first-line-indent: 0pt, justify: false, leading: 0.75em)
|
|
#if person != none {
|
|
grid(
|
|
columns: (1fr,),
|
|
rows: (auto, 0.35cm, 3.5cm, 0.25cm, auto),
|
|
[#text(weight: "bold")[#item.role] \ #person.title],
|
|
[],
|
|
block(width: 100%, height: 3.5cm)[
|
|
#signing-media(
|
|
person.stamp,
|
|
person.signature,
|
|
media_policy,
|
|
show_stamp: true,
|
|
offset: 0cm,
|
|
)
|
|
],
|
|
[],
|
|
[#person.name],
|
|
)
|
|
} else {
|
|
text(weight: "bold")[#item.role]
|
|
}
|
|
]
|
|
}),
|
|
)
|
|
}
|
|
|
|
#let appendix-heading(item, display-number) = {
|
|
set par(first-line-indent: 0pt, justify: false, leading: 0.75em)
|
|
align(right)[Приложение #display-number]
|
|
v(0.2cm)
|
|
align(center)[
|
|
#text(weight: "bold")[#upper(item.title)]
|
|
#if item.subtitle != none { [\ #text(weight: "bold")[#item.subtitle]] }
|
|
]
|
|
}
|