# 03 · Levando mudanças para outro ambiente

Fonte: https://muriloht.com/idempiere/03-migracao (Guia iDempiere, Murilo H. Torquato). Versão alvo: iDempiere 14.

Objetivos:
- Saber onde ficam os scripts de migração que o iDempiere gerou para você
- Entender por que scripts SQL não bastam para distribuir uma customização
- Empacotar o projeto num 2Pack com Pack Out
- Aplicar um 2Pack em outro ambiente com Pack In

## Antes de começar

- Módulos 01 e 02 concluídos: a janela **Visita Técnica** funciona, com as lógicas do módulo 02.
- **Log Migration Script** ligado desde o módulo 01. Se você esqueceu, este módulo ainda funciona: o 2Pack não depende dos scripts.

## O que você vai construir

Até aqui, tudo o que você fez existe **só no seu banco**. Se o banco for apagado, o trabalho some. Se outra pessoa quiser rodar a sua janela, não tem como.

Neste módulo você empacota o projeto das visitas num arquivo que qualquer outro iDempiere consegue instalar. Esse arquivo vai para o Git junto com o seu código e, no módulo 04, passa a ser instalado sozinho quando o plugin sobe.

## Duas formas de levar mudanças

| | Script de migração (SQL) | 2Pack (zip) |
|---|---|---|
| O que é | Os `INSERT` e `UPDATE` que você fez no Dicionário, em ordem | Os objetos do Dicionário descritos em XML, com suas dependências |
| Como identifica registros | Pelo **ID numérico** | Pelo **UUID** |
| Quem usa | O próprio core do iDempiere, em `migration/` | Plugins e customizações |
| Funciona em outro banco? | Só se os IDs não colidirem | Sim, os IDs são reatribuídos na chegada |

A diferença está na segunda linha. Um script diz "insira a coluna de ID 1000123". Se o banco de destino já tem um registro com esse ID (outra customização, outro plugin), o script falha ou, pior, sobrescreve o que não devia. O 2Pack diz "insira a coluna com UUID `a3f9...`" e deixa o banco de destino escolher o ID.

Regra prática: **scripts são o seu histórico; o 2Pack é o que você distribui.**

## Passo a passo

### 1. Encontre os seus scripts

Rodando pelo Eclipse, o iDempiere grava os scripts dentro do repositório do core:

```bash
ls ~/sources/idempiere/migration/iD14/postgresql/ | grep PlaceholderForTicket
```

Cada arquivo tem um nome como `202609231530_PlaceholderForTicket.sql`. Abra um:

```sql
SELECT register_migration_script('202609231530_PlaceholderForTicket.sql') FROM dual;

-- Sep 23, 2026, 3:30:12 PM BRT
INSERT INTO AD_Element (AD_Element_ID, ColumnName, EntityType, Name, ...)
VALUES (1000123, 'EDU_DataVisita', 'EDU', 'Data da Visita', ...);
```

Repare nos dois detalhes: a primeira linha registra o script na tabela `AD_MigrationScript` (é assim que o iDempiere sabe o que já foi aplicado), e os IDs estão escritos como números fixos.

### 2. Tire os scripts do repositório do core

Esses arquivos **não são do core**. Deixá-los em `migration/` tem dois problemas: o `git status` do iDempiere fica sujo, e o `RUN_SyncDBDev.sh` vai tentar aplicá-los em qualquer banco que você sincronizar.

Crie uma pasta para o seu projeto e mova os scripts para lá:

```bash
mkdir -p ~/sources/guia-idempiere-projeto/migration
mv ~/sources/idempiere/migration/iD14/postgresql/*_PlaceholderForTicket.sql \
   ~/sources/guia-idempiere-projeto/migration/
```

Essa pasta vira o repositório Git do projeto. No módulo 04 o plugin mora nela.

### 3. Crie o pacote com Pack Out

**Conceito:** o **Pack Out** exporta objetos do Dicionário para um zip. Você lista o que quer levar, e o iDempiere descreve cada objeto em XML.

Entre como **System**, abra a janela **Pack Out** e crie o cabeçalho:

| Campo | Valor |
|---|---|
| Name of Package | `EDU_Visitas` |
| Package Version | `1.0.0` |
| Export Dictionary Entity | marcado |

Na aba de detalhes, adicione uma linha para cada objeto, **nesta ordem**:

| Seq | Type | Objeto |
|---|---|---|
| 10 | Entity Type | `EDU` |
| 20 | Reference | `EDU_TipoVisita` |
| 30 | Dynamic Validation Rule | `EDU - Somente Clientes` |
| 40 | Table | `EDU_Visita` |
| 50 | Table | `EDU_VisitaLinha` |
| 60 | Window | `Visita Técnica` |
| 70 | Application or Module | o item de menu `Visita Técnica` |

A ordem importa: na chegada, os objetos são criados de cima para baixo. A tabela precisa do tipo de entidade e da referência; a janela precisa das tabelas; o menu precisa da janela.

> **Application or Module** é o tipo usado para itens de **menu**. O nome engana.

Salve e clique em **Export Package**, no cabeçalho. O iDempiere pergunta o **Export Format**: deixe `XML`.

O iDempiere gera `EDU_Visitas.zip`. Dentro dele:

```
EDU_Visitas/
└── dict/
    └── PackOut.xml
```

Abra o `PackOut.xml` e procure suas tabelas, suas colunas e a janela. Cada registro aparece com o seu UUID (`EDU_Visita_UU`, `AD_Column_UU`...), não com o ID.

### 4. Guarde o pacote no projeto

Copie o zip para o projeto, com o nome que o módulo 04 vai usar:

```bash
mkdir -p ~/sources/guia-idempiere-projeto/2pack
cp EDU_Visitas.zip ~/sources/guia-idempiere-projeto/2pack/2Pack_1.0.0.zip
```

O `1.0.0` no nome é a versão do pacote. Quando você mudar algo no Dicionário, gera o pacote de novo como `2Pack_1.0.1.zip`, e assim por diante. Nunca altere um pacote já distribuído.

Agora inicie o Git no projeto e faça o primeiro commit:

```bash
cd ~/sources/guia-idempiere-projeto
git init
git add .
git commit -m "EDU Visitas 1.0.0: tabelas, janela e menu"
```

### 5. Aplique em outro ambiente com Pack In

No ambiente de destino (o iDempiere de um colega ou um segundo banco importado como no módulo 00):

1. Entre como **System** e abra a janela **Pack In**.
2. Crie um registro, anexe o `2Pack_1.0.0.zip` e salve.
3. Clique no botão **PackIn**.
4. Confira o resultado na aba **Package Installation** da própria janela: o pacote aparece com o status da importação.
5. Entre como GardenAdmin, rode **Role Access Update** e abra a janela **Visita Técnica**.

O Pack In cria as tabelas e colunas no banco, sem precisar de **Synchronize Column**.

Se não tiver um segundo ambiente agora, pule este passo. No módulo 04 o pacote passa a ser aplicado automaticamente, e você testa por lá.

## Checkpoint

- [ ] Encontrei os scripts gerados desde o módulo 01 e tirei do repositório do core
- [ ] Sei explicar por que um script SQL pode quebrar em outro banco
- [ ] Gerei o zip do pacote EDU Visitas com Pack Out
- [ ] Abri o zip e encontrei o PackOut.xml com as minhas tabelas e a janela
- [ ] Sei aplicar o pacote em outro ambiente com Pack In

## Erros comuns

| Sintoma | Causa provável | Correção |
|---|---|---|
| Pack In falha dizendo que a entidade `EDU` não existe | O Entity Type não foi incluído ou veio depois da tabela | Inclua o Entity Type como primeiro item |
| A janela chega, mas a lista do Tipo de Visita vem vazia | A referência não foi incluída no pacote | Inclua a Reference antes das tabelas |
| O menu não aparece no destino | Falta o item **Application or Module**, ou falta rodar **Role Access Update** | Inclua o menu e rode o processo como GardenAdmin |
| Script SQL falha com `duplicate key` em outro banco | O ID numérico já existe no destino | Use o 2Pack para distribuir; scripts são histórico |
| O `RUN_SyncDBDev.sh` aplica scripts que não são do core | Os scripts ficaram em `migration/` do repositório do iDempiere | Mova-os para a pasta do seu projeto |

## Desafio

Faça uma mudança pequena no Dicionário (por exemplo, a Display Logic do desafio do módulo 02), gere o pacote de novo como **1.0.1** e compare o `PackOut.xml` das duas versões com `diff`. Quais objetos mudaram?

## Referência

### Tipos de item no Pack Out

| Type | Leva |
|---|---|
| Entity Type | O tipo de entidade |
| Table | A tabela e suas colunas |
| Window | A janela, suas abas e seus campos |
| Reference | Uma referência (lista ou tabela) |
| Dynamic Validation Rule | Uma regra de validação |
| Process/Report | Um processo ou relatório e seus parâmetros |
| Application or Module | Um item de menu |
| Info Window | Uma janela de informação |
| Message | Uma mensagem |
| Form | Um formulário |
| Workflow | Um fluxo de trabalho |
| PrintFormat | Um formato de impressão |
| Role | Um perfil e seus acessos |
| Data | Registros de dados de uma tabela (não é Dicionário) |
| SQL Statement | Um comando SQL executado na chegada |

### Aplicando vários pacotes pelo terminal

No servidor, o script `utils/RUN_ApplyPackInFromFolder.sh <pasta>` aplica todos os pacotes de uma pasta, sem abrir a interface. É útil em deploy automatizado.

### Formatos

O Pack Out do iDempiere 14 exporta em **XML**, **JSON** ou **YAML** (parâmetro **Export Format**, perguntado ao clicar em **Export Package**). XML é o padrão e o mais usado pela comunidade.

## Próximo módulo

[04 · Plugin e classe de modelo](https://muriloht.com/idempiere/04-plugin-e-modelo): o pacote vai para dentro de um plugin OSGi, é aplicado sozinho quando o plugin sobe, e as suas tabelas ganham classes Java.
