fase 2.2: impuestos configurables por instancia (tasa por servicio/articulo, calculo por linea, desglose en POS/tickets/estado de cuenta, reporte de impuestos, permiso ventas.impuestos) con defaults apagados

This commit is contained in:
2026-10-11 08:04:11 +00:00
parent 28a690aa0f
commit 358b85afcd
22 changed files with 1105 additions and 30 deletions

View File

@@ -316,6 +316,9 @@ export interface Service {
sequence?: number;
sold_90d?: number | null;
genera_monedero?: boolean;
/** Impuesto propio del servicio (%) y si usa la tasa por defecto de la config */
tax_rate?: number;
tax_use_default?: boolean;
}
export interface ImpactSimulationDoctor {
@@ -476,6 +479,9 @@ export interface SaleLine {
quantity: number;
price_unit: number;
subtotal: number;
/** Tasa (%) e importe del impuesto de la línea (0 con impuestos apagados) */
tax_rate?: number;
tax_amount?: number;
is_prescribed?: boolean;
prescribed_by_id?: number | null;
prescribed_by?: string | null;
@@ -531,6 +537,9 @@ export interface Sale {
subtotal: number;
discount: number;
tax: number;
/** Impuesto sumado de las líneas y base sin impuesto (0 = sin impuestos) */
tax_total?: number;
subtotal_sin_impuesto?: number;
total: number;
amount_paid: number;
amount_due: number;
@@ -757,6 +766,9 @@ export interface InventoryItem {
last_count_date: string | null;
active: boolean;
genera_monedero?: boolean;
/** Impuesto propio del artículo (%) y si usa la tasa por defecto */
tax_rate?: number;
tax_use_default?: boolean;
notes: string;
stock_level: InventoryLevel;
}
@@ -963,6 +975,27 @@ export interface ServiceCategory {
}
// ---- Ajustes de la clínica ----
export interface ImpuestosSettings {
/** Enciende el cálculo del impuesto en POS, tickets y reportes */
activo: boolean;
/** Etiqueta del impuesto (p. ej. 'IVA') */
nombre: string;
/** % aplicado cuando el catálogo no fija uno */
tasa_default: number;
/** El precio capturado ya trae impuesto: el subtotal se desglosa hacia atrás */
precios_incluyen_impuesto: boolean;
/** Tasas ofrecidas en los formularios (%) */
tasa_opciones: number[];
}
export const IMPUESTOS_DEFAULT: ImpuestosSettings = {
activo: false,
nombre: 'IVA',
tasa_default: 0,
precios_incluyen_impuesto: false,
tasa_opciones: [0, 8, 16],
};
export interface ClinicSettings {
nombre: string;
telefono: string;
@@ -976,6 +1009,7 @@ export interface ClinicSettings {
url_publica: string;
moneda_base: string;
nota_ticket: string;
impuestos: ImpuestosSettings;
}
export const CLINIC_SETTINGS_DEFAULT: ClinicSettings = {
@@ -991,6 +1025,7 @@ export const CLINIC_SETTINGS_DEFAULT: ClinicSettings = {
url_publica: '',
moneda_base: 'MXN',
nota_ticket: '',
impuestos: IMPUESTOS_DEFAULT,
};
export interface FrontendUser {
@@ -1407,18 +1442,39 @@ export interface AppNotification {
export interface PosCheckoutPayload {
partner_id: number;
lines: { service_id?: number; item_id?: number; paquete_id?: number; quantity: number; price_unit: number; description?: string; prescribed_by_id?: number }[];
lines: { service_id?: number; item_id?: number; paquete_id?: number; quantity: number; price_unit: number; description?: string; prescribed_by_id?: number; tax_rate?: number }[];
discount?: number;
/** Tasa de impuesto a nivel venta (opcional); cada línea puede traer la suya */
tax_rate?: number;
payment_method?: string;
amount_received?: number;
pay_with_points?: boolean;
currency?: 'MXN' | 'USD';
}
export interface ImpuestosDesglose {
activo: boolean;
nombre: string;
incluido: boolean;
subtotal: number;
descuento: number;
impuesto: number;
total: number;
subtotal_usd?: number;
impuesto_usd?: number;
total_usd?: number;
}
export interface PosCheckoutResult {
status: string;
sale: Sale;
payment: Payment | null;
/** Desglose de la venta (subtotal, descuento, impuesto, total) */
subtotal?: number;
descuento?: number;
impuesto?: number;
total?: number;
impuestos?: ImpuestosDesglose;
cambio: number;
/** Efectivo recibido en caja (MXN), para imprimir "Recibido" en el ticket */
recibido?: number;
@@ -1446,6 +1502,42 @@ export interface InventoryReportSummary {
critical: number;
}
// ---- Reporte de impuestos ----
export interface TaxMonthRow {
mes: string;
base: number;
impuesto: number;
ventas: number;
}
export interface TaxServiceRow {
servicio: string;
base: number;
impuesto: number;
cantidad: number;
}
export interface TaxBranchRow {
sucursal: string;
base: number;
impuesto: number;
}
export interface TaxReportResult {
status: string;
start: string;
end: string;
activo: boolean;
nombre: string;
incluido: boolean;
total_base: number;
total_impuesto: number;
total: number;
por_mes: TaxMonthRow[];
por_servicio: TaxServiceRow[];
por_sucursal: TaxBranchRow[];
}
export interface ApiResponse<T> {
status: string;
message?: string;
@@ -2517,6 +2609,11 @@ export const odooApi = {
return data;
},
async getTaxReport(start?: string, end?: string): Promise<TaxReportResult> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/reports/taxes`, { params: { start, end } });
return data;
},
// Tipo de cambio
async getExchangeRate(): Promise<{ status: string; current: { rate: number; date: string; source: string } | null; history: { id: number; date: string; rate: number; source: string }[] }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/exchange_rate`);