feat(web): feature puntos-de-venta — types, api, hooks
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { axiosClient } from '@/api/axiosClient'
|
||||
import type {
|
||||
CreatePuntoDeVentaRequest,
|
||||
PuntoDeVentaCreated,
|
||||
PuntoDeVentaDetail,
|
||||
PuntoDeVentaListItem,
|
||||
PuntosDeVentaQuery,
|
||||
UpdatePuntoDeVentaRequest,
|
||||
PagedResult,
|
||||
} from '../types'
|
||||
|
||||
export async function listPuntosDeVenta(
|
||||
query: PuntosDeVentaQuery,
|
||||
): Promise<PagedResult<PuntoDeVentaListItem>> {
|
||||
const params = new URLSearchParams()
|
||||
if (query.page !== undefined) params.set('page', String(query.page))
|
||||
if (query.pageSize !== undefined) params.set('pageSize', String(query.pageSize))
|
||||
if (query.medioId !== undefined) params.set('medioId', String(query.medioId))
|
||||
if (query.activo !== undefined) params.set('activo', String(query.activo))
|
||||
|
||||
const response = await axiosClient.get<PagedResult<PuntoDeVentaListItem>>(
|
||||
'/api/v1/admin/puntos-de-venta',
|
||||
{ params },
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function getPuntoDeVenta(id: number): Promise<PuntoDeVentaDetail> {
|
||||
const response = await axiosClient.get<PuntoDeVentaDetail>(
|
||||
`/api/v1/admin/puntos-de-venta/${id}`,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function createPuntoDeVenta(
|
||||
payload: CreatePuntoDeVentaRequest,
|
||||
): Promise<PuntoDeVentaCreated> {
|
||||
const response = await axiosClient.post<PuntoDeVentaCreated>(
|
||||
'/api/v1/admin/puntos-de-venta',
|
||||
payload,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function updatePuntoDeVenta(
|
||||
id: number,
|
||||
payload: UpdatePuntoDeVentaRequest,
|
||||
): Promise<PuntoDeVentaDetail> {
|
||||
const response = await axiosClient.put<PuntoDeVentaDetail>(
|
||||
`/api/v1/admin/puntos-de-venta/${id}`,
|
||||
payload,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function deactivatePuntoDeVenta(id: number): Promise<void> {
|
||||
await axiosClient.post(`/api/v1/admin/puntos-de-venta/${id}/deactivate`)
|
||||
}
|
||||
|
||||
export async function reactivatePuntoDeVenta(id: number): Promise<void> {
|
||||
await axiosClient.post(`/api/v1/admin/puntos-de-venta/${id}/reactivate`)
|
||||
}
|
||||
22
src/web/src/features/puntos-de-venta/api/secuencias.api.ts
Normal file
22
src/web/src/features/puntos-de-venta/api/secuencias.api.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { axiosClient } from '@/api/axiosClient'
|
||||
import type { TipoComprobante, ReservarNumeroResponse, ProximoNumeroResponse } from '../types'
|
||||
|
||||
export async function reservarNumero(
|
||||
puntoDeVentaId: number,
|
||||
tipoComprobante: TipoComprobante,
|
||||
): Promise<ReservarNumeroResponse> {
|
||||
const response = await axiosClient.post<ReservarNumeroResponse>(
|
||||
`/api/v1/admin/puntos-de-venta/${puntoDeVentaId}/secuencias/${tipoComprobante}/reservar`,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function getProximoNumero(
|
||||
puntoDeVentaId: number,
|
||||
tipoComprobante: TipoComprobante,
|
||||
): Promise<ProximoNumeroResponse> {
|
||||
const response = await axiosClient.get<ProximoNumeroResponse>(
|
||||
`/api/v1/admin/puntos-de-venta/${puntoDeVentaId}/secuencias/${tipoComprobante}/proximo`,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
listPuntosDeVenta,
|
||||
getPuntoDeVenta,
|
||||
createPuntoDeVenta,
|
||||
updatePuntoDeVenta,
|
||||
deactivatePuntoDeVenta,
|
||||
reactivatePuntoDeVenta,
|
||||
} from '../api/puntos-de-venta.api'
|
||||
import type { CreatePuntoDeVentaRequest, PuntosDeVentaQuery, UpdatePuntoDeVentaRequest } from '../types'
|
||||
|
||||
export const puntosDeVentaListQueryKey = (query: PuntosDeVentaQuery) =>
|
||||
['puntos-de-venta', 'list', query] as const
|
||||
|
||||
export const puntoDeVentaDetailQueryKey = (id: number) =>
|
||||
['puntos-de-venta', 'detail', id] as const
|
||||
|
||||
// ─── List ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export function usePuntosDeVentaList(query: PuntosDeVentaQuery) {
|
||||
return useQuery({
|
||||
queryKey: puntosDeVentaListQueryKey(query),
|
||||
queryFn: () => listPuntosDeVenta(query),
|
||||
staleTime: 15_000,
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Detail ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export function usePuntoDeVenta(id: number) {
|
||||
return useQuery({
|
||||
queryKey: puntoDeVentaDetailQueryKey(id),
|
||||
queryFn: () => getPuntoDeVenta(id),
|
||||
enabled: !!id,
|
||||
staleTime: 15_000,
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Create ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export function useCreatePuntoDeVenta() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (payload: CreatePuntoDeVentaRequest) => createPuntoDeVenta(payload),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['puntos-de-venta'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Update ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export function useUpdatePuntoDeVenta(id: number) {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (payload: UpdatePuntoDeVentaRequest) => updatePuntoDeVenta(id, payload),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['puntos-de-venta'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Deactivate / Reactivate ─────────────────────────────────────────────────
|
||||
|
||||
export function useDeactivatePuntoDeVenta() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => deactivatePuntoDeVenta(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['puntos-de-venta'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useReactivatePuntoDeVenta() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => reactivatePuntoDeVenta(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['puntos-de-venta'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { reservarNumero, getProximoNumero } from '../api/secuencias.api'
|
||||
import type { TipoComprobante } from '../types'
|
||||
|
||||
// ─── Reservar ────────────────────────────────────────────────────────────────
|
||||
|
||||
export function useReservarNumero(puntoDeVentaId: number) {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (tipoComprobante: TipoComprobante) =>
|
||||
reservarNumero(puntoDeVentaId, tipoComprobante),
|
||||
onSuccess: (_data, tipoComprobante) => {
|
||||
// Invalidate the proximo query for this pdv+tipo so it refetches
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['puntos-de-venta', 'proximo', puntoDeVentaId, tipoComprobante],
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Próximo número (read-only) ──────────────────────────────────────────────
|
||||
|
||||
export function useProximoNumero(puntoDeVentaId: number, tipoComprobante: TipoComprobante) {
|
||||
return useQuery({
|
||||
queryKey: ['puntos-de-venta', 'proximo', puntoDeVentaId, tipoComprobante],
|
||||
queryFn: () => getProximoNumero(puntoDeVentaId, tipoComprobante),
|
||||
enabled: !!puntoDeVentaId,
|
||||
staleTime: 5_000,
|
||||
})
|
||||
}
|
||||
71
src/web/src/features/puntos-de-venta/types.ts
Normal file
71
src/web/src/features/puntos-de-venta/types.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// ADM-008 — shared types for puntos-de-venta feature
|
||||
|
||||
export enum TipoComprobante {
|
||||
FacturaA = 1,
|
||||
FacturaB = 2,
|
||||
FacturaC = 3,
|
||||
NotaCreditoA = 4,
|
||||
NotaCreditoB = 5,
|
||||
NotaCreditoC = 6,
|
||||
}
|
||||
|
||||
export interface PuntoDeVentaListItem {
|
||||
id: number
|
||||
medioId: number
|
||||
numeroAFIP: number
|
||||
nombre: string
|
||||
activo: boolean
|
||||
}
|
||||
|
||||
export interface PuntoDeVentaDetail {
|
||||
id: number
|
||||
medioId: number
|
||||
numeroAFIP: number
|
||||
nombre: string
|
||||
activo: boolean
|
||||
fechaCreacion: string
|
||||
fechaModificacion: string | null
|
||||
}
|
||||
|
||||
export interface PuntoDeVentaCreated {
|
||||
id: number
|
||||
medioId: number
|
||||
numeroAFIP: number
|
||||
nombre: string
|
||||
activo: boolean
|
||||
}
|
||||
|
||||
export interface CreatePuntoDeVentaRequest {
|
||||
medioId: number
|
||||
numeroAFIP: number
|
||||
nombre: string
|
||||
}
|
||||
|
||||
export interface UpdatePuntoDeVentaRequest {
|
||||
nombre: string
|
||||
numeroAFIP: number
|
||||
}
|
||||
|
||||
export interface PuntosDeVentaQuery {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
medioId?: number
|
||||
activo?: boolean
|
||||
}
|
||||
|
||||
export interface ReservarNumeroResponse {
|
||||
tipoComprobante: TipoComprobante
|
||||
numeroReservado: number
|
||||
}
|
||||
|
||||
export interface ProximoNumeroResponse {
|
||||
tipoComprobante: TipoComprobante
|
||||
proximoNumero: number
|
||||
}
|
||||
|
||||
export interface PagedResult<T> {
|
||||
items: T[]
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
}
|
||||
Reference in New Issue
Block a user