Histórico da Página
Ultizar o seguinte documento como base:
http://tdn.totvs.com/pages/viewpage.action?pageId=181142263
Converter os exemplos para a utilização no JsonSchema
Índice |
---|
<MODIFICAR ABAIXO>
Objetivo
O objetivo da InternalId é permitir trafegar em um só campo a composição da chave das entidades de cada produto. Isto foi necessário, pois várias entidades dos produtos possuem composições de chaves diferentes e não pode ser responsabilidade dos produtos entender a complexidade da composição destas chaves uns dos outros.
Bloco de código | ||
---|---|---|
|
...
Fornecedor no Protheus: empresa + filial + loja + código |
...
Fornecedor no Logix: empresa + código |
Para o Logix o campo Loja do Protheus não faz sentido, portanto não alterará o produto para começar a reconhecer uma loja. Ao mesmo tempo em que Logix só tem campo “empresa” e Protheus trabalha com “empresa + filial”.
...
- Empresa Logix 23 == Empresa Protheus 50|10 ou ou seja, a empresa 23 do Logix corresponde a empresa 50 e filial 10 do Protheus.
...
Por mais que sempre existirão juntas as tags próprias (exemplo: CompanyId e BranchId) e as tags de InternalId (CompanyInternalId) o ideal é que os adapters passem a priorizar o reconhecimento das informações recebidas via InternalId. Ou seja, se a CustomerInternalId recebida em uma mensagem já existe nos valores de de/para do produto, quer dizer que já existe um registro local associado. Sendo assim, é mais fácil extrair da informação “50|123456” o código da empresa e cliente do Logix, do que resolver separadamente os de/para de empresa e de cliente, caso fosse esta a estratégia.
Identificação única de De/Para de InternalId
Atualmente cada produto (ex.: Logix, Datasul, Protheus e RM) tem seu próprio ferramental de de/para, ou seja, conjunto de tabelas e funções que permitem gravar valores de de/para referente a um determinado produto.
Bloco de código | ||||
---|---|---|---|---|
| ||||
Tabela VDP_DPARA_GERAL TABELA_DPARA CHAR(18) CAMPO_DPARA CHAR(150) CARACTER_LOGIX CHAR(150) SISTEMA_INTEGR CHAR(20) CARACTER_INTEGR CHAR(150) |
...
Por isso, ficou definido que deverá existir para cada campo que possua um InternalId, um nome único entre todos os produtos. Este nome será sempre “Nome da Mensagem”.
...
...
- InternalId
...
- para
...
- a
...
- mensagem
...
- City:
...
- City.
...
- InternalId
...
- para
...
- a
...
- mensagem
...
- CustomerVendor:
...
- CustomerVendor.
Informações | ||
---|---|---|
| ||
Este padrão é válido para o retorno do de/para por parte do aplicativo destino. Nos casos em que é necessário trafegar na mensagem um InternalId que representa uma chave estrangeira, o padrão adotado deve ser Nome da Mensagem + "InternalId", conforme descrito mais adiante. |
Assim, tendo os ferramentais de de/para de cada produto uma referência a este nome, será possível no futuro fazer uma rotina de conferência de integridade de de/para entre os produtos.
Múltiplas referências a uma InternalId na mesma mensagem
Vamos supor que a mensagem DeliverySchedule tenha os campos OriginCityCode e DestinationCityCode, consequentemente existirão os campos OriginCityInternalId e DestinationCityInternalId. A existência destes dois campos não significa que haverá dois de/para diferentes, um para Origin e outro para Destination, o de/para deverá ser o mesmo, ou seja, CityInternalId, pois a origem da informação é a mesma.
Para permitir que o ferramental do EAI reconheça que estes dois campos pertencem a uma mesma chave estrangeira City, é necessário incluir uma tag de documentação <InternalIdName>, conforme o exemplo abaixo:
Bloco de código | ||||
---|---|---|---|---|
| ||||
<xs:element name="OriginCityInternalId" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:appinfo>
<InternalIdName>CityInternalId</InternalIdName>
</xs:appinfo>
<xs:documentation>InternalId do OriginCityCode</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DestinationCityInternalId" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:appinfo>
<InternalIdName>CityInternalId</InternalIdName>
</xs:appinfo>
<xs:documentation>InternalId do DestinationCityCode</xs:documentation>
</xs:annotation>
</xs:element> |
Esta mesma regra vale para quando o campo da chave estrangeira, por qualquer motivo, não tem um nome idêntico ao padrão “Mensagem”+InternalId, não necessariamente precisa existir dois campos com a mesma fonte de informação na mesma mensagem.
Bloco de código | ||
---|---|---|
| ||
Mensagem Order
Campos VendorCode e VendorInternalId |
Este campo receberá apenas códigos de fornecedor (Vendor), apesar de que a mensagem de origem da chave seja CustomerVendor.
Desta forma, o campo VendorInternalId na mensagem deve ter declarado o InternalIdName para o valor CustomerVendorInternalId.
Bloco de código | ||||
---|---|---|---|---|
| ||||
<xs:element name="VendorInternalId" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:appinfo>
<InternalIdName>CustomerVendorInternalId</InternalIdName>
</xs:appinfo>
<xs:documentation>InternalId do VendorCode</xs:documentation>
</xs:annotation>
</xs:element> |
O uso do <InternalIdName> somente será necessário nos casos em que o nome do campo da InternalId não respeita exatamente o padrão “Mensagem” + InternalId. Ou seja, se o nome do campo na mensagem for CityInternalId, não é necessário informar o <InternalIdName>.
Utilização na mensagem
A chave interna é utilizada em dois contextos, como chave primária e como chave estrangeira.
Regra para chave primária
Toda mensagem que seja de evento, deverá ter uma tag InternalId dentro de seu <BusinessContent> que irá trafegar a composição de chave da sua entidade. Deve-se ressaltar que mesmo que em um produto a chave de uma entidade não for composta deve-se utilizar a InternalId, pois se está é simples em um produto, mesmo assim poderá ser composta em outro.
Esta tag deverá estar localizada logo abaixo dos campos da mensagem, que fazem parte da sua composição. O exemplo a seguir é um trecho do schema RefundReason_1_000.json.
Esta mesma regra vale para quando o campo da chave estrangeira, por qualquer motivo, não tem um nome idêntico ao padrão “Mensagem”+InternalId, não necessariamente precisa existir dois campos com a mesma fonte de informação na mesma mensagem.
- Mensagem Order
- Campos VendorCode e VendorInternalId
Utilização na mensagem
A chave interna (InternalId) é utilizada em dois contextos, como chave primária e como chave estrangeira.
Regra para chave primária
Toda mensagem que seja de evento, deverá ter uma tag InternalId dentro de seu "BusinessContent" que irá trafegar a composição de chave da sua entidade. Deve-se ressaltar que mesmo que em um produto a chave de uma entidade não for composta deve-se utilizar a InternalId, pois se está é simples em um produto, mesmo assim poderá ser composta em outro.
Esta tag deverá estar localizada logo abaixo dos campos da mensagem, que fazem parte da sua composição. O exemplo a seguir é um trecho do schema RefundReason_1_000.json.
Bloco de código | ||||
---|---|---|---|---|
| ||||
(...)
"Code": {
"description": "Código do Motivo",
"type": "string",
"x-totvs": [
{
"product": "PROTHEUS",
"field": "G8P.G8P_CODIGO",
"required": true,
"type": "Char",
"length": "4",
"note": "",
"available": true,
"canUpdate": false
}
]
},
"InternalId": {
"description": "InternalId do Motivo",
"type": "string",
"x-totvs": [
{
"product": "PROTHEUS",
"field": "cEmpAnt+G8P.G8P_FILIAL+G8P.G8P_CODIGO",
"required": true,
"type": "Char",
"length": "136",
"note": "",
"available": true,
"canUpdate": false
}
]
},
(...) |
Suponhamos que em um cenário tenhamos CompanyId com valor 23 e Code com valor 50. O internalId trafegado seguiria então a seguinte lógica:
Bloco de código | ||
---|---|---|
| ||
"CompanyId": "23"
"Code": "50"
"InternalId": "23|50" |
Toda mensagem de evento (quando for Upsert) deverá conter em seu conteúdo de retorno "ReturnContent" campos de InternalId para armazenar a chave interna do gerador do evento e a chave interna gerada no recebedor do evento. A necessidade disso é para que o recebedor da mensagem de evento e o gerador da mensagem de evento tenham conhecimento da chave interna gerada em cada produto. O fluxo abaixo exemplifica este funcionamento, para o cenário de inclusão de um novo Cliente.
Desta forma, a tag "ReturnContent" da mensagem (ainda utilizando RefundReason_1_000.json como exemplo) seria construída da seguinte forma:
Bloco de código | ||||
---|---|---|---|---|
| ||||
(...)
"ReturnContentType": {
"type": "object",
"properties": {
"ListOfInternalId": {
"type": "array",
"items": {
"$ref": "https://raw.githubusercontent.com/totvs/ttalk-standard-message/master/jsonschema/schemas/types/ListOfInternalId_1_000.json#/definitions/ListOfInternalIdType",
"type": "object"
}
}
}
}
|
O ListOfInternalIdType está definido em um arquivo a parte e está estruturado conforme o exemplo abaixo:
Bloco de código | ||||
---|---|---|---|---|
| ||||
(...)
"definitions": {
"ReturnContentWithModelType": {
"type": "object",
"properties": {
"ListOfInternalId": {
"type": "object",
"$ref": "#/definitions/ListOfInternalIdType"
}
}
},
"ListOfInternalIdType": {
"type": "array",
"items": {
"$ref": "#/definitions/InternalIdType",
"type": "object"
}
},
"InternalIdType": {
"type": "object",
"properties": {
"name": {
"description": "Nome da InternalId, este nome será padronizado entre todas as linhas e corresponderá ao nome da própria transação. Exemplo: City, Item, CustomerVendor. Observação: em outras partes da mensagem, que não sejam a tag ListOfInternalId, a regra pode ser diferente. Para mais informações, consulte http://tdn.totvs.com/pages/viewpage.action?pageId=181142263",
"type": "string"
},
"origin": {
"description": "InternalId da origem",
"type": "string"
},
"destination": {
"description": "InternalId do destino",
"type": "string"
}
}
}
}
(...) |
Como este retorno representa a resposta de uma mensagem enviada, deve-se entender a tag "origin" como a InternalId do produto que enviou a mensagem (a origem), e "destination" como a InternalId do produto que foi o destino da mensagem.
Regra para chave estrangeira
A utilização da InternalId se aplica também para chaves estrangeiras. Porém, neste caso a regra é que para cada tag que representar uma chave estrangeira também exista uma tag InternalId correspondente.
Na mensagem Warehouse_1_002 temos o seguinte trecho de definições:
Bloco de código | ||||
---|---|---|---|---|
| ||||
(...)
"InternalId": {
"description": "InternalId de Integração para o Grupo de Produto",
"type": "string",
"x-totvs": [
{
"product": "PROTHEUS",
"available": true,
"note": "O InternalID do Codigo do Armazém é formado por EMPRESA|NNR_FILIAL|NNR_CODIGO",
"field": "EMPRESA|NNR_FILIAL|NNR_CODIGO",
"length": "50",
"type": "string"
}
]
},
"Code": {
"description": "Código do Local de Estoque (armazém/almoxarifado/depósito)",
"type": "string",
"x-totvs": [
{
"product": "PROTHEUS",
"available": true,
"note": "Pode ter tamanho entre 02 e 06 dependendo da configuração do Protheus",
"field": "NNRXX0.NNR_CODIGO",
"length": "2",
"type": "string",
"required": true
}
]
},
"Description": {
"description": "Descrição do Local de Estoque",
"type": "string",
"x-totvs": [
{
"product": "PROTHEUS",
"available": true,
"note": "Descrição do Local de Estoque(armazém/almoxarifado/depósito)",
"field": "NNRXX0.NNR_DESCRI",
"length": "20",
"type": "string"
}
]
}
(...) |
Já na mensagem Item_4_006 temos o seguinte trecho de definições:
Bloco de código | ||||
---|---|---|---|---|
| ||||
(...)
"Code": {
"description": "Código Item",
"type": "string",
"maxLength": 30,
"x-totvs": [
{
"product": "PROTHEUS",
"field": "SB1.B1_COD",
"required": true,
"type": "char",
"length": "15",
"note": "campo expansivel ate 30",
"available": true,
"canUpdate": false
},
{
| ||||
Bloco de código | ||||
| ||||
(...) "Code": { "description": "Código do Motivo", "type": "string", "x-totvs": [ { "product": "PROTHEUSRM", "field": "G8PTPRODUTO.G8P_CODIGOCODIGOPRD", "required": true, "type": "Charvarchar", "length": "430", "note": """: "Código do produto", "available": true, "canUpdate": false } ] }, "InternalId": { "description": "InternalId do Motivo", "type": "string", "x-totvs": [ { "productdescription": "PROTHEUSInternalId de Integração", "fieldtype": "cEmpAnt+G8P.G8P_FILIAL+G8P.G8P_CODIGOstring", "requiredx-totvs": true,[ "type": "Char", { "lengthproduct": "136PROTHEUS", "notefield": "SB1.B1_FILIAL+SB1.B1_COD", "availablerequired": true, "canUpdatetype": false "char", } "length": "", ] }, (...) |
Suponhamos que em um cenário tenhamos CompanyId com valor 23 e Code com valor 50. O internalId trafegado seguiria então a seguinte lógica:
Bloco de código | ||
---|---|---|
| ||
"CompanyId "note": "23", "Code "available": "50" "InternalId": "23|50" |
...
Desta forma, a tag <ReturnContent> da mensagem (ainda utilizando RefundReason_1_000.json como exemplo) seria construída da seguinte forma:
Bloco de código | ||
---|---|---|
| ||
(...) "ReturnContentType": { "type": "object", "properties": { "ListOfInternalId": { true, "canUpdate": false }, { "product": "RM", "field": ".", "required": true, "type": "varchar", "length": "6", "note": "O InternalID do Produto é formado por TPRODUTO.CODCOLIGADA|TPRODUTO.IDPRD", "available": true, "canUpdate": false } ] }, (...) "StandardWarehouseCode": { "typedescription": "arrayCódigo Depósito Padrão", "itemstype": { "$ref": "https://raw.githubusercontent.com/totvs/ttalk-standard-message/master/jsonschema/schemas/types/ListOfInternalId_1_000.json#/definitions/ListOfInternalIdType", "string", "typemaxLength": "object" 10, } "x-totvs": [ } { } } |
O ListOfInternalIdType está definido em um arquivo a parte e está estruturado conforme o exemplo abaixo:
Bloco de código | ||
---|---|---|
| ||
(...) "definitions": { "ReturnContentWithModelTypeproduct": { "PROTHEUS", "typefield": "objectSB1.B1_LOCPAD", "propertiesrequired": { true, "ListOfInternalIdtype": { "char", "typelength": "object2", "$refnote": "#/definitions/ListOfInternalIdType", } "available": true, } }, "ListOfInternalIdTypecanUpdate": {false "type": "array"}, "items": { "$refproduct": "#/definitions/InternalIdTypeRM", "typefield": "objecttabela.campo", } }, "InternalIdTyperequired": { false, "type": "object", "properties": { "namelength": { "", "description": "Nome da InternalId, este nome será padronizado entre todas as linhas e corresponderá ao nome da própria transação. Exemplo: City, Item, CustomerVendor. Observação: em outras partes da mensagem, que não sejam a tag ListOfInternalId, a regra pode ser diferente. Para mais informações, consulte http://tdn.totvs.com/pages/viewpage.action?pageId=181142263", note": "Não utiliza", "available": true, "canUpdate": false } ] }, "StandardWarehouseInternalId": { "description": "InternalId da chave completa de Depósito Padrão do produto", "type": "string", }, "x-totvs": [ "origin": { "descriptionproduct": "InternalId da origemPROTHEUS", "typefield": "string" NNR.NNR_FILIAL+SB1.B1_LOCPAD", }, "destinationrequired": {false, "descriptiontype": "InternalId do destino", "typelength": "string" , } } } } (...) |
Como este retorno representa a resposta de uma mensagem enviada, deve-se entender a tag "origin" como a InternalId do produto que enviou a mensagem (a origem), e "destination" como a InternalId do produto que foi o destino da mensagem.
Regra para chave estrangeira
A utilização da InternalId se aplica também para chaves estrangeiras. Porém, neste caso a regra é que para cada Tag que representar uma chave estrangeira também exista uma Tag InternalId correspondente.
Bloco de código | ||
---|---|---|
| ||
Mensagem Warehouse
Code
Description
InternalId
Mensagem Item
Code
InternalId
Description
Status
RegisterDate
WarehouseCode
WarehouseInternalId |
Assim a InternalId de chave estrangeira terá o mesmo nome da tag normal – Warehouse para WarehouseCode – mas usando o termo InternalId em vez de Code.
Em qualquer posição que ocorra (sendo chave primária ou estrangeira), a tag InternalId sempre será uma tag type=”xs:string” sem tamanho definido.
Bloco de código | ||||
---|---|---|---|---|
| ||||
<xs:element name="WarehouseInternalId" type="xs:string">
<xs:annotation>
<xs:documentation>InternalId do WarehouseCode</xs:documentation>
</xs:annotation>
</xs:element> |
Para a chave estrangeira o adapter também deverá dar prioridade em resolver o relacionamento pela InternalId. Ou seja, ao receber uma mensagem de Item (seguindo o exemplo), este deve primeiro consultar no seu ferramental de de/para se existe registro correspondente para o WarehouseInternaId recebido, pois este valor já irá fornecer a chave completa correspondente no produto.
Utilização no Adapter
É sabido que cada mensagem única terá um único adapter/versão nos produtos. Com o objetivo de centralizar a regra para composição da chave da InternalId é interessante que cada adapter seja responsável por receber os valores das chaves e concatena-los para a composição da InternalId. Se isto ficar a cargo de cada programa ou adapter que for utilizar o valor, este poderá correr o risco de ser concatenado de formas diferentes em cada ponto em que seja necessário, o que irá prejudicar completamente o funcionamento do recurso.
Ou seja, o adapter da mensagem Item deverá ter um método que recebe os valores da chave e retorne estes concatenados. Ao mesmo tempo que deverá ter um método que recebe o valor da InternalId e retorna o valor correspondente a uma informação da composição desta. Todos os adapters de mensagens que tenham o Item como chave estrangeira utilizarão esta mesma função.
Desta forma, utilizando sempre este mesmo mecanismo onde for necessário, será garantido que o valor será composto sempre da mesma forma e devolvido sempre da mesma forma também.
Exemplo em pseudocódigo (consulte o manual de cada produto para conhecer as funções reais):
Bloco de código | ||
---|---|---|
| ||
Adapter da mensagem Item
AdapterItem.Get_InternalId(cod_empresa, cod_filial, item)
Retorna Empresa + “|” + Filial + “|” + Código
Uso: AdapterItem.Get_InternalId(50,10,123456) = “50|10|123456”
AdapterItem.Get_InternalId_Value(InternalId, Campo)
Retorna <retorna o valor correspondente a posição de “Campo”>
Uso: AdapterItem.Get_InternalId_Value(“50|10|123456”,”cod_empresa”) == “123456” |
...
"note": "",
"available": true,
"canUpdate": false
},
{
"product": "RM",
"field": "tabela.campo",
"required": false,
"type": "",
"length": "",
"note": "Não utiliza",
"available": true,
"canUpdate": false
}
]
},
"StandardWarehouseDescription": {
"description": "Descrição Depósito Padrão",
"type": "string",
"maxLength": 40,
"x-totvs": [
{
"product": "RM",
"field": "tabela.campo",
"required": false,
"type": "",
"length": "",
"note": "Não utiliza",
"available": true,
"canUpdate": false
}
]
},
(...) |
Recomenda-se que a InternalId de chave estrangeira tenha o mesmo nome da tag normal – referenciando o InternalId de Warehouse, seria então WarehouseInternalId. No caso do exemplo acima, o analista optou por chamar de StandardWarehouseInternalId. Não há problema, porém é necessário que as outras definições que fazem referência a Warehouse sigam o mesmo padrão (StandardWarehouseCode e StandardWarehouseDescription).
Em qualquer posição que ocorra (sendo chave primária ou estrangeira), a tag InternalId sempre será uma tag "type":”string” sem tamanho definido.
Para a chave estrangeira o adapter também deverá dar prioridade em resolver o relacionamento pela InternalId. Ou seja, ao receber uma mensagem de Item (seguindo o exemplo), este deve primeiro consultar no seu ferramental de de/para se existe registro correspondente para o WarehouseInternaId recebido, pois este valor já irá fornecer a chave completa correspondente no produto.
Utilização no Adapter
É sabido que cada mensagem única terá um único adapter/versão nos produtos. Com o objetivo de centralizar a regra para composição da chave da InternalId é interessante que cada adapter seja responsável por receber os valores das chaves e concatena-los para a composição da InternalId. Se isto ficar a cargo de cada programa ou adapter que for utilizar o valor, este poderá correr o risco de ser concatenado de formas diferentes em cada ponto em que seja necessário, o que irá prejudicar completamente o funcionamento do recurso.
Ou seja, o adapter da mensagem Item deverá ter um método que recebe os valores da chave e retorne estes concatenados. Ao mesmo tempo que deverá ter um método que recebe o valor da InternalId e retorna o valor correspondente a uma informação da composição desta. Todos os adapters de mensagens que tenham o Item como chave estrangeira utilizarão esta mesma função.
Desta forma, utilizando sempre este mesmo mecanismo onde for necessário, será garantido que o valor será composto sempre da mesma forma e devolvido sempre da mesma forma também.
Bloco de código | ||
---|---|---|
| ||
Adapter da mensagem Item
AdapterItem.Get_InternalId(cod_empresa, cod_filial, item)
Retorna Empresa + “|” + Filial + “|” + Código
Uso: AdapterItem.Get_InternalId(50,10,123456) = “50|10|123456”
AdapterItem.Get_InternalId_Value(InternalId, Campo)
Retorna <retorna o valor correspondente a posição de “Campo”>
Uso: AdapterItem.Get_InternalId_Value(“50|10|123456”,”cod_empresa”) == “123456” |
...