El diagrama de flujo para el proceso de facturación podría ser algo así:

Sobre el código Java que solicita,mmmmh, aunque no lo parezca, puede ser bastante extenso.
Para empezar, pide una clase
Cliente, que podría ser como esta:
public final class Cliente {
private String id;
private String nombre;
private String telefono;
private String domicilio;
public Cliente(String id, String nombre, String telefono, String domicilio) {
this.id = id;
this.nombre = nombre;
this.telefono = telefono;
this.domicilio = domicilio;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
public String getDomicilio() {
return domicilio;
}
public void setDomicilio(String domicilio) {
this.domicilio = domicilio;
}
@Override
public String toString() {
return String.format("ID: %s\t\t\t\t Nombre: %s\nTelef: %s\t\t\t Domicilio: %s",
id, nombre, telefono, domicilio);
}
}
Luego pide una clase
Factura, donde se debe asociar un
Cliente con una relación de artículos que se le facturan, así como sus cantidades e importes.
Además luego cada
Factura ha de mostrarse en pantalla indicando la relación de artículos, la suma total de las cantidades y la suma total de los importes.
Así que hay que pensar en como relacionamos todos estos datos de forma que luego fácilmente se puedan mostrar y hacer esos cálculos.
No se pide una clase
Artículo, así que a priori podemos usar un
String para este dato.
Precio de cada artículo un double y las cantidades un int, vale, ¿Pero como coleccionamos esos datos para que cada
Factura tenga varios artículos, cada uno con un precio y una cantidad facturada?
Podrían usarse tres arrays primitivos, pero como no sabemos de antemano cuántos artículos va a tener una factura, no nos sirve.
Tres ArrayLists nos solucionarían el problema, pero creo que para optimizar aún más el proceso, podemos crear una clase (aunque no la pida el enunciado) que relacione directamente esos tres datos juntos en un único objeto.
Podría llamarse
LineaFactura, porque representaría las distintas líneas de productos facturados que tendrá una factura.
public final class LineaFactura {
private String producto;
private double precio;
private int cantidad;
public LineaFactura(String producto, double precio, int cantidad) {
this.producto = producto;
this.precio = precio;
this.cantidad = cantidad;
}
//Para esta clase solo nos interesan tener dos getters
public int getCantidad() {
return cantidad;
}
public double getImporteLinea() {
return precio * cantidad;
}
@Override
public String toString() {
return String.format("%20s\t\t%5.2f\t%6d\t\t%5.2f", producto, precio, cantidad, getImporteLinea());
}
}
Gracias a esto, la clase
Factura ya no va a requerir de tres ArrayList, bastará con solo uno que almacene las distintas líneas de facturación
Esta clase
Factura va a requerir de un objeto
Cliente y también de una fecha, la cuál podemos pedirla por teclado o usar la clase
LocalDate y automáticamente coger la fecha actual del sistema.
Podemos añadirle también un atributo ID, un número que identifique una factura. No tiene porque ser un "dato real", podemos generar números al azar, ya que es simplemente una simulación.
Factura tendrá un método que se encargará de mostrar en pantalla todas las "líneas de factura", datos del cliente, fecha y calcular los totales.
import java.time.LocalDate;
import java.util.ArrayList;
public final class Factura {
private int numFactura;
private Cliente cliente;
private LocalDate fecha;
private ArrayList<LineaFactura> facturado;
public Factura(Cliente cliente) {
numFactura = (int) (Math.random() * 10000);//Numero de factura lo generamos al azar
this.cliente = cliente;
fecha = LocalDate.now();
facturado = new ArrayList<LineaFactura>();
}
public Cliente getCliente() {
return cliente;
}
public void agregarLinea(LineaFactura linea) {
facturado.add(linea);
}
public void imprimirFactura() {
/*
* Recorreremos la lista de productos facturados para
* mostrar los datos en pantalla de cada linea.
* De paso, iremos sumando el total de artículos
* y los importes de cada línea para mostrar al final
*/
int productosTotales = 0;
double importeTotal = 0d;
System.out.println("\n\t\t****** FACTURA #" + numFactura + " *****");
System.out.println("\n\tFecha: " + fecha.toString());
System.out.println("\n\t\tDatos del Cliente");
System.out.println("\t\t----- --- -------");
System.out.println(cliente);
System.out.println("\n\t\t\tProductos Facturados");
System.out.println("\t\t\t--------- ----------\n");
System.out.println(" LINEA\t\tPRODUCTO\t\tPRECIO\tCANTIDAD\tTOTAL LINEA");
System.out.println(" -----\t\t--------\t\t------\t--------\t-----------");
for (int i = 0; i < facturado.size(); i++) {
System.out.println("> #" + (i+1) + facturado.get(i));
productosTotales += facturado.get(i).getCantidad();
importeTotal += facturado.get(i).getImporteLinea();
}
System.out.println("\nTotal Articulos: " + productosTotales);
System.out.printf("Importa a facturar: %.2f", importeTotal);
System.out.println("\n\n\t\t****** FIN DE FACTURA *****");
}
/*
* Indica si esta factura carece de líneas.
* Será útil para decidir si esta factura
* debemos guardarla en el sistema o descartarla
*/
public boolean facturaEnBlanco() {
return facturado.isEmpty();
}
}
Bien, tenemos una clase
Cliente y una clase
Factura, que se apoya en una clase secundaria llamada
LineaFactura para organizar los datos de los artículos que se facturan.
Se podría comenzar con el programa principal, pero si nos fijamos en el enunciado nos dice que este va a tener "3 módulos": Cliente, Facturación y Finanzas
Es decir, el programa se va a dividir en tres partes: una para la gestión de clientes, otra para facturar artículos y otra que no estoy seguro de su cometido, pero supongo será para cosas como mostrar las facturas registradas en el sistema.
Esto implica mucho código, pues son tres subprogramas dentro de uno solo.
Para repartir el código en lugar de concentrarlo todo en una única clase principal, podemos crear más clases de apoyo que se encarguen de tareas específicas.
Por ejemplo, podemos hacer una clase que se encargue de la gestión de los
Clientes.
Esta clase tendrá un ArrayList de
Cliente y tendrá métodos para registar un cliente nuevo, para mostrar los clientes registrados, para buscar un cliente en concreto...
import java.util.ArrayList;
import java.util.Scanner;
public final class GestorClientes {
public ArrayList<Cliente> clientes;
private Scanner teclado;
public GestorClientes() {
teclado = new Scanner(System.in);
clientes = new ArrayList<Cliente>();
//Añadimos unos clientes predefinidos para comenzar el programa con algun dato cargado.
//El ID se autogenera según el tamaño del ArrayList
clientes.add(new Cliente("ID00" + clientes.size(), "Pablo Segura", "656901234", "C/Paloma nº12"));
clientes.add(new Cliente("ID00" + clientes.size(), "Laura Juan", "690579012", "C/Carretas nº23"));
clientes.add(new Cliente("ID00" + clientes.size(), "Jorge Morales", "677119854", "C/Girasol nº56"));
clientes.add(new Cliente("ID00" + clientes.size(), "Carmen Robledo", "609718131", "C/Real nº3"));
}
public Cliente registrarCliente() {
System.out.println("\n\t\tREGISTAR CLIENTE");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Telefono: ");
String telef = teclado.nextLine();
System.out.print("Domicilio: ");
String domi = teclado.nextLine();
Cliente nuevo = new Cliente("ID00" + clientes.size(), nombre, telef, domi);
clientes.add(nuevo);
System.out.println("\n\t\tCLIENTE REGISTRADO");
return nuevo;
}
public void listarClientes() {
System.out.println("\n\t\tLISTADO CLIENTES");
for (Cliente cl: clientes)
System.out.println(cl + "\n");
}
public Cliente buscarPorId() {
System.out.print("\nIntroduzca ID cliente: ");
String id = teclado.nextLine();
//Buscamos Cliente
for (Cliente cl: clientes)
if (cl.getId().equals(id))
return cl; //Encontrado
//Si bucle FOR termina sin retornar nada, es que no existe este cliente
return null;
}
public Cliente buscarPorNombre() {
System.out.print("\nIntroduzca nombre completo de cliente: ");
String nombre = teclado.nextLine();
//Buscamos Cliente
for (Cliente cl: clientes)
if (cl.getNombre().equals(nombre))
return cl; //Encontrado
//Si bucle FOR termina sin retornar nada, es que no existe este cliente
return null;
}
}
Y lo mismo con las Finanzas, una clase gestora con un ArrayList donde se almacenarán
Facturas y se encargue de mostrar listados, ya sea de todas las facturas como también facturas de clientes concretos.
import java.util.ArrayList;
public final class GestorFinanzas {
private ArrayList<Factura> facturas = new ArrayList<Factura>();
public void agregarFactura(Factura fc) {
facturas.add(fc);
}
public void listarFacturas() {
System.out.println("\n\t\tLISTADO GENERAL FACTURAS");
if (facturas.isEmpty())
System.out.println("No hay FACTURAS que mostrar");
else
for (Factura fc: facturas)
fc.imprimirFactura();
System.out.println("\n\t\tFIN DE LISTADO");
}
public void listarPorID(String id) {
boolean encontrado = false;
System.out.println("\n\tLISTADO FACTURAS CLIENTE: " + id);
for (Factura fc: facturas)
if (fc.getCliente().getId().equals(id)) {
encontrado = true;
fc.imprimirFactura();
}
if (!encontrado)
System.out.println("No hay facturas de este cliente");
}
public void listarPorNombre(String nombre) {
boolean encontrado = false;
System.out.println("\n\tLISTADO FACTURAS CLIENTE: " + nombre);
for (Factura fc: facturas)
if (fc.getCliente().getNombre().equals(nombre)) {
encontrado = true;
fc.imprimirFactura();
}
if (!encontrado)
System.out.println("No hay facturas de este cliente");
}
}
Esta clase, sin embargo, no puede encargarse de
crear facturas porque no tiene acceso a la lista de clientes que están en
GestorClientes.
Puede registrar
Facturas ya creadas, pero no crearlas por ella misma.
Así que la clase
GestorClientes hará las principales tareas del "módulo Cliente".
La clase
GestorFinanzas hará las del "módulo Finazas".
¿Y quién se encarga del "módulo Facturación"?
Pues esta tarea va a recaer en el propio programa principal, ya que es el que va a tener acceso tanto a
Clientes como al registro de
Facturas (mediante las clases gestoras correspondientes).
Total, el "módulo Facturación" es simplemente pedir datos para crear una
Factura, realmente no necesita una clase específica para eso.
Así que a continuación pongo la clase
Main principal.
Es muy extensa pero porque hay muchos menús.
Está el menú principal para escoger "módulo", y cada "módulo" tiene su propio submenú.
Todo está repartido en distintos métodos, así que en realidad el método main() principal es minúsculo.
Esto ayuda a entender mejor el código. Si uno quiere centrarse en leer y querer comprender un "módulo" en concreto, solo ha de irse a leer el método correspondiente a ese "módulo", en lugar de intentar vislumbrarlo en 200 líneas de código si se hubiera escrito todo junto.
import java.util.Scanner;
public final class Main {
private static Scanner teclado = new Scanner(System.in);
private static GestorClientes clientes = new GestorClientes();
private static GestorFinanzas finanzas = new GestorFinanzas();
public static void main(String[] args) {
int opcion = 0;
do {
System.out.println("\n\t\tMENU SISTEMA <<SEASHELL>>");
System.out.println("[1] -- Modulo Clientes");
System.out.println("[2] -- Modulo Facturacion");
System.out.println("[3] -- Modulo Finanzas");
System.out.println("[4] -- TERMINAR PROGRAMA");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
moduloClientes();
break;
case 2:
moduloFacturacion();
break;
case 3:
moduloFinanzas();
break;
case 4:
System.out.println("\n\t\tFIN DE PROGRAMA");
break;
default:
System.out.println("Opcion equivocada");
}
}while (opcion != 4);
}
private static void moduloClientes() {
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO CLIENTES");
System.out.println("[1] -- Registrar Cliente");
System.out.println("[2] -- Listar todos los Clientes");
System.out.println("[3] -- Buscar Cliente por ID");
System.out.println("[4] -- Buscar Cliente por Nombre");
System.out.println("[5] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
clientes.registrarCliente();
break;
case 2:
clientes.listarClientes();
break;
case 3:
Cliente buscaID = clientes.buscarPorId();
if (buscaID == null)
System.out.println("No hay Cliente con ese ID");
else
System.out.println(buscaID);
break;
case 4:
Cliente buscaNom = clientes.buscarPorNombre();
if (buscaNom == null)
System.out.println("No hay Cliente con ese Nombre");
else
System.out.println(buscaNom);
break;
case 5:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}
}while (opcion != 5);
}
private static void moduloFacturacion() {
Cliente cliente = null;
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO FACTURACION");
System.out.println("[1] -- Identificar Cliente por ID");
System.out.println("[2] -- Identificar Cliente por Nombre");
System.out.println("[3] -- Registrar Nuevo Cliente");
System.out.println("[4] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
cliente = clientes.buscarPorId();
if (cliente == null)
System.out.println("No hay Cliente con ese ID");
else
nuevaFactura(cliente);
break;
case 2:
cliente = clientes.buscarPorNombre();
if (cliente == null)
System.out.println("No hay Cliente con ese Nombre");
else
nuevaFactura(cliente);
break;
case 3:
cliente = clientes.registrarCliente();
nuevaFactura(cliente);
break;
case 4:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}
}while (opcion != 4);
}
private static void moduloFinanzas() {
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO FINANZAS");
System.out.println("[1] -- Listar Facturas por ID");
System.out.println("[2] -- Listar Facturas por Nombre");
System.out.println("[3] -- Listar todas las Facturas");
System.out.println("[4] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
Cliente buscaID = clientes.buscarPorId();
if (buscaID == null)
System.out.println("No hay Cliente con ese ID");
else
finanzas.listarPorID(buscaID.getId());
break;
case 2:
Cliente buscaNombre = clientes.buscarPorNombre();
if (buscaNombre == null)
System.out.println("No hay Cliente con ese Nombre");
else
finanzas.listarPorNombre(buscaNombre.getNombre());
break;
case 3:
finanzas.listarFacturas();
break;
case 4:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}
}while (opcion != 4);
}
private static void nuevaFactura(Cliente cliente) {
Factura nueva = new Factura(cliente);
String articulo = "";
do {
System.out.println("\nIntroduzca nuevo ARTICULO o pulse ENTER dejando el campo vacio para terminar.");
System.out.print("Nombre Articulo: ");
articulo = teclado.nextLine();
if (!articulo.isBlank()) {
//Pedimos datos para crear una LineaFactura
System.out.print("PVP del Artículo: ");
double pvp = Double.parseDouble(teclado.nextLine());
System.out.print("Cantidad unidades: ");
int cantidad = Integer.parseInt(teclado.nextLine());
nueva.agregarLinea(new LineaFactura(articulo, pvp, cantidad));
}
}while(!articulo.isBlank());
//Antes de guardar esta factura, comprobamos si tiene articulos registrados
if (!nueva.facturaEnBlanco()) {
finanzas.agregarFactura(nueva);
nueva.imprimirFactura();
System.out.println("\n\t\tFACTURA REGISTRADA");
}
}
}
He dado una explicación general de la lógica que se ha aplicado, sin entrar en cosas concretas porque entonces la explicación sería muy extensa y poco práctica.
Pero podéis preguntar cualquier duda que surja, cualquier cosa que no se entienda, instrucciones que sean desconocidas.., lo que sea ...
Un saludo.