ADM-008: Puntos de Venta (CRUD fundacional) #19
@@ -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