Facturas recurrentes, edición, padding, y política de privacidad

- Servicio: nuevo campo esRecurrente + migración DB v2
- Al marcar pagada una factura recurrente se genera la siguiente
- Editar facturas existentes desde CrearFacturaScreen
- Nuevo método actualizarFactura en FacturaProvider
- padding inferior en listas de facturas y servicios (último item visible)
- Nota de factura visible en subtítulo
- Filtro Vencidas corregido
- Monto pendiente total visible en dashboard
- Botón undo para revertir factura pagada
- Configuración de release signing (keystore)
- Pantalla de política de privacidad en la app + PRIVACY_POLICY.md
This commit is contained in:
Super User
2026-06-15 21:45:59 -04:00
parent be28ef39cd
commit bbd20f66da
12 changed files with 260 additions and 16 deletions

View File

@@ -8,7 +8,8 @@ import '../providers/configuracion_provider.dart';
import '../utils/currency_utils.dart';
class CrearFacturaScreen extends StatefulWidget {
const CrearFacturaScreen({super.key});
final Factura? factura;
const CrearFacturaScreen({super.key, this.factura});
@override
State<CrearFacturaScreen> createState() => _CrearFacturaScreenState();
@@ -28,6 +29,15 @@ class _CrearFacturaScreenState extends State<CrearFacturaScreen> {
super.initState();
_montoCtrl = TextEditingController();
_notaCtrl = TextEditingController();
if (widget.factura != null) {
final f = widget.factura!;
_montoCtrl.text = f.monto.toString();
_notaCtrl.text = f.nota ?? '';
_servicioId = f.servicioId;
_fechaEmision = DateTime.parse(f.fechaEmision);
_fechaVencimiento = DateTime.parse(f.fechaVencimiento);
_estado = f.estado;
}
context.read<ServicioProvider>().cargarServicios();
}
@@ -47,7 +57,7 @@ class _CrearFacturaScreenState extends State<CrearFacturaScreen> {
final symbol = CurrencyUtils.getSymbol(moneda);
return Scaffold(
appBar: AppBar(title: const Text('Nueva factura')),
appBar: AppBar(title: Text(widget.factura != null ? 'Editar factura' : 'Nueva factura')),
body: Form(
key: _formKey,
child: ListView(
@@ -110,18 +120,23 @@ class _CrearFacturaScreenState extends State<CrearFacturaScreen> {
onPressed: () async {
if (!_formKey.currentState!.validate()) return;
final factura = Factura(
id: widget.factura?.id,
servicioId: _servicioId!,
monto: double.parse(_montoCtrl.text),
fechaEmision: DateFormat('yyyy-MM-dd').format(_fechaEmision),
fechaVencimiento: DateFormat('yyyy-MM-dd').format(_fechaVencimiento),
estado: _estado,
nota: _notaCtrl.text.isEmpty ? null : _notaCtrl.text,
fechaPago: _estado == 'Pagada' ? DateFormat('yyyy-MM-dd').format(DateTime.now()) : null,
fechaPago: widget.factura?.fechaPago ?? (_estado == 'Pagada' ? DateFormat('yyyy-MM-dd').format(DateTime.now()) : null),
);
await facturaProv.agregarFactura(factura);
if (widget.factura != null) {
await facturaProv.actualizarFactura(factura);
} else {
await facturaProv.agregarFactura(factura);
}
if (context.mounted) Navigator.pop(context);
},
child: const Text('Guardar factura'),
child: Text(widget.factura != null ? 'Guardar cambios' : 'Guardar factura'),
),
],
),