feat(rutinas): catálogo de rutinas reutilizable por familia
Normaliza las rutinas de tarde replicando el patrón de las mañanas: RoutineTask es el catálogo (gemela de Activity) y AfternoonRoutine pasa a ser pura asignación niño+día. Incluye migración Liquibase 003 con consolidación de duplicados y rollback, y la cascada de monedas (override del día -> catálogo -> coinsPerTask) en DayGenerationService.
This commit is contained in:
@@ -7,6 +7,7 @@ import es.asepeyo.recordalexia.domain.EventType;
|
||||
import es.asepeyo.recordalexia.domain.Family;
|
||||
import es.asepeyo.recordalexia.domain.MaterialItem;
|
||||
import es.asepeyo.recordalexia.domain.Reward;
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import es.asepeyo.recordalexia.domain.SpecialEvent;
|
||||
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
||||
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
||||
@@ -15,6 +16,7 @@ import es.asepeyo.recordalexia.repository.ChildRepository;
|
||||
import es.asepeyo.recordalexia.repository.FamilyRepository;
|
||||
import es.asepeyo.recordalexia.repository.MaterialItemRepository;
|
||||
import es.asepeyo.recordalexia.repository.RewardRepository;
|
||||
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||
import es.asepeyo.recordalexia.repository.SpecialEventRepository;
|
||||
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
||||
import java.time.Clock;
|
||||
@@ -51,6 +53,7 @@ public class DataSeeder implements ApplicationRunner {
|
||||
private final ActivityRepository activityRepository;
|
||||
private final WeeklyTemplateEntryRepository templateRepository;
|
||||
private final AfternoonRoutineRepository routineRepository;
|
||||
private final RoutineTaskRepository routineTaskRepository;
|
||||
private final SpecialEventRepository eventRepository;
|
||||
private final RewardRepository rewardRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
@@ -60,6 +63,7 @@ public class DataSeeder implements ApplicationRunner {
|
||||
MaterialItemRepository materialRepository, ActivityRepository activityRepository,
|
||||
WeeklyTemplateEntryRepository templateRepository,
|
||||
AfternoonRoutineRepository routineRepository,
|
||||
RoutineTaskRepository routineTaskRepository,
|
||||
SpecialEventRepository eventRepository, RewardRepository rewardRepository,
|
||||
PasswordEncoder passwordEncoder, Clock clock) {
|
||||
this.familyRepository = familyRepository;
|
||||
@@ -68,6 +72,7 @@ public class DataSeeder implements ApplicationRunner {
|
||||
this.activityRepository = activityRepository;
|
||||
this.templateRepository = templateRepository;
|
||||
this.routineRepository = routineRepository;
|
||||
this.routineTaskRepository = routineTaskRepository;
|
||||
this.eventRepository = eventRepository;
|
||||
this.rewardRepository = rewardRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
@@ -86,6 +91,7 @@ public class DataSeeder implements ApplicationRunner {
|
||||
|
||||
var materials = seedMaterials(family);
|
||||
var activities = seedActivities(family, materials);
|
||||
var routines = seedRoutineCatalog(family);
|
||||
seedRewards(family);
|
||||
|
||||
Child nora = childRepository.save(child(family, "Nora", "🦊", "#F2A65A", 7, 42, LocalTime.of(8, 30)));
|
||||
@@ -94,7 +100,7 @@ public class DataSeeder implements ApplicationRunner {
|
||||
|
||||
for (Child c : List.of(nora, leo, mia)) {
|
||||
seedWeeklyMornings(c, activities);
|
||||
seedAfternoonRoutines(c);
|
||||
seedAfternoonRoutines(c, routines);
|
||||
}
|
||||
|
||||
LocalDate today = LocalDate.now(clock);
|
||||
@@ -185,19 +191,31 @@ public class DataSeeder implements ApplicationRunner {
|
||||
templateRepository.save(new WeeklyTemplateEntry(child, day, activity, 0));
|
||||
}
|
||||
|
||||
private void seedAfternoonRoutines(Child child) {
|
||||
/** Catálogo de rutinas reutilizables de la familia (se definen una sola vez). */
|
||||
private RoutinesCatalog seedRoutineCatalog(Family family) {
|
||||
return new RoutinesCatalog(
|
||||
routineTask(family, "Deshacer la mochila", "Buidar la motxilla", "🎒", "#F2A65A"),
|
||||
routineTask(family, "Merendar", "Berenar", "🥪", "#F4C95D"),
|
||||
routineTask(family, "Hacer los deberes", "Fer els deures", "📝", "#5B8DEF"),
|
||||
routineTask(family, "Practicar piano", "Practicar piano", "🎹", "#A78BD0"),
|
||||
routineTask(family, "Recoger la mesa", "Parar taula", "🍽️", "#7FBF6B"));
|
||||
}
|
||||
|
||||
private RoutineTask routineTask(Family family, String es, String ca, String icon, String color) {
|
||||
RoutineTask rt = new RoutineTask(es, ca, icon, color);
|
||||
rt.setFamily(family);
|
||||
return routineTaskRepository.save(rt);
|
||||
}
|
||||
|
||||
/** Asigna las rutinas del catálogo a cada día laborable, reutilizando las mismas. */
|
||||
private void seedAfternoonRoutines(Child child, RoutinesCatalog r) {
|
||||
for (DayOfWeek day : List.of(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY,
|
||||
DayOfWeek.THURSDAY, DayOfWeek.FRIDAY)) {
|
||||
routineRepository.save(new AfternoonRoutine(child, day, "Deshacer la mochila",
|
||||
"Buidar la motxilla", "🎒", "#F2A65A", 0));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, "Merendar",
|
||||
"Berenar", "🥪", "#F4C95D", 1));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, "Hacer los deberes",
|
||||
"Fer els deures", "📝", "#5B8DEF", 2));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, "Practicar piano",
|
||||
"Practicar piano", "🎹", "#A78BD0", 3));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, "Recoger la mesa",
|
||||
"Parar taula", "🍽️", "#7FBF6B", 4));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, r.mochila, 0));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, r.merienda, 1));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, r.deberes, 2));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, r.piano, 3));
|
||||
routineRepository.save(new AfternoonRoutine(child, day, r.mesa, 4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,4 +228,8 @@ public class DataSeeder implements ApplicationRunner {
|
||||
private record ActivitiesCatalog(Activity gimnasia, Activity musica, Activity matematicas,
|
||||
Activity lengua) {
|
||||
}
|
||||
|
||||
private record RoutinesCatalog(RoutineTask mochila, RoutineTask merienda, RoutineTask deberes,
|
||||
RoutineTask piano, RoutineTask mesa) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@ import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.DayOfWeek;
|
||||
|
||||
/** Rutina de tarde recurrente de un niño para un día de la semana (reordenable). */
|
||||
/**
|
||||
* Asignación de una rutina del catálogo ({@link RoutineTask}) a un niño para un día de
|
||||
* la semana (reordenable). Es la gemela de tarde de {@link WeeklyTemplateEntry}: aquí
|
||||
* NO vive la definición (texto, emoji, color), que es reutilizable y se lee del
|
||||
* {@link RoutineTask} referenciado. Así una misma rutina sirve para varios días y niños.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "afternoon_routine")
|
||||
public class AfternoonRoutine {
|
||||
@@ -30,33 +35,24 @@ public class AfternoonRoutine {
|
||||
@Column(name = "day_of_week")
|
||||
private DayOfWeek dayOfWeek;
|
||||
|
||||
@Column(name = "label_es")
|
||||
private String labelEs;
|
||||
|
||||
@Column(name = "label_ca")
|
||||
private String labelCa;
|
||||
|
||||
private String icon;
|
||||
private String color;
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "routine_task_id")
|
||||
private RoutineTask routineTask;
|
||||
|
||||
@Column(name = "order_index")
|
||||
private int orderIndex;
|
||||
|
||||
/** Monedas de la rutina; si es null se usa coinsPerTask del niño. */
|
||||
/** Monedas para este día concreto; si es null se usa la cascada catálogo→niño. */
|
||||
@Column(name = "coins_reward")
|
||||
private Integer coinsReward;
|
||||
|
||||
protected AfternoonRoutine() {
|
||||
}
|
||||
|
||||
public AfternoonRoutine(Child child, DayOfWeek dayOfWeek, String labelEs, String labelCa,
|
||||
String icon, String color, int orderIndex) {
|
||||
public AfternoonRoutine(Child child, DayOfWeek dayOfWeek, RoutineTask routineTask, int orderIndex) {
|
||||
this.child = child;
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
this.labelEs = labelEs;
|
||||
this.labelCa = labelCa;
|
||||
this.icon = icon;
|
||||
this.color = color;
|
||||
this.routineTask = routineTask;
|
||||
this.orderIndex = orderIndex;
|
||||
}
|
||||
|
||||
@@ -80,36 +76,12 @@ public class AfternoonRoutine {
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
}
|
||||
|
||||
public String getLabelEs() {
|
||||
return labelEs;
|
||||
public RoutineTask getRoutineTask() {
|
||||
return routineTask;
|
||||
}
|
||||
|
||||
public void setLabelEs(String labelEs) {
|
||||
this.labelEs = labelEs;
|
||||
}
|
||||
|
||||
public String getLabelCa() {
|
||||
return labelCa;
|
||||
}
|
||||
|
||||
public void setLabelCa(String labelCa) {
|
||||
this.labelCa = labelCa;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(String color) {
|
||||
this.color = color;
|
||||
public void setRoutineTask(RoutineTask routineTask) {
|
||||
this.routineTask = routineTask;
|
||||
}
|
||||
|
||||
public int getOrderIndex() {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package es.asepeyo.recordalexia.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* Rutina reutilizable del catálogo de la familia (ej. "Merendar", "Hacer los deberes").
|
||||
* Es la DEFINICIÓN: texto bilingüe + emoji + color + monedas por defecto. La asignación
|
||||
* a un niño y a un día concreto se modela aparte en {@link AfternoonRoutine}, igual que
|
||||
* {@link Activity} (catálogo de mañana) se asigna mediante {@link WeeklyTemplateEntry}.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "routine_task")
|
||||
public class RoutineTask {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "family_id")
|
||||
private Family family;
|
||||
|
||||
@Column(name = "label_es")
|
||||
private String labelEs;
|
||||
|
||||
@Column(name = "label_ca")
|
||||
private String labelCa;
|
||||
|
||||
private String icon;
|
||||
private String color;
|
||||
|
||||
/** Monedas por defecto de la rutina; si es null, la asignación o el niño deciden. */
|
||||
@Column(name = "coins_reward")
|
||||
private Integer coinsReward;
|
||||
|
||||
protected RoutineTask() {
|
||||
}
|
||||
|
||||
public RoutineTask(String labelEs, String labelCa, String icon, String color) {
|
||||
this.labelEs = labelEs;
|
||||
this.labelCa = labelCa;
|
||||
this.icon = icon;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Family getFamily() {
|
||||
return family;
|
||||
}
|
||||
|
||||
public void setFamily(Family family) {
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
public String getLabelEs() {
|
||||
return labelEs;
|
||||
}
|
||||
|
||||
public void setLabelEs(String labelEs) {
|
||||
this.labelEs = labelEs;
|
||||
}
|
||||
|
||||
public String getLabelCa() {
|
||||
return labelCa;
|
||||
}
|
||||
|
||||
public void setLabelCa(String labelCa) {
|
||||
this.labelCa = labelCa;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Integer getCoinsReward() {
|
||||
return coinsReward;
|
||||
}
|
||||
|
||||
public void setCoinsReward(Integer coinsReward) {
|
||||
this.coinsReward = coinsReward;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package es.asepeyo.recordalexia.repository;
|
||||
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
/** Catálogo de rutinas de tarde, SIEMPRE scopeado a la familia (tenant). */
|
||||
public interface RoutineTaskRepository extends JpaRepository<RoutineTask, Long> {
|
||||
|
||||
List<RoutineTask> findByFamilyId(Long familyId);
|
||||
|
||||
Optional<RoutineTask> findByIdAndFamilyId(Long id, Long familyId);
|
||||
}
|
||||
@@ -73,16 +73,36 @@ public class DayGenerationService {
|
||||
monedas, TaskOrigin.TEMPLATE, entrada.getOrderIndex()));
|
||||
}
|
||||
|
||||
// --- Tarde: una tarea por cada rutina del día ---
|
||||
// --- Tarde: una tarea por cada rutina asignada al día ---
|
||||
// La definición (texto, emoji, color) se lee del catálogo (RoutineTask), igual
|
||||
// que la mañana la lee de Activity. Monedas con cascada: override del día →
|
||||
// valor por defecto del catálogo → coinsPerTask del niño.
|
||||
List<AfternoonRoutine> rutinas =
|
||||
routineRepository.findByChildIdAndDayOfWeekOrderByOrderIndexAsc(childId, dayOfWeek);
|
||||
for (AfternoonRoutine rutina : rutinas) {
|
||||
int monedas = rutina.getCoinsReward() != null ? rutina.getCoinsReward() : child.getCoinsPerTask();
|
||||
var tarea = rutina.getRoutineTask();
|
||||
int monedas = resolverMonedas(rutina.getCoinsReward(), tarea.getCoinsReward(),
|
||||
child.getCoinsPerTask());
|
||||
nuevas.add(new DailyTask(child, date, Slot.AFTERNOON,
|
||||
rutina.getLabelEs(), rutina.getLabelCa(), rutina.getIcon(), rutina.getColor(),
|
||||
tarea.getLabelEs(), tarea.getLabelCa(), tarea.getIcon(), tarea.getColor(),
|
||||
monedas, TaskOrigin.ROUTINE, rutina.getOrderIndex()));
|
||||
}
|
||||
|
||||
return dailyTaskRepository.saveAll(nuevas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resuelve las monedas de una rutina de tarde por precedencia: primero el override
|
||||
* del día (asignación), si no el valor por defecto del catálogo, y como último
|
||||
* recurso las monedas por tarea del niño.
|
||||
*/
|
||||
private int resolverMonedas(Integer overrideDia, Integer porDefectoCatalogo, int porTareaNino) {
|
||||
if (overrideDia != null) {
|
||||
return overrideDia;
|
||||
}
|
||||
if (porDefectoCatalogo != null) {
|
||||
return porDefectoCatalogo;
|
||||
}
|
||||
return porTareaNino;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# Catálogo reutilizable de rutinas de tarde.
|
||||
# Normaliza afternoon_routine: la definición (texto/emoji/color) pasa a routine_task
|
||||
# (catálogo por familia) y afternoon_routine queda como pura asignación niño+día.
|
||||
# Migra los datos existentes deduplicando las rutinas repetidas por día y familia.
|
||||
# Cada changeset lleva rollback explícito (Liquibase los revierte en orden inverso).
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 300-create-routine-task
|
||||
author: recordalexia
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: routine_task
|
||||
columns:
|
||||
- column: { name: id, type: BIGINT, autoIncrement: true, constraints: { primaryKey: true, nullable: false } }
|
||||
- column: { name: family_id, type: BIGINT, constraints: { nullable: false } }
|
||||
- column: { name: label_es, type: VARCHAR(120), constraints: { nullable: false } }
|
||||
- column: { name: label_ca, type: VARCHAR(120), constraints: { nullable: false } }
|
||||
- column: { name: icon, type: VARCHAR(16), constraints: { nullable: false } }
|
||||
- column: { name: color, type: VARCHAR(9), constraints: { nullable: false } }
|
||||
- column: { name: coins_reward, type: INT }
|
||||
- addForeignKeyConstraint:
|
||||
baseTableName: routine_task
|
||||
baseColumnNames: family_id
|
||||
referencedTableName: family
|
||||
referencedColumnNames: id
|
||||
constraintName: fk_routine_task_family
|
||||
onDelete: CASCADE
|
||||
rollback:
|
||||
- dropTable:
|
||||
tableName: routine_task
|
||||
|
||||
# Poblar el catálogo deduplicando las rutinas existentes por (familia + definición).
|
||||
# La familia se obtiene a través del niño dueño de la rutina.
|
||||
- changeSet:
|
||||
id: 301-seed-routine-task-from-existing
|
||||
author: recordalexia
|
||||
changes:
|
||||
- sql:
|
||||
sql: >-
|
||||
INSERT INTO routine_task (family_id, label_es, label_ca, icon, color, coins_reward)
|
||||
SELECT DISTINCT c.family_id, ar.label_es, ar.label_ca, ar.icon, ar.color, NULL
|
||||
FROM afternoon_routine ar
|
||||
JOIN child c ON c.id = ar.child_id;
|
||||
rollback:
|
||||
- sql:
|
||||
sql: DELETE FROM routine_task;
|
||||
|
||||
- changeSet:
|
||||
id: 302-add-routine-task-id
|
||||
author: recordalexia
|
||||
changes:
|
||||
- addColumn:
|
||||
tableName: afternoon_routine
|
||||
columns:
|
||||
- column: { name: routine_task_id, type: BIGINT }
|
||||
rollback:
|
||||
- dropColumn:
|
||||
tableName: afternoon_routine
|
||||
columnName: routine_task_id
|
||||
|
||||
# Enlazar cada asignación con su rutina de catálogo por (familia + definición exacta).
|
||||
- changeSet:
|
||||
id: 303-link-routine-task
|
||||
author: recordalexia
|
||||
changes:
|
||||
# Subconsulta correlacionada (SQL estándar, portable a H2 y PostgreSQL).
|
||||
- sql:
|
||||
sql: >-
|
||||
UPDATE afternoon_routine ar
|
||||
SET routine_task_id = (
|
||||
SELECT rt.id FROM routine_task rt
|
||||
WHERE rt.family_id = (SELECT c.family_id FROM child c WHERE c.id = ar.child_id)
|
||||
AND rt.label_es = ar.label_es
|
||||
AND rt.label_ca = ar.label_ca
|
||||
AND rt.icon = ar.icon
|
||||
AND rt.color = ar.color
|
||||
);
|
||||
rollback: [] # no-op: el enlace se deshace al recrear las columnas en 304
|
||||
|
||||
# Cerrar: NOT NULL + FK, y eliminar las columnas de definición ya migradas.
|
||||
- changeSet:
|
||||
id: 304-finalize-afternoon-routine
|
||||
author: recordalexia
|
||||
changes:
|
||||
- addNotNullConstraint:
|
||||
tableName: afternoon_routine
|
||||
columnName: routine_task_id
|
||||
columnDataType: BIGINT
|
||||
- addForeignKeyConstraint:
|
||||
baseTableName: afternoon_routine
|
||||
baseColumnNames: routine_task_id
|
||||
referencedTableName: routine_task
|
||||
referencedColumnNames: id
|
||||
constraintName: fk_routine_assignment_task
|
||||
- dropColumn: { tableName: afternoon_routine, columnName: label_es }
|
||||
- dropColumn: { tableName: afternoon_routine, columnName: label_ca }
|
||||
- dropColumn: { tableName: afternoon_routine, columnName: icon }
|
||||
- dropColumn: { tableName: afternoon_routine, columnName: color }
|
||||
rollback:
|
||||
# Recrear las columnas de definición y repoblarlas desde el catálogo.
|
||||
- addColumn:
|
||||
tableName: afternoon_routine
|
||||
columns:
|
||||
- column: { name: label_es, type: VARCHAR(120) }
|
||||
- column: { name: label_ca, type: VARCHAR(120) }
|
||||
- column: { name: icon, type: VARCHAR(16) }
|
||||
- column: { name: color, type: VARCHAR(9) }
|
||||
- sql:
|
||||
sql: >-
|
||||
UPDATE afternoon_routine ar
|
||||
SET label_es = (SELECT rt.label_es FROM routine_task rt WHERE rt.id = ar.routine_task_id),
|
||||
label_ca = (SELECT rt.label_ca FROM routine_task rt WHERE rt.id = ar.routine_task_id),
|
||||
icon = (SELECT rt.icon FROM routine_task rt WHERE rt.id = ar.routine_task_id),
|
||||
color = (SELECT rt.color FROM routine_task rt WHERE rt.id = ar.routine_task_id);
|
||||
- dropForeignKeyConstraint:
|
||||
baseTableName: afternoon_routine
|
||||
constraintName: fk_routine_assignment_task
|
||||
@@ -7,12 +7,14 @@ import es.asepeyo.recordalexia.domain.AfternoonRoutine;
|
||||
import es.asepeyo.recordalexia.domain.Child;
|
||||
import es.asepeyo.recordalexia.domain.DailyTask;
|
||||
import es.asepeyo.recordalexia.domain.Family;
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import es.asepeyo.recordalexia.domain.Slot;
|
||||
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
||||
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
||||
import es.asepeyo.recordalexia.repository.AfternoonRoutineRepository;
|
||||
import es.asepeyo.recordalexia.repository.ChildRepository;
|
||||
import es.asepeyo.recordalexia.repository.FamilyRepository;
|
||||
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
@@ -32,6 +34,7 @@ class DayGenerationServiceTest {
|
||||
@Autowired private ActivityRepository activityRepository;
|
||||
@Autowired private WeeklyTemplateEntryRepository templateRepository;
|
||||
@Autowired private AfternoonRoutineRepository routineRepository;
|
||||
@Autowired private RoutineTaskRepository routineTaskRepository;
|
||||
@Autowired private FamilyRepository familyRepository;
|
||||
|
||||
private Family family;
|
||||
@@ -48,7 +51,8 @@ class DayGenerationServiceTest {
|
||||
actividad.setFamily(family);
|
||||
Activity mates = activityRepository.save(actividad);
|
||||
templateRepository.save(new WeeklyTemplateEntry(nino, dia, mates, 0));
|
||||
routineRepository.save(new AfternoonRoutine(nino, dia, "Deberes", "Deures", "📝", "#F2A65A", 0));
|
||||
RoutineTask deberes = routineTaskRepository.save(routineTask("Deberes", "Deures", "📝", "#F2A65A"));
|
||||
routineRepository.save(new AfternoonRoutine(nino, dia, deberes, 0));
|
||||
|
||||
List<DailyTask> primera = dayGenerationService.generateIfAbsent(nino.getId(), fecha);
|
||||
|
||||
@@ -63,6 +67,45 @@ class DayGenerationServiceTest {
|
||||
assertThat(segunda).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aplicaLaCascadaDeMonedasDeLaRutina() {
|
||||
family = familyRepository.save(new Family("coins@x.com", "h", "C", "p"));
|
||||
LocalDate fecha = LocalDate.of(2026, 6, 22);
|
||||
DayOfWeek dia = fecha.getDayOfWeek();
|
||||
|
||||
// El niño rinde 10 por tarea por defecto.
|
||||
Child nino = childRepository.save(nuevoNino());
|
||||
nino.setCoinsPerTask(10);
|
||||
|
||||
// (a) Override por día (8) gana al catálogo (5) y al niño (10).
|
||||
RoutineTask conDefecto = routineTaskRepository.save(routineTask("Merienda", "Berenar", "🥪", "#F4C95D"));
|
||||
conDefecto.setCoinsReward(5);
|
||||
AfternoonRoutine conOverride = new AfternoonRoutine(nino, dia, conDefecto, 0);
|
||||
conOverride.setCoinsReward(8);
|
||||
routineRepository.save(conOverride);
|
||||
|
||||
// (b) Sin override → vale el del catálogo (5).
|
||||
RoutineTask soloCatalogo = routineTaskRepository.save(routineTask("Leer", "Llegir", "📖", "#F2A65A"));
|
||||
soloCatalogo.setCoinsReward(5);
|
||||
routineRepository.save(new AfternoonRoutine(nino, dia, soloCatalogo, 1));
|
||||
|
||||
// (c) Sin override ni catálogo → cae a coinsPerTask del niño (10).
|
||||
RoutineTask sinNada = routineTaskRepository.save(routineTask("Recoger", "Recollir", "🧹", "#7FBF6B"));
|
||||
routineRepository.save(new AfternoonRoutine(nino, dia, sinNada, 2));
|
||||
|
||||
List<DailyTask> tareas = dayGenerationService.generateIfAbsent(nino.getId(), fecha);
|
||||
|
||||
assertThat(tareas).filteredOn(t -> t.getSlot() == Slot.AFTERNOON)
|
||||
.extracting(DailyTask::getCoinsReward)
|
||||
.containsExactly(8, 5, 10);
|
||||
}
|
||||
|
||||
private RoutineTask routineTask(String es, String ca, String icon, String color) {
|
||||
RoutineTask rt = new RoutineTask(es, ca, icon, color);
|
||||
rt.setFamily(family);
|
||||
return rt;
|
||||
}
|
||||
|
||||
private Child nuevoNino() {
|
||||
Child c = new Child();
|
||||
c.setFamily(family);
|
||||
|
||||
@@ -14,6 +14,7 @@ import es.asepeyo.recordalexia.domain.Child;
|
||||
import es.asepeyo.recordalexia.domain.Family;
|
||||
import es.asepeyo.recordalexia.domain.FamilySession;
|
||||
import es.asepeyo.recordalexia.domain.Reward;
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
||||
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
||||
import es.asepeyo.recordalexia.repository.AfternoonRoutineRepository;
|
||||
@@ -21,6 +22,7 @@ import es.asepeyo.recordalexia.repository.ChildRepository;
|
||||
import es.asepeyo.recordalexia.repository.FamilyRepository;
|
||||
import es.asepeyo.recordalexia.repository.FamilySessionRepository;
|
||||
import es.asepeyo.recordalexia.repository.RewardRepository;
|
||||
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
||||
import es.asepeyo.recordalexia.security.SessionAuthFilter;
|
||||
import java.time.DayOfWeek;
|
||||
@@ -46,6 +48,7 @@ class TodayFlowIT {
|
||||
@Autowired private ActivityRepository activityRepository;
|
||||
@Autowired private WeeklyTemplateEntryRepository templateRepository;
|
||||
@Autowired private AfternoonRoutineRepository routineRepository;
|
||||
@Autowired private RoutineTaskRepository routineTaskRepository;
|
||||
@Autowired private RewardRepository rewardRepository;
|
||||
@Autowired private FamilyRepository familyRepository;
|
||||
@Autowired private FamilySessionRepository sessionRepository;
|
||||
@@ -62,7 +65,10 @@ class TodayFlowIT {
|
||||
mates.setFamily(family);
|
||||
mates = activityRepository.save(mates);
|
||||
templateRepository.save(new WeeklyTemplateEntry(nino, hoy, mates, 0));
|
||||
routineRepository.save(new AfternoonRoutine(nino, hoy, "Deberes", "Deures", "📝", "#F2A65A", 0));
|
||||
RoutineTask deberes = new RoutineTask("Deberes", "Deures", "📝", "#F2A65A");
|
||||
deberes.setFamily(family);
|
||||
deberes = routineTaskRepository.save(deberes);
|
||||
routineRepository.save(new AfternoonRoutine(nino, hoy, deberes, 0));
|
||||
Reward premio = new Reward("Tablet", "Tauleta", "🎮", "#5B8DEF", 5);
|
||||
premio.setFamily(family);
|
||||
premio = rewardRepository.save(premio);
|
||||
|
||||
Reference in New Issue
Block a user