diff --git a/assets/js/components/ChargingPlans/PlansSettings.vue b/assets/js/components/ChargingPlans/PlansSettings.vue
index cb2dbc5ec..9f181cf48 100644
--- a/assets/js/components/ChargingPlans/PlansSettings.vue
+++ b/assets/js/components/ChargingPlans/PlansSettings.vue
@@ -51,6 +51,9 @@
{{ selectedPreviewPlanTitle }}
+
+ {{ $t("main.targetCharge.goalReached") }}
+
{{ nextPlanTitle }}
@@ -168,6 +171,9 @@ export default defineComponent({
return options;
},
+ alreadyReached(): boolean {
+ return this.plan.duration === 0;
+ },
nextPlanTitle(): string {
return `${this.$t("main.targetCharge.nextPlan")} #${this.nextPlanId}`;
},
@@ -175,14 +181,14 @@ export default defineComponent({
watch: {
effectivePlanTime(newValue: string) {
if (null !== newValue) {
- this.fetchPlanDebounced();
+ this.updatePlanDebounced();
}
},
staticPlan: {
deep: true,
handler(vNew: StaticPlan, vOld: StaticPlan) {
if (!deepEqual(vNew, vOld)) {
- this.fetchPlanDebounced();
+ this.updatePlanDebounced();
}
},
},
@@ -191,20 +197,20 @@ export default defineComponent({
handler(vNew: RepeatingPlan[], vOld: RepeatingPlan[]) {
if (!deepEqual(vNew, vOld)) {
this.adjustPreviewId();
- this.fetchPlanDebounced();
+ this.updatePlanDebounced();
}
},
},
},
mounted(): void {
- this.fetchPlanDebounced();
+ this.updatePlanDebounced();
},
methods: {
selectPreviewPlan(id: number): void {
this.selectedPreviewId = id;
- this.updatePlanPreviewDebounced();
+ this.updatePlanDebounced();
},
- async fetchPlanDebounced() {
+ async updatePlanDebounced() {
if (this.noActivePlan) {
await this.updatePlanPreviewDebounced();
} else {
@@ -217,6 +223,7 @@ export default defineComponent({
}
},
async updateActivePlan(): Promise {
+ await this.updateTariff();
try {
const res = await this.apiFetchPlan(`loadpoints/${this.id}/plan`);
this.plan = res?.data.result ?? ({} as PlanWrapper);
@@ -224,7 +231,6 @@ export default defineComponent({
} catch (e) {
console.error(e);
}
- await this.updateTariff();
},
async fetchStaticPreviewSoc(plan: StaticSocPlan): Promise {
const timeISO = plan.time.toISOString();
@@ -260,9 +266,11 @@ export default defineComponent({
}
},
async updatePreviewPlan(): Promise {
- // only show preview of no plan is active
+ // only show preview if no plan is active
if (!this.noActivePlan) return;
+ await this.updateTariff();
+
try {
let planRes: PlanResponse | undefined = undefined;
@@ -295,7 +303,6 @@ export default defineComponent({
planRes = await this.fetchRepeatingPreview({ weekdays, soc, time, tz });
}
this.plan = planRes?.data.result ?? ({} as PlanWrapper);
- await this.updateTariff();
} catch (e) {
console.error(e);
}
diff --git a/i18n/de.toml b/i18n/de.toml
index a87185508..e87f0a775 100644
--- a/i18n/de.toml
+++ b/i18n/de.toml
@@ -658,6 +658,7 @@ costLimitIgnore = "Die eingestellte {limit} wird in diesem Zeitraum ignoriert."
currentPlan = "Aktiver Plan"
descriptionEnergy = "Bis wann sollen {targetEnergy} ins Fahrzeug geladen sein?"
descriptionSoc = "Wann soll das Fahrzeug auf {targetSoc} geladen sein?"
+goalReached = "Ladeziel bereits erreicht"
inactiveLabel = "Zielzeit"
nextPlan = "Nächster Plan"
notReachableInTime = "Zielzeit wird {overrun} später erreicht."
diff --git a/i18n/en.toml b/i18n/en.toml
index f2e14a253..b3e9c984f 100644
--- a/i18n/en.toml
+++ b/i18n/en.toml
@@ -658,6 +658,7 @@ costLimitIgnore = "The configured {limit} will be ignored during this period."
currentPlan = "Active plan"
descriptionEnergy = "Until when should {targetEnergy} be loaded into the vehicle?"
descriptionSoc = "When should the vehicle be charged to {targetSoc}?"
+goalReached = "Goal already reached"
inactiveLabel = "Target time"
nextPlan = "Next plan"
notReachableInTime = "Goal will be reached {overrun} later."
diff --git a/server/http_loadpoint_handler.go b/server/http_loadpoint_handler.go
index 80e3ab650..54807f32b 100644
--- a/server/http_loadpoint_handler.go
+++ b/server/http_loadpoint_handler.go
@@ -51,10 +51,6 @@ func planHandler(lp loadpoint.API) http.HandlerFunc {
goal, _ := lp.GetPlanGoal()
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
- if plan == nil {
- w.WriteHeader(http.StatusNotFound)
- return
- }
res := struct {
PlanId int `json:"planId"`
@@ -110,10 +106,6 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
maxPower := lp.EffectiveMaxPower()
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
- if plan == nil {
- w.WriteHeader(http.StatusNotFound)
- return
- }
res := struct {
PlanTime time.Time `json:"planTime"`
@@ -163,10 +155,6 @@ func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
maxPower := lp.EffectiveMaxPower()
requiredDuration := lp.GetPlanRequiredDuration(soc, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
- if plan == nil {
- w.WriteHeader(http.StatusNotFound)
- return
- }
res := struct {
PlanTime time.Time `json:"planTime"`
diff --git a/tests/plan.spec.js b/tests/plan.spec.js
index ffe1eb582..8a68692fd 100644
--- a/tests/plan.spec.js
+++ b/tests/plan.spec.js
@@ -544,6 +544,13 @@ test.describe("repeating", async () => {
await plan2.getByTestId("repeating-plan-apply").click();
await expect(modal.getByTestId("plan-preview-title")).toHaveText("Next plan #1");
await expect(modal.getByTestId("target-text")).toContainText("9:30 AM");
+
+ // set lower targets than vehicle soc (50%)
+ await plan1.getByTestId("static-plan-soc").selectOption("40%");
+ await plan1.getByTestId("static-plan-apply").click();
+ await plan2.getByTestId("repeating-plan-soc").selectOption("40%");
+ await plan2.getByTestId("repeating-plan-apply").click();
+ await expect(modal.getByTestId("plan-preview-title")).toHaveText("Goal already reached");
});
test("repeating plan persistence", async ({ page }) => {