Перейти к основному содержимому

Как добавить новый тип документа

Обзор шагов

  1. Добавить значение в DocumentType enum
  2. Создать Entity-класс (наследник BaseDoc)
  3. Создать Entity-класс позиции (наследник BaseDocItem) — если нужны позиции
  4. Создать enum статусов
  5. Создать Repository
  6. Создать EntityService (наследник BaseDocEntityService)
  7. Создать Manager (наследник BaseDocManager) — обязательно
  8. Создать ListTemplate и EditTemplate
  9. Создать миграцию Liquibase
  10. Добавить локализацию
  11. Зарегистрировать в контроллере

Шаг 1: DocumentType

Добавить значение в model.enums.document.DocumentType:

MY_NEW_DOC("my_new_doc")

Шаг 2: Entity-класс

Создать model.entity.document.MyNewDoc:

@Entity
@Table(name = "wms_doc_my_new")
@Audited
public class MyNewDoc extends BaseDoc<MyNewDocStatus> {

// Дополнительные поля
@ManyToOne(fetch = FetchType.LAZY)
private SomeEntity someReference;

@Override
public DocumentType getDocumentType() {
return DocumentType.MY_NEW_DOC;
}
}

Ключевые моменты:

  • Наследование от BaseDoc<S> где S — enum статусов
  • @Entity + @Table — своя таблица с префиксом wms_doc_
  • @Audited — аудит через Envers
  • Переопределить getDocumentType()

Шаг 3: Позиции (опционально)

@Entity
@Table(name = "wms_doc_my_new_item")
@Audited
public class MyNewDocItem extends BaseDocItem<MyNewDoc, MyNewDocStatus> {
// Дополнительные поля позиции
}

Шаг 4: Enum статусов

public enum MyNewDocStatus {
NEW,
IN_PROGRESS,
DONE,
CANCELED
}

Шаг 5: Repository

public interface MyNewDocRepository extends JpaRepository<MyNewDoc, UUID>,
JpaSpecificationExecutor<MyNewDoc> {
}

Шаг 6: EntityService

@Service
public class MyNewDocEntityService extends BaseDocEntityService<MyNewDoc, MyNewDocStatus, MyNewDocRepository> {
// save и delete заблокированы автоматически
// Добавить кастомные методы поиска при необходимости
}

Шаг 7: Manager (критически важный)

@Service
public class MyNewDocManager extends BaseDocManager<MyNewDoc, MyNewDocStatus, MyNewDocEntityService> {

@Override
protected void beforeSave(MyNewDoc doc, Set<ProcessingModifier> modifiers) {
// Подготовка данных
}

@Override
protected void validate(MyNewDoc doc) {
// Бизнес-валидация
if (doc.getSomeReference() == null) {
throw new DocumentValidationException("some.reference.required");
}
}

@Override
protected void validateItems(MyNewDoc doc) {
// Валидация позиций
}

@Override
protected boolean additionalBusinessLogic(MyNewDoc doc, Map<String, Object> changedFields) {
// Доп. логика. Вернуть true если документ изменился и нужно повторное сохранение
return false;
}

@Override
protected BaseDocSavedEvent<MyNewDoc, MyNewDocStatus> createDocSavedEvent(MyNewDoc doc) {
return new MyNewDocSavedEvent(doc, getChangedFields());
}
}

Менеджер автоматически регистрируется в DocManager при старте приложения.

Шаг 8: Templates

ListTemplate

@Service
public class MyNewDocListTemplate extends BaseDocListTemplate<MyNewDocManager, MyNewDocEntityService, MyNewDoc, MyNewDocStatus> {

public MyNewDocListTemplate(/* DI */) {
super(/* привилегии */);

// Колонки
columns.put(10, ListColumn.<MyNewDoc>builder().fieldName("someReference").build());

// Фильтры
filters.put(10, ListFilterParam.<MyNewDoc>builder().fieldName("someReference").build());
}
}

EditTemplate

@Service
public class MyNewDocEditTemplate extends BaseDocEditTemplate<MyNewDocManager, MyNewDocEntityService, MyNewDoc, MyNewDocStatus> {

public MyNewDocEditTemplate(/* DI */) {
super(/* привилегии */);

// Поля
fields.put(10, EditFormField.<MyNewDoc>builder().fieldName("someReference").build());

// Таблица позиций (опционально)
tables.put(1, EditFormTable.<MyNewDoc, MyNewDocItem>builder()
.fieldName("items")
.columns(Map.of(
1, EditFormTableColumn.<MyNewDoc, MyNewDocItem>builder()
.fieldName("quantity").build()
))
.build());
}
}

Шаг 9: Миграция Liquibase

Формат XML, id в формате <timestamp13>-<порядковый номер>:

<changeSet id="1711526400000-1" author="developer">
<createTable tableName="wms_doc_my_new">
<column name="id" type="uuid">
<constraints primaryKey="true"/>
</column>
<column name="some_reference_id" type="uuid"/>
</createTable>
<addForeignKeyConstraint
constraintName="fk_my_new_doc_some_reference"
baseTableName="wms_doc_my_new"
baseColumnNames="some_reference_id"
referencedTableName="wms_some_reference"
referencedColumnNames="id"/>
</changeSet>

Правила:

  • XML формат (SQL только для сложных случаев)
  • constraintName по образцу из src/main/resources/db/create.sql
  • Названия индексов должны совпадать с Hibernate-генерированными

Шаг 10: Локализация

# entities_ru.properties
MyNewDoc.someReference=Ссылка

# enums_ru.properties
MyNewDocStatus.NEW=Новый
MyNewDocStatus.IN_PROGRESS=В работе
MyNewDocStatus.DONE=Завершён
MyNewDocStatus.CANCELED=Отменён

# messages_ru.properties (ошибки)
some.reference.required=Необходимо указать ссылку

Значения отсортированы по ключу (case-insensitive).

Шаг 11: Регистрация в контроллере

Добавить case в DocumentController для маршрутизации по DocumentType.MY_NEW_DOC.