FU11: anchor cumulative cost at recording start; add daily-reset cost entities; prefill add-version form
frontend / frontend (push) Successful in 2m6s
pytest / test (push) Successful in 7m32s

- MQTT import_cost_total / export_revenue_total now anchor fixed-fee / tax-credit
  accrual at max(contract start, first recorded period), so standing charges for
  untracked pre-recording days are no longer folded into the running total.
- Add energy.import_cost_today / energy.export_revenue_today (monetary,
  total_increasing) that reset at server-local midnight, for per-day cost
  aggregation in Home Assistant alongside the running totals.
- ContractForm add-version mode prefills the contract's open version values, so
  only the fields that actually change need editing.
This commit is contained in:
2026-06-25 12:45:24 +02:00
parent b71009620a
commit 96e85fb48e
5 changed files with 689 additions and 63 deletions
+89
View File
@@ -246,4 +246,93 @@ describe('ContractForm', () => {
expect(onClose).toHaveBeenCalled()
})
it('prefills fields from the open version when in add-version mode', async () => {
// FU11: add-version mode should auto-prefill rate fields from the contract's
// current open version so the user only has to change what's different.
const CONTRACT_DETAIL = {
id: 42,
name: 'My Contract',
kind: 'manual',
active: true,
currency: 'EUR',
created_at: '2026-01-01T00:00:00Z',
updated_at: '2026-01-01T00:00:00Z',
versions: [
{
id: 1,
contract_id: 42,
effective_from: '2026-01-01T00:00:00Z',
effective_to: null, // open version
values: {
energy: {
buy: { normal: 0.133, dal: 0.127 },
sell: { normal: 0.05, dal: 0.05 },
energy_tax: 0.11,
ode: 0.0,
},
standing: {
network_fee: 30.0,
management_fee: 60.0,
},
credits: {
heffingskorting: 365.0,
},
},
created_at: '2026-01-01T00:00:00Z',
},
],
}
mockGet.mockImplementation((path: string) => {
if (path === '/api/energy/profiles') {
return Promise.resolve({ data: PROFILES_RESPONSE })
}
if (path === '/api/energy/contracts/{contract_id}') {
return Promise.resolve({ data: CONTRACT_DETAIL })
}
return Promise.resolve({ data: null })
})
const onClose = vi.fn()
const onSaved = vi.fn()
renderWithProviders(
<ContractForm contractId={42} defaultKind="manual" onClose={onClose} onSaved={onSaved} />,
)
const user = userEvent.setup()
// Wait for the form to render and profile fields to appear
await waitFor(() => {
expect(screen.getByTestId('contract-section-energy')).toBeInTheDocument()
}, { timeout: 3000 })
// Wait for prefill to apply: both profile and contract detail queries must resolve.
// Fill in the required effective_from date and submit; the submitted values
// body.values should contain the prefilled rates from the open version.
mockPost.mockResolvedValue({ data: {} })
const effectiveInput = screen.getByTestId('contract-effective-from')
await user.type(effectiveInput, '2026-07-01')
await user.click(screen.getByTestId('contract-form-submit'))
// The submitted body should contain the prefilled values from CONTRACT_DETAIL
await waitFor(() => {
expect(mockPost).toHaveBeenCalledWith(
'/api/energy/contracts/{contract_id}/versions',
expect.objectContaining({
body: expect.objectContaining({
values: expect.objectContaining({
standing: expect.objectContaining({
network_fee: 30,
management_fee: 60,
}),
}),
}),
}),
)
}, { timeout: 3000 })
})
})