diff --git a/Lançamento de documentos/.project b/Lançamento de documentos/.project new file mode 100644 index 0000000..95f01ce --- /dev/null +++ b/Lançamento de documentos/.project @@ -0,0 +1,11 @@ + + + Lançamento de documentos + + + + + + + + diff --git a/Lançamento de documentos/lançamentos/__pycache__/portalfornecedor_proxy_example.cpython-313.pyc b/Lançamento de documentos/lançamentos/__pycache__/portalfornecedor_proxy_example.cpython-313.pyc new file mode 100644 index 0000000..09d9e40 Binary files /dev/null and b/Lançamento de documentos/lançamentos/__pycache__/portalfornecedor_proxy_example.cpython-313.pyc differ diff --git a/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorNF.js b/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorNF.js deleted file mode 100644 index 407dd52..0000000 --- a/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorNF.js +++ /dev/null @@ -1,42 +0,0 @@ -function createDataset(fields, constraints, sortFields) { - var dataset = DatasetBuilder.newDataset(); - dataset.addColumn("STATUS"); - dataset.addColumn("CAMPO"); - dataset.addColumn("VALOR"); - - try { - dataset.addRow(["DEBUG", "__version__", "dsPortalFornecedorNF_debug_2026_03_12"]); - - if (!constraints || constraints.length === 0) { - dataset.addRow(["DEBUG", "__constraints__", "vazio"]); - return dataset; - } - - for (var i = 0; i < constraints.length; i++) { - var c = constraints[i]; - var fieldName = getFieldNameSafe(c); - var initialValue = getInitialValueSafe(c); - dataset.addRow(["DEBUG", fieldName, initialValue]); - } - - return dataset; - } catch (e) { - var erro = (e && e.message) ? e.message : ("" + e); - dataset.addRow(["ERRO", "__exception__", erro]); - return dataset; - } -} - -function getFieldNameSafe(c) { - if (!c) return ""; - if (typeof c.getFieldName === "function") return (c.getFieldName() || "") + ""; - if (c.fieldName !== undefined && c.fieldName !== null) return (c.fieldName || "") + ""; - return ""; -} - -function getInitialValueSafe(c) { - if (!c) return ""; - if (typeof c.getInitialValue === "function") return (c.getInitialValue() || "") + ""; - if (c.initialValue !== undefined && c.initialValue !== null) return (c.initialValue || "") + ""; - return ""; -} diff --git a/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorStartProcess.js b/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorStartProcess.js new file mode 100644 index 0000000..30fa6e9 --- /dev/null +++ b/Lançamento de documentos/lançamentos/datasets/dsPortalFornecedorStartProcess.js @@ -0,0 +1,174 @@ +function createDataset(fields, constraints, sortFields) { + var dataset = DatasetBuilder.newDataset(); + dataset.addColumn("STATUS"); + dataset.addColumn("MESSAGE"); + dataset.addColumn("PROCESS_INSTANCE_ID"); + dataset.addColumn("RAW_RESPONSE"); + + try { + var params = constraintsToMap(constraints); + validateRequired(params); + + var payload = { + targetState: parseInt(params.targetState || "5", 10), + comment: params.comment || "Envio via portal fornecedor", + formFields: { + data_abertura: valueOrEmpty(params.data_abertura), + emitido_por: valueOrEmpty(params.emitido_por), + entidade_responsavel: valueOrEmpty(params.entidade_responsavel), + tipo_cadastro: valueOrEmpty(params.tipo_cadastro), + emailSolicitante: valueOrEmpty(params.emailSolicitante), + cpf: valueOrEmpty(params.cpf), + tipo_documento: valueOrEmpty(params.tipo_documento), + numero_documento: valueOrEmpty(params.numero_documento), + valor: valueOrEmpty(params.valor), + justificativa: valueOrEmpty(params.justificativa) + } + }; + + var clientService = fluigAPI.getAuthorizeClientService(); + var requestData = { + companyId: getCompanyId(), + serviceCode: "fluig_rest", + endpoint: "/process-management/api/v2/processes/FlowEssentials_LancamentodeDocumento/start", + method: "post", + timeoutService: "100", + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + }, + options: { + encoding: "UTF-8", + mediaType: "application/json", + useSSL: true + }, + params: payload + }; + + var vo = clientService.invoke(JSON.stringify(requestData)); + var raw = vo ? String(vo.getResult() || "") : ""; + if (!raw) { + throw "fluig_rest retornou vazio."; + } + + var response = parseJsonSafe(raw); + var processInstanceId = extractProcessInstanceId(response); + var responseMessage = extractResponseMessage(response); + + if (isErrorResponse(response, raw)) { + dataset.addRow(["ERROR", responseMessage || "Falha ao iniciar processo.", processInstanceId, raw]); + return dataset; + } + + dataset.addRow(["OK", responseMessage || "Solicitação enviada com sucesso.", processInstanceId, raw]); + return dataset; + } catch (e) { + dataset.addRow(["ERROR", errorMessage(e), "", ""]); + return dataset; + } +} + +function constraintsToMap(constraints) { + var map = {}; + if (!constraints) { + return map; + } + + for (var i = 0; i < constraints.length; i++) { + var c = constraints[i]; + var fieldName = getFieldNameSafe(c); + if (!fieldName) { + continue; + } + map[fieldName] = getInitialValueSafe(c); + } + + return map; +} + +function validateRequired(params) { + var requiredFields = [ + "data_abertura", + "emitido_por", + "entidade_responsavel", + "tipo_cadastro", + "cpf", + "numero_documento", + "justificativa" + ]; + + for (var i = 0; i < requiredFields.length; i++) { + var fieldName = requiredFields[i]; + if (!valueOrEmpty(params[fieldName])) { + throw "Campo obrigatório não informado: " + fieldName; + } + } +} + +function extractProcessInstanceId(response) { + if (!response) return ""; + if (response.processInstanceId) return String(response.processInstanceId); + if (response.content && response.content.processInstanceId) return String(response.content.processInstanceId); + if (response.content && response.content.processInstanceid) return String(response.content.processInstanceid); + if (response.content && response.content.requestNumber) return String(response.content.requestNumber); + return ""; +} + +function extractResponseMessage(response) { + if (!response) return ""; + if (response.message) return String(response.message); + if (response.detailedMessage) return String(response.detailedMessage); + if (response.content && response.content.message) return String(response.content.message); + return ""; +} + +function isErrorResponse(response, raw) { + if (!response) return false; + if (response.code && !extractProcessInstanceId(response)) return true; + if (response.message && String(response.message).toLowerCase().indexOf("erro") >= 0 && !extractProcessInstanceId(response)) return true; + if (raw && raw.indexOf("\"code\"") >= 0 && !extractProcessInstanceId(response)) return true; + return false; +} + +function parseJsonSafe(value) { + try { + return JSON.parse(value); + } catch (e) { + return { raw: value }; + } +} + +function getCompanyId() { + try { + if (typeof getValue === "function") { + return String(getValue("WKCompany") || "1"); + } + } catch (e) { + // ignore + } + + return "1"; +} + +function valueOrEmpty(value) { + return value == null ? "" : String(value); +} + +function errorMessage(e) { + if (e && e.message) return String(e.message); + return String(e); +} + +function getFieldNameSafe(c) { + if (!c) return ""; + if (typeof c.getFieldName === "function") return String(c.getFieldName() || ""); + if (c.fieldName !== undefined && c.fieldName !== null) return String(c.fieldName || ""); + return ""; +} + +function getInitialValueSafe(c) { + if (!c) return ""; + if (typeof c.getInitialValue === "function") return String(c.getInitialValue() || ""); + if (c.initialValue !== undefined && c.initialValue !== null) return String(c.initialValue || ""); + return ""; +} diff --git a/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/.metadata b/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/.metadata index 9eca6c2..ab065a3 100644 Binary files a/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/.metadata and b/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/.metadata differ diff --git a/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/totvsflow_lancamento_documento.html b/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/totvsflow_lancamento_documento.html index 701035a..62ad23e 100644 --- a/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/totvsflow_lancamento_documento.html +++ b/Lançamento de documentos/lançamentos/forms/41254 - totvsflow_lancamento_documento/totvsflow_lancamento_documento.html @@ -1,4 +1,4 @@ - + @@ -79,7 +79,7 @@ -

Lançamento de documento

+

Lançamento de documento

@@ -90,7 +90,7 @@

 Dados do documento 

-
Dados referentes ao documento que será lançado.
+
Dados referentes ao documento que será lançado.

@@ -117,7 +117,7 @@
- + *
- + Tipo de cadastro * + obrigatório

* @@ -176,7 +176,7 @@ mask="00.000.000/0000-00" class="form-control" readonly /> + obrigatório

@@ -199,7 +199,7 @@ />

@@ -208,7 +208,7 @@ @@ -219,13 +219,13 @@
- * + * + placeholder="Inserir número do documento" class="form-control" /> + obrigatório

@@ -245,8 +245,8 @@ * -

Utilize a aba anexos para anexar o documento ou o botão - abaixo. Anexo obrigatório.

+

Utilize a aba anexos para anexar o documento ou o botão + abaixo. Anexo obrigatório.

@@ -258,21 +258,21 @@

 Descrição dos serviços +  Descrição dos serviços  

-
Descrição detalhada dos serviços prestados e as possíveis informações complementares.
+
Descrição detalhada dos serviços prestados e as possíveis informações complementares.

- Descrição dos serviços * + placeholder="Descreva os serviços prestados."> + obrigatório

@@ -288,9 +288,9 @@ @@ -416,17 +416,17 @@
@@ -436,12 +436,12 @@ -
-
-
-
-
-
- - + obrigatório

@@ -479,7 +469,7 @@

-
Todos os campos com * são de preenchimento obrigatório.
+
Todos os campos com * são de preenchimento obrigatório.
@@ -1131,8 +1121,8 @@ function msgModal(size) { FLUIGC.modal({ - title: "Atenção", - content: "Existem campos que estão preenchidos incorretamente e/ou não foram preenchidos. Confira e realize as correções nos campos indicados.", + title: "Atenção", + content: "Existem campos que estão preenchidos incorretamente e/ou não foram preenchidos. Confira e realize as correções nos campos indicados.", id: 'fluig-modal', size: size, actions: [{ @@ -1156,7 +1146,7 @@ document.getElementById("mensagemErroDiasExpiracao").textContent = ""; } else { // O valor não é um número positivo, exibe mensagem de erro - document.getElementById("mensagemErroDiasExpiracao").textContent = "Digite um número positivo válido."; + document.getElementById("mensagemErroDiasExpiracao").textContent = "Digite um número positivo válido."; } } diff --git a/Lançamento de documentos/lançamentos/portalfornecedor_endpoint_contract.md b/Lançamento de documentos/lançamentos/portalfornecedor_endpoint_contract.md new file mode 100644 index 0000000..f6bd8d9 --- /dev/null +++ b/Lançamento de documentos/lançamentos/portalfornecedor_endpoint_contract.md @@ -0,0 +1,117 @@ +# Portal Fornecedor - contrato do endpoint intermediario + +## Objetivo + +A widget publica nao deve chamar diretamente: + +- `/process-management/api/v2/processes/FlowEssentials_LancamentodeDocumento/start` +- `/api/public/ecm/dataset/datasets` + +Ela deve chamar um endpoint intermediario no servidor, por exemplo: + +- `POST /api/public/portalfornecedor/enviar` + +Esse endpoint e quem usa `fluig_rest` no backend. + +## Request esperado da widget + +```json +{ + "targetState": 5, + "comment": "Envio via portal fornecedor", + "formFields": { + "data_abertura": "2026-03-13", + "emitido_por": "fornecedor", + "entidade_responsavel": "Empresa X", + "tipo_cadastro": "cpf", + "emailSolicitante": "email@empresa.com.br", + "cpf": "12345678900", + "tipo_documento": "danfe", + "numero_documento": "123456", + "valor": "10,00", + "justificativa": "Descricao do servico" + } +} +``` + +## Response de sucesso + +```json +{ + "success": true, + "message": "Solicitacao enviada com sucesso.", + "processInstanceId": "12345", + "content": { + "processInstanceId": "12345" + } +} +``` + +## Response de erro + +```json +{ + "success": false, + "message": "Descricao do erro" +} +``` + +## Logica esperada no backend + +1. Receber o JSON da widget. +2. Validar os campos obrigatorios. +3. Usar `fluigAPI.getAuthorizeClientService()`. +4. Invocar o servico `fluig_rest`. +5. Chamar o endpoint final: + `/process-management/api/v2/processes/FlowEssentials_LancamentodeDocumento/start` +6. Retornar para a widget somente o resultado final. + +## Exemplo de chamada server-side + +```javascript +var clientService = fluigAPI.getAuthorizeClientService(); + +var requestData = { + companyId: String(getValue("WKCompany") || "1"), + serviceCode: "fluig_rest", + endpoint: "/process-management/api/v2/processes/FlowEssentials_LancamentodeDocumento/start", + method: "post", + timeoutService: "100", + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + }, + options: { + encoding: "UTF-8", + mediaType: "application/json", + useSSL: true + }, + params: payloadRecebidoDaWidget +}; + +var vo = clientService.invoke(JSON.stringify(requestData)); +var raw = String(vo.getResult() || ""); +var response = JSON.parse(raw); +``` + +## Configuracao da widget + +Por padrao a widget usa: + +- `https://api.grupoginseng.com.br/v2/portalfornecedor/enviar_api_public_portalfornecedor_enviar_post` + +Se necessario, sobrescreva antes de carregar a widget: + +```html + +``` + +## Observacao importante + +`apiKey` no front nao substitui OAuth 1.0 do Fluig. Se existir um header como `apiKey`, +ele deve ser validado apenas no endpoint intermediario. O endpoint intermediario continua +sendo o responsavel por usar `fluig_rest` no servidor. diff --git a/Lançamento de documentos/lançamentos/portalfornecedor_proxy.env.example b/Lançamento de documentos/lançamentos/portalfornecedor_proxy.env.example new file mode 100644 index 0000000..1929f22 --- /dev/null +++ b/Lançamento de documentos/lançamentos/portalfornecedor_proxy.env.example @@ -0,0 +1,9 @@ +PORTAL_FORNECEDOR_BASE_URL=https://comerciode188006.fluig.cloudtotvs.com.br +PORTAL_FORNECEDOR_PROCESS_ID=FlowEssentials_LancamentodeDocumento +PORTAL_FORNECEDOR_CLIENT_KEY=your_consumer_key +PORTAL_FORNECEDOR_CLIENT_SECRET=your_consumer_secret +PORTAL_FORNECEDOR_RESOURCE_OWNER_KEY=your_access_token +PORTAL_FORNECEDOR_RESOURCE_OWNER_SECRET=your_token_secret +PORTAL_FORNECEDOR_COMPANY_ID=1 +PORTAL_FORNECEDOR_PARENT_FOLDER_ID=10 +PORTAL_FORNECEDOR_CORS_ORIGINS=https://comerciode188006.fluig.cloudtotvs.com.br diff --git a/Lançamento de documentos/lançamentos/portalfornecedor_proxy_example.py b/Lançamento de documentos/lançamentos/portalfornecedor_proxy_example.py new file mode 100644 index 0000000..025c31f --- /dev/null +++ b/Lançamento de documentos/lançamentos/portalfornecedor_proxy_example.py @@ -0,0 +1,214 @@ +from __future__ import annotations + +import os +from typing import Any + +import requests +from fastapi import FastAPI, File, Form, HTTPException, UploadFile +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from requests_oauthlib import OAuth1 + + +class PortalFornecedorSuccessResponse(BaseModel): + success: bool = True + message: str + processInstanceId: str = "" + content: dict[str, Any] + + +class PortalFornecedorErrorDetail(BaseModel): + message: str | None = None + + +def env(name: str, default: str = "") -> str: + value = os.getenv(name, default).strip() + if not value: + raise RuntimeError(f"Missing environment variable: {name}") + return value + + +app = FastAPI(title="Portal Fornecedor Proxy") + +app.add_middleware( + CORSMiddleware, + allow_origins=os.getenv("PORTAL_FORNECEDOR_CORS_ORIGINS", "*").split(","), + allow_credentials=False, + allow_methods=["POST", "OPTIONS", "GET"], + allow_headers=["*"], +) + + +@app.get("/health") +def health() -> dict[str, str]: + return {"status": "ok"} + + +@app.post( + "/api/public/portalfornecedor/enviar", + response_model=PortalFornecedorSuccessResponse, + responses={ + 400: {"model": PortalFornecedorErrorDetail}, + 401: {"model": PortalFornecedorErrorDetail}, + 500: {"model": PortalFornecedorErrorDetail}, + }, +) +async def enviar( + arquivo: UploadFile = File(...), + targetState: int = Form(5), + comment: str = Form("Envio via portal fornecedor"), + data_abertura: str = Form(...), + emitido_por: str = Form(...), + entidade_responsavel: str = Form(...), + tipo_cadastro: str = Form(...), + emailSolicitante: str = Form(""), + cpf: str = Form(...), + tipo_documento: str = Form(""), + numero_documento: str = Form(...), + valor: str = Form(""), + justificativa: str = Form(...), +) -> PortalFornecedorSuccessResponse: + file_name = arquivo.filename or "anexo" + file_mime = arquivo.content_type or "application/octet-stream" + file_bytes = await arquivo.read() + + if not file_bytes: + raise HTTPException(status_code=400, detail={"message": "Arquivo obrigatorio."}) + + auth = build_auth() + upload_binary(file_name, file_bytes, auth) + document_id = create_document(file_name, file_mime, auth) + + process_payload = { + "targetState": targetState, + "comment": comment, + "formFields": { + "data_abertura": data_abertura, + "emitido_por": emitido_por, + "entidade_responsavel": entidade_responsavel, + "tipo_cadastro": tipo_cadastro, + "emailSolicitante": emailSolicitante, + "cpf": cpf, + "tipo_documento": tipo_documento, + "numero_documento": numero_documento, + "valor": valor, + "justificativa": justificativa, + "anexo_documento_id": str(document_id), + "anexo_documento_nome": file_name, + "anexo_documento_mime": file_mime, + }, + } + + response = requests.post( + process_start_endpoint(), + json=process_payload, + auth=auth, + headers={"Accept": "application/json"}, + timeout=30, + ) + + if not response.ok: + raise HTTPException(status_code=response.status_code, detail=safe_json(response)) + + data = safe_json(response) + + return PortalFornecedorSuccessResponse( + success=True, + message="Solicitacao enviada com sucesso.", + processInstanceId=extract_process_instance_id(data), + content=data, + ) + + +def build_auth() -> OAuth1: + return OAuth1( + client_key=env("PORTAL_FORNECEDOR_CLIENT_KEY"), + client_secret=env("PORTAL_FORNECEDOR_CLIENT_SECRET"), + resource_owner_key=env("PORTAL_FORNECEDOR_RESOURCE_OWNER_KEY"), + resource_owner_secret=env("PORTAL_FORNECEDOR_RESOURCE_OWNER_SECRET"), + signature_method="HMAC-SHA1", + ) + + +def base_url() -> str: + return env("PORTAL_FORNECEDOR_BASE_URL").rstrip("/") + + +def process_start_endpoint() -> str: + process_id = env("PORTAL_FORNECEDOR_PROCESS_ID", "FlowEssentials_LancamentodeDocumento") + return f"{base_url()}/process-management/api/v2/processes/{process_id}/start" + + +def upload_binary(file_name: str, file_bytes: bytes, auth: OAuth1) -> None: + response = requests.post( + f"{base_url()}/api/public/2.0/contentfiles/upload/", + params={"fileName": file_name}, + data=file_bytes, + auth=auth, + headers={ + "Content-Type": "application/octet-stream", + "Accept": "application/json", + }, + timeout=30, + ) + + if not response.ok: + raise HTTPException(status_code=response.status_code, detail=safe_json(response)) + + +def create_document(file_name: str, mime_type: str, auth: OAuth1) -> str: + payload = { + "companyId": env("PORTAL_FORNECEDOR_COMPANY_ID", "1"), + "description": file_name, + "parentId": int(env("PORTAL_FORNECEDOR_PARENT_FOLDER_ID", "10")), + "immutable": True, + "isPrivate": False, + "downloadEnabled": True, + "attachments": [{"fileName": file_name}], + "additionalComments": mime_type, + } + + response = requests.post( + f"{base_url()}/api/public/ecm/document/createDocument", + json=payload, + auth=auth, + headers={"Accept": "application/json"}, + timeout=30, + ) + + if not response.ok: + raise HTTPException(status_code=response.status_code, detail=safe_json(response)) + + data = safe_json(response) + content = data.get("content", {}) if isinstance(data, dict) else {} + document_id = content.get("id") or content.get("documentId") + + if not document_id: + raise HTTPException(status_code=500, detail={"message": "Fluig nao retornou documentId do anexo."}) + + return str(document_id) + + +def safe_json(response: requests.Response) -> Any: + try: + return response.json() + except Exception: + return {"message": response.text} + + +def extract_process_instance_id(data: Any) -> str: + if not isinstance(data, dict): + return "" + if data.get("processInstanceId"): + return str(data["processInstanceId"]) + + content = data.get("content") + if isinstance(content, dict): + if content.get("processInstanceId"): + return str(content["processInstanceId"]) + if content.get("processInstanceid"): + return str(content["processInstanceid"]) + if content.get("requestNumber"): + return str(content["requestNumber"]) + + return "" diff --git a/Lançamento de documentos/lançamentos/portalfornecedor_proxy_requirements.txt b/Lançamento de documentos/lançamentos/portalfornecedor_proxy_requirements.txt new file mode 100644 index 0000000..30f7c4e --- /dev/null +++ b/Lançamento de documentos/lançamentos/portalfornecedor_proxy_requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.115.12 +uvicorn==0.34.0 +requests==2.32.3 +requests-oauthlib==2.0.0 +python-multipart==0.0.20 diff --git a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/resources/view.ftl b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/resources/view.ftl index 2aaf5fe..2288fc2 100644 --- a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/resources/view.ftl +++ b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/resources/view.ftl @@ -1,4 +1,4 @@ -
@@ -102,6 +102,32 @@ data-params="MyWidget.instance()">
+
+ +
+ + + +

+ Selecione o arquivo ou use a camera do celular para capturar o documento. +

+ + + + + +
+ +
+ +
+
@@ -131,29 +157,6 @@ data-params="MyWidget.instance()">
-
- -
- - - -

- Utilize o botão abaixo para anexar o documento. -

- - - - - -
- -
- -
-
diff --git a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/webapp/resources/js/portalfornecedor.js b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/webapp/resources/js/portalfornecedor.js index 79fddb5..44b7a37 100644 --- a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/webapp/resources/js/portalfornecedor.js +++ b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/src/main/webapp/resources/js/portalfornecedor.js @@ -1,9 +1,10 @@ -if (typeof window !== "undefined") { +if (typeof window !== "undefined") { window.WCMAPI = window.WCMAPI || {}; if (typeof window.WCMAPI.isMobileAppMode !== "function") { window.WCMAPI.isMobileAppMode = function () { return false; }; } } + function showCamera(param) { if (typeof JSInterface !== "undefined" && JSInterface && typeof JSInterface.showCamera === "function") { JSInterface.showCamera(param); @@ -16,7 +17,6 @@ var MyWidget = SuperWidget.extend({ init: function () { this.root = $("#MyWidget_" + this.instanceId); this.anexoInfo = null; - this.gedParentFolderId = 10; this.isSending = false; this.bindEvents(); this.setupInitialValues(); @@ -29,11 +29,11 @@ var MyWidget = SuperWidget.extend({ self.enviarDocumento(); }); - this.root.find("#anexaDocumento").off("click").on("click", function () { + this.root.find("#anexaDocumento_" + this.instanceId + ", #anexaDocumento").off("click").on("click", function () { self.handleAnexo(); }); - this.root.find("#arquivoLocal").off("change").on("change", function () { + this.root.find("#arquivoLocal_" + this.instanceId + ", #arquivoLocal").off("change").on("change", function () { self.onArquivoSelecionado(this); }); @@ -57,6 +57,7 @@ var MyWidget = SuperWidget.extend({ this.root.find("#tipo_cadastro").val("cpf").trigger("change"); this.root.find("#emitido_por").val("fornecedor"); this.root.find("#tipo_documento").val("danfe"); + this.root.find("#arquivoLocalNome").text(""); this.clearStatus(); }, @@ -78,48 +79,10 @@ var MyWidget = SuperWidget.extend({ this.root.find("#arquivoLocalNome").text("Arquivo selecionado: " + file.name + " (" + this.formatFileSize(file.size || 0) + ")"); this.clearStatus(); - - console.log("[portalfornecedor] anexo preparado para envio:", { - fileName: this.anexoInfo.fileName, - mimeType: this.anexoInfo.mimeType, - fileSize: this.anexoInfo.fileSize - }); }, handleAnexo: function () { - if (showCamera("anexo_documento")) { - return; - } - - if (this.openAttachmentTab()) { - return; - } - - this.root.find("#arquivoLocal").trigger("click"); - }, - - openAttachmentTab: function () { - try { - var parentDoc = window.parent && window.parent.document ? window.parent.document : document; - var selectors = [ - "#tab-attachments", - "a[href*='attachments']", - "a[aria-controls*='attachments']", - "[data-tab*='attachments']" - ]; - - for (var i = 0; i < selectors.length; i++) { - var el = parentDoc.querySelector(selectors[i]); - if (el) { - el.click(); - return true; - } - } - } catch (e) { - // ignore - } - - return false; + this.root.find("#arquivoLocal_" + this.instanceId + ", #arquivoLocal").trigger("click"); }, toggleTipoCadastro: function (tipo) { @@ -188,61 +151,44 @@ var MyWidget = SuperWidget.extend({ this.setLoading(true, "Enviando documento, aguarde..."); - this.uploadAnexoToECM(this.anexoInfo.file) - .done(function (docData) { - var payloadProcesso = { - targetState: 0, - subProcessTargetState: 0, - comment: "Solicitacao criada via widget", - formFields: { - data_abertura: self.value("#data_abertura"), - emitido_por: self.value("#emitido_por"), - entidade_responsavel: self.value("#entidade_responsavel"), - tipo_cadastro: tipoCadastro, - emailSolicitante: self.value("#emailSolicitante"), - cpf: cpfField || documentoPessoa, - tipo_documento: self.value("#tipo_documento"), - numero_documento: numeroDocumento, - valor: valor, - justificativa: self.value("#justificativa"), - anexo_documento_id: String(docData.documentId || ""), - anexo_documento_nome: self.anexoInfo.fileName, - anexo_documento_mime: self.anexoInfo.mimeType - } - }; + var payloadProcesso = { + targetState: 5, + comment: "Envio via portal fornecedor", + formFields: { + data_abertura: self.value("#data_abertura"), + emitido_por: self.value("#emitido_por"), + entidade_responsavel: self.value("#entidade_responsavel"), + tipo_cadastro: tipoCadastro, + emailSolicitante: self.value("#emailSolicitante"), + cpf: cpfField || documentoPessoa, + tipo_documento: self.value("#tipo_documento"), + numero_documento: numeroDocumento, + valor: valor, + justificativa: self.value("#justificativa") + } + }; - window.__portalfornecedor_lastProcessPayload = payloadProcesso; - console.log("[portalfornecedor] payload direto processo:", payloadProcesso); + window.__portalfornecedor_lastProcessPayload = payloadProcesso; + console.log("[portalfornecedor] payload endpoint:", payloadProcesso); - self.enviarDiretoProcesso(payloadProcesso) - .done(function (response) { - console.log("[portalfornecedor] response processo direto:", response); - self.setLoading(false); - self.renderSuccessState(response); - }) - .fail(function (xhrDireto) { - self.setLoading(false); - console.warn("[portalfornecedor] falha no envio direto:", xhrDireto); - console.error("[portalfornecedor] detalhe erro processo direto:", { - status: xhrDireto.status, - statusText: xhrDireto.statusText, - responseText: xhrDireto.responseText - }); - self.setStatus("error", "Falha ao iniciar processo. Veja o console para detalhes."); - FLUIGC.toast({ - title: "Erro", - message: "Falha ao iniciar processo. Veja o console para detalhes.", - type: "danger" - }); - }); - }) - .fail(function (uploadErr) { + this.enviarViaEndpoint(payloadProcesso, this.anexoInfo.file) + .done(function (response) { + console.log("[portalfornecedor] response endpoint:", response); self.setLoading(false); - console.error("[portalfornecedor] falha upload documento ECM:", uploadErr); - self.setStatus("error", "Falha ao enviar anexo para o GED."); + self.renderSuccessState(response); + }) + .fail(function (xhr) { + self.setLoading(false); + console.warn("[portalfornecedor] falha no envio via endpoint:", xhr); + console.error("[portalfornecedor] detalhe erro endpoint:", { + status: xhr.status, + statusText: xhr.statusText, + responseText: xhr.responseText + }); + self.setStatus("error", self.extractEndpointErrorMessage(xhr)); FLUIGC.toast({ title: "Erro", - message: "Falha ao enviar anexo para o GED.", + message: "Falha ao enviar pelo endpoint do portal.", type: "danger" }); }); @@ -270,67 +216,68 @@ var MyWidget = SuperWidget.extend({ return ""; }, - enviarDiretoProcesso: function (payloadProcesso) { + enviarViaEndpoint: function (payloadProcesso, file) { return $.ajax({ - url: "/process-management/api/v2/processes/FlowEssentials_LancamentodeDocumento/start", + url: this.getPublicEndpointUrl(), type: "POST", - contentType: "application/json", - data: JSON.stringify(payloadProcesso) + data: this.buildMultipartData(payloadProcesso, file), + processData: false, + contentType: false }); }, - uploadAnexoToECM: function (file) { - var self = this; - var dfd = $.Deferred(); - var fileName = file.name; - var uploadUrl = "/api/public/2.0/contentfiles/upload/?fileName=" + encodeURIComponent(fileName); + getPublicEndpointUrl: function () { + if (window.portalfornecedorConfig && window.portalfornecedorConfig.publicEndpointUrl) { + return window.portalfornecedorConfig.publicEndpointUrl; + } - fetch(uploadUrl, { - method: "POST", - headers: { - "Content-Type": "application/octet-stream" - }, - body: file - }) - .then(function (resp) { - if (!resp.ok) throw new Error("Falha no upload binario"); - return resp.text(); - }) - .then(function () { - var companyId = (window.WCMAPI && WCMAPI.organizationId) ? String(WCMAPI.organizationId) : "1"; - var createPayload = { - companyId: companyId, - description: fileName, - parentId: self.gedParentFolderId, - immutable: true, - isPrivate: false, - downloadEnabled: true, - attachments: [{ fileName: fileName }] - }; + return "https://api.grupoginseng.com.br/v2/api/public/portalfornecedor/enviar"; + }, - return fetch("/api/public/ecm/document/createDocument", { - method: "POST", - headers: { - "Content-Type": "application/json;charset=utf-8" - }, - body: JSON.stringify(createPayload) - }); - }) - .then(function (resp) { - if (!resp.ok) throw new Error("Falha ao criar documento no GED"); - return resp.json(); - }) - .then(function (data) { - var content = data && data.content ? data.content : {}; - var documentId = content.id || content.documentId; - if (!documentId) throw new Error("GED nao retornou documentId"); - dfd.resolve({ documentId: documentId, raw: data }); - }) - .catch(function (err) { - dfd.reject(err); - }); + buildMultipartData: function (payloadProcesso, file) { + var formData = new FormData(); + var formFields = payloadProcesso && payloadProcesso.formFields ? payloadProcesso.formFields : {}; + var keys = Object.keys(formFields); - return dfd.promise(); + formData.append("targetState", String(payloadProcesso.targetState || 5)); + formData.append("comment", payloadProcesso.comment || ""); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + formData.append(key, formFields[key] == null ? "" : String(formFields[key])); + } + + if (file) { + formData.append("arquivo", file, file.name || "anexo"); + } + + return formData; + }, + + extractEndpointErrorMessage: function (xhr) { + if (!xhr || !xhr.responseText) { + return "Falha ao enviar pelo endpoint do portal. Veja o console para detalhes."; + } + + try { + var payload = JSON.parse(xhr.responseText); + if (payload && payload.message) { + return String(payload.message); + } + if (payload && payload.detail && typeof payload.detail === "string") { + return String(payload.detail); + } + if (payload && payload.detail && payload.detail.message) { + return String(payload.detail.message); + } + if (payload && payload.detailedMessage) { + return String(payload.detailedMessage); + } + } catch (e) { + // ignore + } + + return xhr.responseText; }, setLoading: function (isLoading, message) { @@ -379,6 +326,8 @@ var MyWidget = SuperWidget.extend({ this.clearFieldError("#cpf"); } + if (!this.anexoInfo || !this.anexoInfo.file) ok = this.markRequired("#arquivoLocal") && ok; + return ok; }, @@ -457,4 +406,3 @@ var MyWidget = SuperWidget.extend({ return (b / 1048576).toFixed(2) + " MB"; } }); - diff --git a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/target/portalfornecedor.war b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/target/portalfornecedor.war index 09757e9..8d016e3 100644 Binary files a/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/target/portalfornecedor.war and b/Lançamento de documentos/lançamentos/wcm/widget/portalfornecedor/target/portalfornecedor.war differ