From 790c79ede6ba9b4b1017227871b06390ca5e071d Mon Sep 17 00:00:00 2001 From: Keven Date: Sat, 8 Nov 2025 20:35:25 -0300 Subject: [PATCH] =?UTF-8?q?[MVPTN-37]=20feat(Itens):=20Implementa=20a=20in?= =?UTF-8?q?ser=C3=A7=C3=A3o=20de=20itens=20ao=20pedido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_data/GEmolumento/GEmolumentoIndexData.ts | 3 +- .../g_emolumento/useGEmolumentoReadHook.ts | 5 +- .../useGEmolumentoItemReadHook.ts | 5 +- .../GEmolumento/GEmolumentoServicoSelect.tsx | 149 ++++++++++++++++++ .../TServicoTipo/TServicoTipoSelect.tsx | 33 ++-- .../data/GCalculo/GCalculoServicoData.ts | 17 ++ .../GCalculo/GCalculoServicoInterface.ts | 4 + .../GCalculo/GCalculoServicoService.ts | 11 ++ .../TServicoItemPedidoFormColumns.tsx | 6 +- .../TServicoPedido/TServicoPedidoForm.tsx | 95 ++++++++++- .../useTServicoItemPedidoAddHook.ts | 54 +++++++ .../useTServicoPedidoFormHook.ts | 3 +- .../TServicoItemPedidoAddInterface.ts | 7 + .../TServicoItemPedidoIntefarce.ts | 3 +- .../TServicoPedido/TServicoPedidoInterface.ts | 3 + .../TServicoPedido/TServicoPedidoSchema.ts | 9 +- src/shared/actions/data/ToMoney.ts | 6 + 17 files changed, 383 insertions(+), 30 deletions(-) create mode 100644 src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx create mode 100644 src/packages/administrativo/data/GCalculo/GCalculoServicoData.ts create mode 100644 src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts create mode 100644 src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts create mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts create mode 100644 src/shared/actions/data/ToMoney.ts diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts index 422fb54..e8e80b8 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts @@ -6,6 +6,7 @@ import API from '@/shared/services/api/Api'; // Importa o enum que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE, etc.) import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; + import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface'; // Função assíncrona responsável por executar a requisição para listar os tipos de marcação @@ -14,7 +15,7 @@ async function executeGEmolumentoIndexData(data: GEmolumentoReadInterface) { const api = new API(); // Concatena o endpoint com a query string (caso existam parâmetros) - const endpoint = `administrativo/g_emolumento/${data.sistema_id}`; + const endpoint = `administrativo/g_emolumento/sistema/${data.sistema_id}`; // Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/' return await api.send({ diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts index b18cc96..924bf9c 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts @@ -1,15 +1,16 @@ // Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.) +import { useMemo, useState } from 'react'; + import { useResponse } from '@/shared/components/response/ResponseContext'; // Importa hooks do React para gerenciamento de estado e memorização de valores -import { useMemo, useState } from 'react'; // Importa a interface que define a estrutura dos dados de "GEmolumento" +import { GEmolumentoInterface } from '../../_interfaces/GEmolumentoInterface'; import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface'; // Importa o serviço responsável por buscar os dados de "GEmolumento" na API import { GEmolumentoIndexService } from '../../_services/g_emolumento/GEmolumentoIndexService'; -import { GEmolumentoInterface } from '../../_interfaces/GEmolumentoInterface'; // Hook personalizado para leitura (consulta) dos emolumentos export const useGEmolumentoReadHook = () => { diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts index 000908a..1fa5dd1 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts @@ -1,15 +1,16 @@ // Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.) +import { useMemo, useState } from 'react'; + import { useResponse } from '@/shared/components/response/ResponseContext'; // Importa hooks do React para gerenciamento de estado e memorização de valores -import { useMemo, useState } from 'react'; // Importa a interface que define a estrutura dos dados de "gEmolumentoItem" +import { GEmolumentoItemInterface } from '../../_interfaces/GEmolumentoItemInterface'; import { GEmolumentoItemReadInterface } from '../../_interfaces/GEmolumentoItemReadInterface'; // Importa o serviço responsável por buscar os dados de "GEmolumentoItem" na API import { GEmolumentoItemValorService } from '../../_services/g_emolumento_item/GEmolumentoItemValorService'; -import { GEmolumentoItemInterface } from '../../_interfaces/GEmolumentoItemInterface'; // Hook personalizado para leitura (consulta) dos emolumentos export const useGEmolumentoItemReadHook = () => { diff --git a/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx new file mode 100644 index 0000000..d35c6a4 --- /dev/null +++ b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx @@ -0,0 +1,149 @@ +'use client'; + +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { useGEmolumentoReadHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook'; +import { GEmolumentoReadInterface } from '@/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface'; +import { Button } from '@/components/ui/button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; + +// Tipagem das props do componente +interface GEmolumentoSelectProps { + sistema_id: number; + field: any; + onSelectChange?: (emolumento: { emolumento_id: number; descricao: string }) => void; + className?: string; +} + +// Componente principal do select de emolumentos +export default function GEmolumentoServicoSelect({ + sistema_id, + field, + onSelectChange, + className, +}: GEmolumentoSelectProps) { + const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const gEmolumentoReadParams: GEmolumentoReadInterface = { sistema_id }; + const { gEmolumento = [], fetchGEmolumento } = useGEmolumentoReadHook(); + + /** + * Efeito para buscar os dados apenas uma vez. + */ + const loadData = useCallback(async () => { + if (gEmolumento.length) return; + setIsLoading(true); + await fetchGEmolumento(gEmolumentoReadParams); + setIsLoading(false); + }, [gEmolumento.length, fetchGEmolumento, gEmolumentoReadParams]); + + useEffect(() => { + loadData(); + }, [loadData]); + + /** + * Memoriza o item selecionado para evitar reprocessamentos. + */ + const selected = useMemo( + () => + gEmolumento.find( + (item) => String(item.emolumento_id) === String(field?.value?.id ?? ''), + ), + [gEmolumento, field?.value], + ); + + /** + * Manipulador de seleção com verificação segura. + */ + const handleSelect = useCallback( + (item: any) => { + if (!field?.onChange) return; + + const selectedValue = { + emolumento_id: Number(item.emolumento_id), + descricao: item.descricao, + }; + + field.onChange(selectedValue); + + if (onSelectChange) onSelectChange(selectedValue); + + setOpen(false); + }, + [field, onSelectChange], + ); + + return ( + + + + + + + + + + + + + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} + + + {gEmolumento.map((item) => ( + handleSelect(item)} + > + + {GetCapitalize(item.descricao ?? '')} + + ))} + + + + + + ); +} diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index 9aa3642..fc665d1 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -19,7 +19,6 @@ import { useTServicoTipoReadHook } from '@/packages/administrativo/hooks/TServic import TServicoTipoSelectInterface from '@/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; - export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterface) { const [open, setOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -27,7 +26,6 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac /** * Efeito para buscar os dados apenas uma vez. - * useCallback evita recriação desnecessária da função. */ const loadData = useCallback(async () => { if (tServicoTipo.length) return; @@ -41,20 +39,29 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac }, [loadData]); /** - * Memoriza o bairro selecionado para evitar reprocessamentos. + * Item selecionado (comparando por ID) */ const selected = useMemo( - () => tServicoTipo.find((b) => String(b.servico_tipo_id) === String(field?.value ?? '')), + () => + tServicoTipo.find( + (item) => String(item.servico_tipo_id) === String(field?.value?.servico_tipo_id ?? ''), + ), [tServicoTipo, field?.value], ); /** - * Manipulador de seleção com verificação segura. + * Manipulador de seleção */ const handleSelect = useCallback( - (bairroId: string | number) => { + (item: any) => { if (!field?.onChange) return; - field.onChange(bairroId); + + const selectedValue = { + servico_tipo_id: Number(item.servico_tipo_id), + descricao: item.descricao, + }; + + field.onChange(selectedValue); setOpen(false); }, [field], @@ -73,16 +80,17 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac > {isLoading ? 'Carregando...' - : selected - ? GetCapitalize(selected.descricao) + : field.value?.descricao + ? GetCapitalize(field.value.descricao) : 'Selecione...'} + - + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} @@ -92,12 +100,13 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac handleSelect(Number(item.servico_tipo_id))} + onSelect={() => handleSelect(item)} > { + const api = new API(); + return api.send({ + method: Methods.POST, + endpoint: `administrativo/g_calculo/servico/`, + body: data, + }); +} + +export const GCalculoServico = withClientErrorHandler(executeGCalculoCalcularData); diff --git a/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts b/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts new file mode 100644 index 0000000..19ecb7b --- /dev/null +++ b/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts @@ -0,0 +1,4 @@ +export default interface GCalculoServicoInterface { + valor_documento?: number; + emolumento_id?: number; +} diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts new file mode 100644 index 0000000..6b90bcc --- /dev/null +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -0,0 +1,11 @@ +import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler"; + +import { GCalculoServico } from "../../data/GCalculo/GCalculoServicoData"; +import GCalculoServicoInterface from "../../interfaces/GCalculo/GCalculoServicoInterface"; + +async function executeGCalculoServicoService(data: GCalculoServicoInterface) { + const response = await GCalculoServico(data); + return response; +} + +export const GCalculoServicoService = withClientErrorHandler(executeGCalculoServicoService); diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx index 558ef73..c31a92f 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -26,7 +26,7 @@ export default function TServicoItemPedidoFormColumns(
- {GetCapitalize(data.servico)} + {GetCapitalize(data.descricao)}
{GetCapitalize(data.tabela)} @@ -36,8 +36,8 @@ export default function TServicoItemPedidoFormColumns( ); }, sortingFn: (a, b) => - (a.original.servico?.toLowerCase() || '').localeCompare( - b.original.servico?.toLowerCase() || '', + (a.original.descricao?.toLowerCase() || '').localeCompare( + b.original.descricao?.toLowerCase() || '', ), }, diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 48c6539..fe964d0 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -16,7 +16,11 @@ import { FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect'; import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; +import TServicoTipoSelect from '@/packages/administrativo/components/TServicoTipo/TServicoTipoSelect'; +import TServicoItemPedidoFormTable from '@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable'; +import { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; @@ -26,16 +30,40 @@ import { StepNavigator, StepNavigatorRef, StepSection } from '@/shared/component import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; + + export default function TServicoPedidoForm() { // Controle de rotas const router = useRouter(); + const form = useTServicoPedidoFormHook({}); + + const { setValue, handleSubmit, watch } = form; + // Controle de estado do botão const [buttonIsLoading, setButtonIsLoading] = useState(false); const { TServicoPedido, saveTServicoPedido } = useTServicoPedidoSaveHook() + const { TServicoItemPedido, addTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue) - const form = useTServicoPedidoFormHook({}); + const servicoAtual = watch('servico_tipo_id'); + const emolumentoAtual = watch('emolumento_id'); + + const handleAddItem = () => { + + if (servicoAtual && emolumentoAtual) { + + // Cria um novo objeto + const servicoEEmolumento = { + servico_tipo: servicoAtual, + emolumento: emolumentoAtual + } + + // Adiciona o item calculado na tabel + addTServicoItemPedido(servicoEEmolumento) + + } + }; const sections: StepSection[] = [ { key: 'pedido', id: 'selectPedido', icon: , title: 'Pedido', description: 'Dados gerais do pedido.' }, @@ -50,8 +78,6 @@ export default function TServicoPedidoForm() { async (formData: TServicoPedidoInterface) => { - console.log(formData); - // Coloca o botão em estado de loading setButtonIsLoading(true); @@ -76,6 +102,11 @@ export default function TServicoPedidoForm() { console.log('Erro no formulário:', error); } + React.useEffect(() => { + console.log(JSON.stringify(TServicoItemPedido)) + }, [TServicoItemPedido]) + + return (

@@ -205,6 +236,58 @@ export default function TServicoPedidoForm() {

+ + + +

+ Itens +

+
+
+ +
+
+ ( + + Serviços + + + + )} + /> +
+
+ ( + + Emolumentos + + + + )} + /> +
+
+ +
+
+ { }} + onDelete={() => { }} + /> +
+
+
+
{/* Seção: Pagamento */} @@ -252,7 +335,7 @@ export default function TServicoPedidoForm() { )} />
-
+
-
+
-
+
) => { + + const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + + const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { + + console.log(data.emolumento) + + data.sistema_id = 2 + data.valor_documento = 0 + data.quantidade = 1 + data.emolumento_id = data.emolumento.emolumento_id + + const response = await GCalculoServicoService(data) + + const item = { + emolumento_id: data.emolumento.emolumento_id, + emolumento_item_id: data.emolumento.emolumento_item_id, + servico_tipo_id: data.servico_tipo.servico_tipo_id, + descricao: data.servico_tipo.descricao, + tabela: data.emolumento.descricao, + situacao: 'F', + qtd: 1, + valor: response.data.valor_total, + emolumento: response.data.valor_emolumento, + fundesp: response.data.valor_fundos, + taxa_judiciaria: response.data.taxa_judiciaria, + valor_iss: response.data.valor_iss, + } + + setTServicoItemPedido((prev) => { + const novoItem = [...prev, item] + if (setValue) setValue('itens', novoItem) + return novoItem + }); + + }; + + return { + TServicoItemPedido, + addTServicoItemPedido, + }; +}; diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index fc60cba..b8fef59 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -13,7 +13,7 @@ export function useTServicoPedidoFormHook(defaults?: Partial texto longo ou base64 + certidao_texto?: string | null ato_antigo_tipo?: string valor_iss?: number id_ato_isentado?: number @@ -63,4 +63,5 @@ export default interface TServicoItemPedidoInterface { vrcext?: number valor_fundo_selo?: number averbacao?: string + descricao?: string } diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index 05342c5..ca0df03 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -17,4 +17,7 @@ export default interface TServicoPedidoInterface { selo_pessoa_cpfcnpj?: string; login?: string; funcao?: string; + itens?: []; + servico_tipo_id: object; + emolumento_id: object; } \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 85c2d92..499aedb 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -1,5 +1,7 @@ import z from "zod"; +import { TServicoItemPedidoSchema } from "../TServicoItemPedido/TServicoItemPedidoSchema"; + export const TServicoPedidoSchema = z.object({ servico_pedido_id: z.number().optional(), @@ -9,7 +11,7 @@ export const TServicoPedidoSchema = z.object({ data_pedido: z.string().optional(), mensalista_livrocaixa_id: z.number().optional(), observacao: z.string().optional(), - escrevente_id: z.number().optional(), + escrevente_id: z.number(), situacao: z.string().optional(), estornado: z.string().optional(), nfse_id: z.number().optional(), @@ -18,7 +20,10 @@ export const TServicoPedidoSchema = z.object({ selo_pessoa_nome: z.string().optional(), selo_pessoa_cpfcnpj: z.string().optional(), login: z.string().optional(), - funcao: z.string().optional() + funcao: z.string().optional(), + itens: z.array(TServicoItemPedidoSchema).default([]), + servico_tipo_id: z.object(), + emolumento_id: z.object(), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/shared/actions/data/ToMoney.ts b/src/shared/actions/data/ToMoney.ts new file mode 100644 index 0000000..95a5c24 --- /dev/null +++ b/src/shared/actions/data/ToMoney.ts @@ -0,0 +1,6 @@ +// Helper: garante número com 2 casas decimais +export const toMoney = (v: unknown): number => { + const n = Number(v ?? 0); + // evita problemas de ponto flutuante + return Math.round((n + Number.EPSILON) * 100) / 100; +};