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