Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
aa2eca4
Support 15-minute electricity market resolution and improve Smart-Me …
tvup Mar 25, 2026
667eef7
Fix token reuse bug in GetMeteringData - create new API instance when…
tvup Mar 25, 2026
0d6ba3b
A
tvup Mar 25, 2026
4ab5289
Remove env/config fallback for refresh token - tokens must come from …
tvup Mar 25, 2026
ffa39ad
bump eloverblikapi to v2.1.0 for compatibility
tvup Mar 28, 2026
ac795c6
Fix Smart-Me credentials using numeric instead of associative array k…
tvup Mar 28, 2026
aab51dc
Fix missing grid/Energinet tariffs in bill - use caller's dataSource …
tvup Mar 28, 2026
fb7b730
Filter DATAHUB tariffs and subscriptions by billing period to exclude…
tvup Mar 28, 2026
a1279ba
Fix charges cache key to include dataSource and skip empty cached res…
tvup Mar 28, 2026
65fa1ce
Use inline prices from DATAHUB tariffs instead of DatahubPriceList lo…
tvup Mar 28, 2026
85ff2e9
Fall back to DATAHUB for charges when POWERUSE returns empty results
tvup Mar 28, 2026
9ce078f
Preserve end_date value after form submission instead of resetting to…
tvup Mar 28, 2026
3131a1c
Handle 96-position (15-min) tariff prices from DATAHUB API
tvup Mar 28, 2026
a4979fe
Skip date filtering of DATAHUB charges when start/end dates are null
tvup Mar 28, 2026
3c47701
Preserve Smart-me checkbox state after form submission
tvup Mar 28, 2026
57d8841
Always try DatahubPriceList lookup before falling back to inline DATA…
tvup Mar 28, 2026
81f6f78
Fix findDatahubPriceList type hint to accept Eloquent Charge models
tvup Mar 28, 2026
4bafcb1
Add temporary debug logging for tariff price resolution
tvup Mar 28, 2026
fea3dc7
Add more debug logging for DatahubPriceList price resolution
tvup Mar 28, 2026
2072d57
temp: log first 8 price lookups per tariff
tvup Mar 28, 2026
1f08faa
temp: increase debug log to 30
tvup Mar 28, 2026
82f7de5
temp: log only hours >= 6
tvup Mar 28, 2026
1180b6c
temp: log hour distribution
tvup Mar 28, 2026
29638c7
bump eloverblikapi to v2.1.1 and update revision number
tvup Mar 28, 2026
77bafa2
Fix cumulative rounding error by rounding only after all intervals
tvup Mar 28, 2026
8ce8760
Persist overhead and subscription values in cookies across sessions
tvup Mar 28, 2026
4bf1a00
Add searchable select component for grid operator (#299)
tvup Mar 28, 2026
d9858a4
Add loading spinner to submit button on el form
tvup Mar 28, 2026
f039a5c
Update ci.yml
tvup Mar 28, 2026
7d7de2d
Update ci.yml
tvup Mar 28, 2026
6e2c28b
Update ci.yml
tvup Mar 28, 2026
4935d04
Add RefreshDatabase trait to tests missing database migrations
tvup Mar 28, 2026
fad7acb
update tariff and cost values to reflect new rates
tvup Mar 28, 2026
5475d36
Fix MeteringPointControllerTest by generating Passport keys in setUp
tvup Mar 28, 2026
29ce43a
Fix all PHPStan errors for clean CI
tvup Mar 28, 2026
e5792a0
fix password change command and clean imports in tests
tvup Mar 28, 2026
9fcfa27
fix HTML tag typo in energy data display section
tvup Mar 28, 2026
c1a0109
Update composer.lock for PHP 8.5 compatibility
tvup Mar 28, 2026
c01b0d1
chore(deps): update all patch dependencies (#294)
renovate[bot] Mar 28, 2026
05f8b9e
Pin paratest to ~7.8.5 to avoid phpunit 12 conflict on PHP 8.5
tvup Mar 28, 2026
38cf901
bump dev dependencies for testing and quality tools
tvup Mar 28, 2026
d4f5cc1
Upgrade to PHPUnit 13, PHPStan 2, and Larastan 3
tvup Mar 28, 2026
a10668d
Resolve merge conflicts from master
tvup Mar 28, 2026
bbda9f3
chore(deps): update all minor dependencies (#287)
renovate[bot] Mar 28, 2026
9c534b1
Remove redundant array_values() call in GetSpotPricesTest
tvup Mar 28, 2026
80bf40e
Fix GetSpotPricesTest for DayAheadPrices API migration
tvup Mar 28, 2026
f85b680
update dependencies to latest versions for security and features
tvup Mar 28, 2026
08f871b
Add charge_tariff_mappings table for deterministic tariff matching
tvup Mar 28, 2026
f2b1c83
Add Laravel Forge deployment status badge to README
tvup Mar 28, 2026
36583ef
fix filtering logic for note prefix matching in tests
tvup Mar 28, 2026
b70ab34
Fix fontsource import for Vite 7 compatibility
tvup Mar 28, 2026
ffcc949
Visual redesign: premium Scandinavian energy dashboard (#303)
tvup Mar 28, 2026
e8e3913
Merge master into develop, resolve conflicts keeping develop versions
tvup Mar 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
name: CI

on:
push:
pull_request:

workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Laravel Forge Site Deployment Status](https://img.shields.io/endpoint?url=https%3A%2F%2Fforge.laravel.com%2Fsite-badges%2F454ef724-eb70-4aaf-b396-ab7d9ca383cf&style=plastic)](https://forge.laravel.com/torben-evald-hansen/tall-hamlet-iuu/2642934)
# Introduction
This project provides an application with tools to
- calculate electricity bill before it's received from your vendor
Expand Down
2 changes: 1 addition & 1 deletion app/Console/Commands/ChargesManualLoad.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ChargesManualLoad extends Command
*
* @var string
*/
protected $description = 'Command description';
protected $description = 'Manually load hardcoded charges into cache';

/**
* Execute the console command.
Expand Down
82 changes: 82 additions & 0 deletions app/Console/Commands/Database/DbExport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace App\Console\Commands\Database;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class DbExport extends Command
{
/**
* @var string
*/
protected $signature = 'db:export
{--tables= : Comma-separated list of tables to export (default: all data tables)}';

/**
* @var string
*/
protected $description = 'Exports database tables to CSV files in database/fixtures for backup before data reload';

/** @var array<string> */
private array $defaultTables = [
'elspotprices',
'metering_points',
'charges',
'charge_prices',
'charge_groups',
'datahub_price_lists',
'users',
];

public function handle(): int
{
$tables = $this->option('tables')
? explode(',', $this->option('tables'))
: $this->defaultTables;

$fixturesPath = database_path('fixtures');

foreach ($tables as $table) {
$table = trim($table);

try {
$count = DB::table($table)->count();
} catch (\Illuminate\Database\QueryException $e) {
$this->error("Table {$table} does not exist");

return Command::FAILURE;
}

if ($count === 0) {
$this->warn("Skipping {$table} — table is empty");
continue;
}

$outputFile = $fixturesPath . '/poweruse-' . $table . '.csv';
$this->info("Exporting {$table} ({$count} rows) to {$outputFile}");

$handle = fopen($outputFile, 'w');
if ($handle === false) {
$this->error("Could not open {$outputFile} for writing");
continue;
}

foreach (DB::table($table)->get() as $row) {
/** @var array<string, mixed> $fields */
$fields = array_map(function ($value) {
return $value === null ? 'NULL' : (string) $value;
}, (array) $row);
fputcsv($handle, $fields);
}

fclose($handle);
$this->info("Exported {$table} successfully");
}

$this->newLine();
$this->info('Export complete. Files saved to ' . $fixturesPath);

return Command::SUCCESS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function handle() : int

$more = 1;
$i = 0;
$bar = $this->output->createProgressBar(266516);
$bar = $this->output->createProgressBar(423500);
$bar->start();
while ($more) {
$records = $datahubPriceListsService->requestAllDatahubPriceListsFromEnergiDataService(100, $i);
Expand Down
25 changes: 25 additions & 0 deletions app/Models/ChargeTariffMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class ChargeTariffMapping extends Model
{
protected $fillable = [
'charge_name',
'charge_owner_gln',
'datahub_note',
'datahub_gln',
'datahub_charge_type',
'datahub_description',
'verified',
];

protected function casts(): array
{
return [
'verified' => 'boolean',
];
}
}
66 changes: 63 additions & 3 deletions app/Services/GetPreliminaryInvoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Enums\SourceEnum;
use App\Exceptions\DataUnavailableException;
use App\Exceptions\MissingDataException;
use App\Models\ChargeTariffMapping;
use App\Models\DatahubPriceList;
use App\Models\User;
use App\Services\Interfaces\GetSpotPricesInterface;
Expand Down Expand Up @@ -508,14 +509,73 @@ private function findDatahubPriceList(array|\ArrayAccess $tariff, string $toDate
if (cache()->has($key)) {
$datahubPriceLists = cache($key);
} else {
$datahubPriceLists = $this->datahubPriceListService->getFromQuery(
$this->datahubPriceListService->getQueryForFetchingSpecificTariffFromDB($tariff['name'], $tariff['owner'], $tariff['description'] ?? '', $toDate, $fromDate)
);
$datahubPriceLists = collect();

$mapping = ChargeTariffMapping::where('charge_name', $tariff['name'])
->where('charge_owner_gln', $tariff['owner'])
->first();

if ($mapping) {
$datahubPriceLists = $this->datahubPriceListService->getFromQuery(
$this->datahubPriceListService->getQueryForFetchingSpecificTariffFromDB(
$mapping->datahub_note,
$mapping->datahub_gln,
$mapping->datahub_description ?? '',
$toDate,
$fromDate
)
);
}

if ($datahubPriceLists->isEmpty()) {
$datahubPriceLists = $this->datahubPriceListService->getFromQuery(
$this->datahubPriceListService->getQueryForFetchingSpecificTariffFromDB($tariff['name'], $tariff['owner'], $tariff['description'] ?? '', $toDate, $fromDate)
);

if ($datahubPriceLists->isNotEmpty() && !$mapping) {
$first = $datahubPriceLists->first();
ChargeTariffMapping::updateOrCreate(
['charge_name' => $tariff['name'], 'charge_owner_gln' => $tariff['owner']],
[
'datahub_note' => $first->Note,
'datahub_gln' => $first->GLN_Number,
'datahub_charge_type' => $first->ChargeType,
'datahub_description' => $first->Description,
'verified' => true,
]
);
}
}

if ($datahubPriceLists->isEmpty()) {
$datahubPriceLists = $this->datahubPriceListService->getFromQuery(
$this->datahubPriceListService->getQueryForFetchingTariffByGlnAndNameLike($tariff['name'], $tariff['owner'], $toDate, $fromDate)
);

if ($datahubPriceLists->isNotEmpty() && !$mapping) {
$first = $datahubPriceLists->first();
ChargeTariffMapping::updateOrCreate(
['charge_name' => $tariff['name'], 'charge_owner_gln' => $tariff['owner']],
[
'datahub_note' => $first->Note,
'datahub_gln' => $first->GLN_Number,
'datahub_charge_type' => $first->ChargeType,
'datahub_description' => $first->Description,
'verified' => false,
]
);
}
}

if ($datahubPriceLists->isEmpty()) {
logger()->warning('No DatahubPriceList match found for tariff', [
'name' => $tariff['name'],
'owner' => $tariff['owner'],
'fromDate' => $fromDate,
'toDate' => $toDate,
]);
}

if ($datahubPriceLists->isNotEmpty()) {
cache([$key => $datahubPriceLists], 2592000);
}
Expand Down
30 changes: 15 additions & 15 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,27 @@
"require": {
"php": ">=8.5.0",
"ext-curl": "*",
"laravel/framework": "^11.0",
"laravel/passport": "^12.0",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.5",
"spatie/laravel-html": "^3.6",
"tvup/eloverblikapi": "^v2.1.1"
"laravel/framework": "^12.56",
"laravel/passport": "^12.4",
"laravel/tinker": "^3.0",
"laravel/ui": "^4.6",
"spatie/laravel-html": "^3.13",
"tvup/eloverblikapi": "^2.1"
},
"require-dev": {
"brianium/paratest": "^7.19.2",
"fakerphp/faker": "^1.23",
"friendsofphp/php-cs-fixer": "^3.64",
"larastan/larastan": "^3.0",
"brianium/paratest": "^7.19",
"fakerphp/faker": "^1.24",
"friendsofphp/php-cs-fixer": "^3.94",
"larastan/larastan": "^3.9",
"laravel/boost": "^2.4",
"laravel/breeze": "^2.2",
"laravel/sail": "^1.35",
"laravel/breeze": "^2.4",
"laravel/sail": "^1.55",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.3",
"nunomaduro/collision": "^8.9",
"phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^13.0.0",
"spatie/laravel-ignition": "^2.6"
"phpunit/phpunit": "^13.0",
"spatie/laravel-ignition": "^2.12"
},
"autoload": {
"psr-4": {
Expand Down
Loading
Loading