Перейти до основного вмісту

Нормативно визначені макети

Деякі документи мають макет, встановлений нормативно: платіжна інструкція, касовий ордер, форма з визначеним переліком полів. Для них макет не є дизайнерським рішенням, тож форма друку має це відображати. Рушій пропонує два способи побудувати такий макет. Використовуйте перший. Другий існує для випадку, коли сама геометрія визначена нормативно.

Використовуйте вбудовану навичку

create-print-form (у .claude/skills/ вашого репозиторію) містить повну таблицю атрибутів і діагностику. Ця сторінка пояснює, коли нормативна форма відрізняється і який інструмент обрати.

Більшості нормативних форм достатньо рамки й ролей​

Нормативний документ у еталонній конфігурації — це звичайна форма друку з трьома відмінними вибором:

  • Frame = PrintFrame.None. Ні фірмового бланка, ні рядка параметрів. Платника або компанію називає блок сторони, тож бланк повторював би її. Смуга нижнього колонтитула все одно малюється.
  • Чернетки. Forbidden для документа, який взагалі не можна друкувати як чернетку, тож друк відхиляється. Marked для того, який можна друкувати як чернетку, з позначкою.
  • Без таблиці, якщо документ її не має. Форма, що складається лише зі сторін, реквізитів і підсумків, просто не має [PrintTable]. Не додавайте колекцію-заглушку, щоб її заповнити.

Решта — той самий словник, що й у будь-якій формі друку: сторони, реквізити, Note, GrandTotal, Currency, підписи і VatMode.

Робочий приклад: платіжна інструкція​

Платіжна інструкція (PaymentInstructionPrintForm, «Платіжна інструкція») — це нормативний документ еталонної конфігурації без таблиці. Її не надсилають банку на папері. Це те, що користувач передруковує в клієнт-банк. Оплачені документи на ній не друкуються, і фірмового бланка в неї немає:

[PrintForm(Frame = PrintFrame.None, Drafts = DraftPrinting.Marked)]
[PrintTitle("PaymentInstructionPrintTitle")]
public sealed class PaymentInstructionPrintForm : DocumentPrintForm
{
[Caption("Payer")]
public required PrintParty Payer { get; set; }

// Null, коли платіж не називає контрагента (комісія банку): блок пропускається.
[Caption("Recipient")]
public PrintParty? Recipient { get; set; }

[PrintRole(PrintRole.Note)]
[Caption("PaymentPurpose")]
public string PaymentPurpose { get; set; } = string.Empty;

// Платіжний документ не робить жодного твердження про ПДВ: сума може містити ПДВ, тож без примітки «Без ПДВ» під сумою прописом.
public PrintVatMode VatMode { get; set; } = PrintVatMode.None;

[PrintRole(PrintRole.GrandTotal)]
public decimal Amount { get; set; }

[PrintRole(PrintRole.Currency)]
public string? CurrencyCode { get; set; }

public IReadOnlyList<PrintSignature> Signatures { get; set; } = [];
}

Кожен вибір має причину:

  • Без фірмового бланка. Блок платника — це сама компанія, тож бланк друкував би компанію двічі.
  • Без таблиці. Інструкція стосується одного платежу. Документи, які цей платіж погашає, на ній не перелічуються.
  • VatMode = None. Сума платежу може містити ПДВ, а може й не містити, і «Без ПДВ» було б твердженням, якого платіж не може зробити. Форма None друкує підсумок і суму прописом, і жодного твердження про ПДВ.
  • Recipient, який може бути null. Комісія банку не називає контрагента. Нульова сторона прибирає свій блок, а не друкує порожній.

Касові ордери (прибутковий і видатковий, «ПКО» та «ВКО») мають ту саму будову з Frame = PrintFrame.None і Drafts = DraftPrinting.Forbidden, бо касовий ордер не можна друкувати як чернетку. Компанія є першою стороною. Ім'я головного бухгалтера береться з константи, а ім'я касира — з каси. Касовий ордер у стані чернетки відхиляється в кожному форматі.

Запасний варіант: розділи та стилі​

Деякі нормативні форми визначають не лише те, що означає поле, а й де воно стоїть на сторінці. Для них рушій зберігає геометричний словник:

ОбластьАтрибутЩо робить
Клас, повторюваний[PrintSection(key, Columns = n)]Секція потоку: сітка з n стовпців «підпис і значення». PositionX, PositionY і PositionWidth прив'язують її до положення на сторінці.
Клас, повторюваний[FixedSection(key, HeightMm = …)]Зарезервований прямокутник в абсолютних міліметрових координатах, завжди прив'язаний. Його поля друкують по одному рядку «підпис і значення» в порядку оголошення.
Клас, повторюваний[PrintStyle(key)]Іменований стиль: Align, FontSize, Bold, Italic, Color, BackgroundColor і Borders. Секція чи стовпець користуються ним через StyleKey.
Властивість[PrintSectionRef(key)]Розміщує скалярну властивість у секції. Явна секція має перевагу над роллю.

Три правила діють для всіх них:

  • У секції немає власного заголовка. Давайте кожному полю відмінний підпис: «IBAN платника» і «IBAN одержувача», а не два «IBAN».
  • Ролі, крім Audit і Status, всередині [FixedSection] ігноруються.
  • Використовуйте з ними Frame = PrintFrame.None. Фірмовий бланк і рядок параметрів не є частиною нормативно визначеного макета.

Жодна форма KandraWms сьогодні не використовує запасний варіант. Рушій малює секції та фіксовані секції, а його власні тести (SectionsOnlyPrintGeneratorTests і PrintRolesGeneratorTests у Kandra.Tests) є еталоном синтаксису. Вважайте його неперевіреним у конфігурації, доки жодна форма у вашій конфігурації його не використовує, і перевіряйте результат за нормативним зразком.

Який інструмент для якої потреби​

ПотрібноВикористовуйте
Сторони, реквізити, підсумки, підписиРолі й типи, як у будь-якій формі друку
Без блоку компаніїFrame = PrintFrame.None
Абзац вільного тексту під таблицею[PrintRole(PrintRole.Note)]
Твердження про ПДВ, якого не має бутиVatMode = PrintVatMode.None (платіжний документ)
Макет, у якому положення полів визначено нормативно[PrintSection], [FixedSection] і [PrintStyle]

Перевірка​

  1. Відрендеріть форму й подивіться на сторінку. У рамці None немає блоку компанії над заголовком, а форма Forbidden не друкує нічого як чернетку.
  2. Для форми Forbidden надрукуйте чернетку й перевірте відповідь 409 з PrintDraftForbiddenException, а не PDF.
  3. Для форми без таблиці експортуйте CSV. Якщо форма не має ні таблиці, ні застарілих секцій, CSV порожній, тож оголосіть [SupportedExportFormats(ExportFormat.Pdf)] на Dto документа.
  4. Для форми з секціями перевірте положення кожного поля за нормативним макетом, а не лише те, що поле з'явилося.

Дивіться також​