Páginas filhas
  • Chave Interna (InternalId)

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

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

...

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
titleExemplo

...

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    10  ou seja, a empresa 23 do Logix corresponde a empresa 50 e filial 10 do Protheus.

...

Ao compor o valor da InternalId o caracter “|” (pipe) sempre deve é recomendado a ser utilizado como separador.

...

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
titleExemplo Logix
collapsetrue
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”.

...

titleExemplo

...

  • InternalId

...

  • para

...

  • a

...

  • mensagem

...

  • City:

...

  • City.

...

  • InternalId

...

  • para

...

  • a

...

  • mensagem

...

  • CustomerVendor:

...

  • CustomerVendor.
Informações
titleImportante

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
titleExemplo
collapsetrue
<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
titleExemplo
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
titleExemplo
collapsetrue
<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.

Bloco de código
titleExemplo
<xs:element name="Code" type="xs:string" minOccurs="0" maxOccurs="1">
	<xs:annotation>
		<xs:documentation>Codigo do departamento</xs:documentation>
	</xs:annotation>
</xs:element>
<xs:element name="InternalId" type="xs:string" minOccurs="0" maxOccurs="1">
	<xs:annotation>
		<xs:documentation>InternalId do Code</xs:documentation>
	</xs:documentation>
</xs:element>

Em XML

Bloco de código
titleExemplo
<Code>123</Code>


<InternalId>23|123</InternalId>

     

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.

Image Removed

Desta forma, na tag <ReturnContent> da mensagem as seguintes tags deverão ser criadas:

Bloco de código
titleExemplo
<xs:include schemaLocation="../types/ListOfInternalId_1_000.xsd"/>


[...]


<xs:element name="ReturnContent" substitutionGroup="AbstractReturnContent">
	<xs:complexType>
		<xs:sequence>
			<xs:element name="ListOfInternalId" type="ListOfInternalIdType" maxOccurs="1" minOccurs="0"/>
		</xs:sequence>
	</xs:complexType>
</xs:element>

ListOfInternalIdType está definido em um arquivo a parte ../types/ListOfInternalId_1_000.xsd e está estruturado conforme o exemplo abaixo.

Em XML

Bloco de código
titleExemplo
<ReturnContent>
	<ListOfInternalId>
		<InternalId>
			<Name>[NomeDaMensagem]</Name> <!-- Considerando a InternalId da mensagem (PK) -->
			<Origin>01|123</Origin>
			<Destination>55|11|ABC</Destination>
		</InternalId>
	</ListOfInternalId>
</ReturnContent>

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
titleExemplo
Mensagem Warehouse


Code


Description






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.

...

titleExemplo
collapsetrue

...

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
titleRefundReason_1_000
collapsetrue
(...)
				"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
titleExemplo
"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.

Image Added

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
titleExemplo
collapsetrue
(...)
		"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"
					}
				}
			}
		}

ListOfInternalIdType está definido em um arquivo a parte e está estruturado conforme o exemplo abaixo:

Bloco de código
titleTrecho do ListOfInternalIdType
collapsetrue
(...)
"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
titleWarehouse 1_002
collapsetrue
(...) 
				"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
titleItem_4_006
collapsetrue
(...)
                "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
						},
						{
							"product": "RM",
							"field": "TPRODUTO.CODIGOPRD",
							"required": true,
							"type": "varchar",
							"length": "30",
							"note": "Código do produto",
							"available": true,
							"canUpdate": false
						}
					]
				},
				"InternalId": {
					"description": "InternalId de Integração",
					"type": "string",
					"x-totvs": [
						{
							"product": "PROTHEUS",
							"field": "SB1.B1_FILIAL+SB1.B1_COD",
							"required": true,
							"type": "char",
							"length": "",
							"note": "",
							"available": 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": {
					"description": "Código Depósito Padrão",
					"type": "string",
					"maxLength": 10,
					"x-totvs": [
						{
							"product": "PROTHEUS",
							"field": "SB1.B1_LOCPAD",
							"required": true,
							"type": "char",
							"length": "2",
							"note": "",
							"available": true,
							"canUpdate": false
						},
						{
							"product": "RM",
							"field": "tabela.campo",
							"required": false,
							"type": "",
							"length": "",
							"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": [
						{
							"product": "PROTHEUS",
							"field": "NNR.NNR_FILIAL+SB1.B1_LOCPAD",
							"required": false,
							"type": "",
							"length": "",
							"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.

...

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
titleExemplo (em pseudocó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”

...