Oleada UX 4: Pagos/Adeudos (cobro en 1 click con recibo, plan de pagos), Monedero (movimientos narrados, ajustes auditados, MXN+USD), Inventario (alta rapida, conteo fisico con ajuste e historial, alertas accionables) + fix de folios de pago y cantidades de movimientos

This commit is contained in:
2026-09-17 20:31:52 +00:00
parent ac5896b1fc
commit 9ee3343e22
20 changed files with 2173 additions and 165 deletions

View File

@@ -679,6 +679,85 @@ export interface AdeudoRow {
num_ventas: number;
total_adeudo: number;
ultima_venta: string | null;
plan?: PaymentPlan | null;
}
export interface PaymentPlan {
count: number;
amount: number;
next_payment_date: string | null;
note: string;
start: string | null;
by: string;
paid_count: number;
remaining_count: number;
}
export interface ReceiptApplied {
id: number;
name: string;
date: string | null;
applied: number;
total: number;
state: string;
amount_due: number;
}
export interface ReceiptData {
folio: string;
date: string;
patient_id: number;
patient: string;
patient_code: string;
phone: string;
amount: number;
payment_method: string;
adeudo_anterior: number;
saldo_restante: number;
aplicado: ReceiptApplied[];
operator: string;
wallet_points: number;
}
export type WalletTxType = 'accrual' | 'redemption' | 'adjustment' | 'expiration';
export interface WalletTransaction {
id: number;
type: WalletTxType;
type_label: string;
points: number;
narration: string;
description: string;
reference: string;
operator: string;
date: string | null;
}
export interface InventoryConteoLine {
id: number;
item_id: number;
item: string;
unit: string;
qty_sistema: number;
qty_contado: number;
difference: number;
cost: number;
value_diff: number;
move_id: number | null;
}
export interface InventoryConteo {
id: number;
name: string;
date: string | null;
user_name: string;
notes: string;
total_items: number;
total_diferencias: number;
total_faltante: number;
total_sobrante: number;
total_value_diff: number;
lines?: InventoryConteoLine[];
}
export interface ComisionRow {
@@ -1261,6 +1340,17 @@ export const odooApi = {
return data;
},
// Cobro del adeudo del paciente (parcial o total) + plan de pagos
async payAdeudo(patientId: number, payload: { amount: number; payment_method: string }): Promise<{ status: string; payment: Payment; receipt: ReceiptData }> {
const { data } = await axios.post(`${ODOO_BASE}/skeen/frontend/v1/patients/${patientId}/pay_adeudo`, payload);
return data;
},
async setPaymentPlan(patientId: number, payload: { count: number; amount: number; next_payment_date?: string | null; note?: string }): Promise<{ status: string; plan: PaymentPlan }> {
const { data } = await axios.put(`${ODOO_BASE}/skeen/frontend/v1/patients/${patientId}/payment_plan`, payload);
return data;
},
// Servicios
async getServices(params?: { search?: string; category?: string; page?: number; page_size?: number }): Promise<{ status: string; services: Service[]; total?: number; page?: number; total_pages?: number }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/services`, { params });
@@ -1347,6 +1437,18 @@ export const odooApi = {
return data;
},
// Movimientos del monedero con narración legible
async getWalletTransactions(walletId: number, params?: { type?: string; date_from?: string; date_to?: string }): Promise<{ status: string; wallet: Wallet; movimientos: WalletTransaction[]; total: number; sum_positive: number; sum_negative: number }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/wallets/${walletId}/transactions`, { params: params || {} });
return data;
},
// Ajuste manual auditado de puntos (admin/gerencia)
async adjustWallet(walletId: number, payload: { points: number; reason: string }): Promise<{ status: string; wallet: Wallet; applied: number; movimiento: { id: number; points: number; date: string; operator: string; narration: string } }> {
const { data } = await axios.post(`${ODOO_BASE}/skeen/frontend/v1/wallets/${walletId}/adjust`, payload);
return data;
},
// Pagos
async getPayments(state?: string): Promise<{ status: string; payments: Payment[] }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/payments`, { params: state ? { state } : {} });
@@ -1487,6 +1589,22 @@ export const odooApi = {
return data;
},
// Conteo físico cíclico de inventario
async getConteos(limit = 30): Promise<{ status: string; conteos: InventoryConteo[] }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/inventario/conteos`, { params: { limit } });
return data;
},
async getConteo(id: number): Promise<{ status: string; conteo: InventoryConteo }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/inventario/conteos/${id}`);
return data;
},
async createConteo(payload: { notes?: string; lines: { item_id: number; qty_contado: number }[] }): Promise<{ status: string; conteo: InventoryConteo; ajustes: number }> {
const { data } = await axios.post(`${ODOO_BASE}/skeen/frontend/v1/inventario/conteos`, payload);
return data;
},
async getHorasAgenda(start?: string, end?: string): Promise<{ status: string; horas: HoraAgendaRow[] }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/reports/horas-agenda`, { params: { start, end } });
return data;