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 }) => {