This commit is contained in:
Mateusz Gruszczyński
2026-08-02 00:19:34 +02:00
parent 5299a7fc66
commit 84ceb23316
7 changed files with 299 additions and 38 deletions
+25 -16
View File
@@ -15,6 +15,7 @@ from ..extensions import db
from ..models import AppSetting, CompanySettings, FuelCard, FuelCardPolicy, FuelCardStationRule, FuelEntry, FuelStationCompany, FuelStationPoint, OrlenPrice, User, Vehicle
from ..services import POLISH_REGIONS, aggregate_ure_companies, calculate_costs, fetch_orlen_price, fetch_orlen_range, fetch_ure_stations, invoice_period
from ..station_catalog import sync_station_catalog
from ..orlen_sync import sync_orlen_prices
FUEL_TYPES = ("PB95", "PB98", "DIESEL", "LPG")
@@ -28,23 +29,31 @@ def orlen_prices():
@api.post('/orlen/sync')
@role_required('boss','admin')
def orlen_sync():
d=payload(); fuels=d.get('fuels',[]); fuels=[fuels] if isinstance(fuels,str) else fuels; fuels=[f.upper() for f in fuels if f.upper() in FUEL_TYPES]; year=int(d.get('year',date.today().year))
regions=d.get('regions',[]); regions=[regions] if isinstance(regions,str) else regions; regions=[str(r).strip().lower() for r in regions if str(r).strip().lower() in POLISH_REGIONS]
if not fuels:return fail('Wybierz co najmniej jedno paliwo')
settings=CompanySettings.query.first(); result={}; total=0
d = payload()
fuels = d.get('fuels', [])
fuels = [fuels] if isinstance(fuels, str) else fuels
fuels = [fuel.upper() for fuel in fuels if fuel.upper() in FUEL_TYPES]
regions = d.get('regions', [])
regions = [regions] if isinstance(regions, str) else regions
regions = [str(region).strip().lower() for region in regions if str(region).strip().lower() in POLISH_REGIONS]
if not fuels:
return fail('Wybierz co najmniej jedno paliwo')
try:
for fuel in fuels:
added=updated=0
for row in fetch_orlen_range(fuel,date(year,1,1),min(date(year,12,31),date.today()),'all' if fuel=='LPG' else settings.region):
if fuel=='LPG' and regions and row.get('region') not in regions: continue
x=OrlenPrice.query.filter_by(fuel_type=row['fuel_type'],effective_date=row['effective_date'],region=row['region']).first()
if x:
for k,v in row.items():setattr(x,k,v)
x.fetched_at=datetime.utcnow();updated+=1
else:db.session.add(OrlenPrice(**row));added+=1
result[fuel]={'added':added,'updated':updated};total+=added+updated
db.session.commit();return response(result,f'Przetworzono {total} rekordów')
except Exception as exc:db.session.rollback();return fail(str(exc),502)
year = int(d.get('year', date.today().year))
settings = CompanySettings.query.first()
result, total = sync_orlen_prices(
fuels=fuels,
year=year,
regions=regions,
default_region=settings.region,
full_refresh=boolv(d, 'full_refresh'),
)
db.session.commit()
return response(result, f'Przetworzono {total} rekordów')
except Exception as exc:
db.session.rollback()
return fail(str(exc), 502)
@api.get('/orlen/preview')
@login_required
+25 -18
View File
@@ -13,6 +13,7 @@ from .station_catalog import sync_station_catalog
from . import THEMES
from .vat import entry_vat_rate, freeze_vat_action, global_vat_override, save_global_vat_override
from .settlement import calculate as calculate_snapshot_settlement, freeze as freeze_settlement
from .orlen_sync import sync_orlen_prices
main = Blueprint("main", __name__)
FUEL_TYPES = ("PB95", "PB98", "DIESEL", "LPG")
@@ -499,28 +500,34 @@ def orlen_data():
@login_required
@roles("boss", "admin")
def orlen_sync():
payload = request.get_json(silent=True) or request.form
fuels = payload.get("fuels", [])
if isinstance(fuels, str): fuels = [fuels]
fuels = [f.upper() for f in fuels if f.upper() in FUEL_TYPES]
year = int(payload.get("year", date.today().year))
if not fuels: return jsonify({"ok": False, "error": "Wybierz co najmniej jedno paliwo."}), 400
settings = CompanySettings.query.first(); start = date(year,1,1); end = min(date(year,12,31), date.today())
result = {}; total = 0
data = request.get_json(silent=True) or request.form
fuels = data.get("fuels", [])
if isinstance(fuels, str):
fuels = [fuels]
fuels = [fuel.upper() for fuel in fuels if fuel.upper() in FUEL_TYPES]
if not fuels:
return jsonify({"ok": False, "error": "Wybierz co najmniej jedno paliwo."}), 400
regions = data.get("regions", [])
if isinstance(regions, str):
regions = [regions]
regions = [str(region).strip().lower() for region in regions if str(region).strip().lower() in POLISH_REGIONS]
full_refresh = data.get("full_refresh") in (True, 1, "1", "true", "on", "yes")
try:
for fuel in fuels:
imported = 0; updated = 0
for row in fetch_orlen_range(fuel, start, end, "all" if fuel == "LPG" else settings.region):
existing = OrlenPrice.query.filter_by(fuel_type=row["fuel_type"], effective_date=row["effective_date"], region=row["region"]).first()
if existing:
existing.price_per_liter=row["price_per_liter"]; existing.raw_value=row["raw_value"]; existing.product_name=row["product_name"]; existing.source=row["source"]; existing.fetched_at=datetime.utcnow(); updated += 1
else:
db.session.add(OrlenPrice(**row)); imported += 1
result[fuel] = {"added": imported, "updated": updated}; total += imported + updated
settings = CompanySettings.query.first()
result, total = sync_orlen_prices(
fuels=fuels,
year=int(data.get("year", date.today().year)),
regions=regions,
default_region=settings.region,
full_refresh=full_refresh,
)
db.session.commit()
return jsonify({"ok": True, "message": f"Przetworzono {total} rekordów.", "result": result})
except Exception as exc:
db.session.rollback(); return jsonify({"ok": False, "error": str(exc)}), 502
db.session.rollback()
return jsonify({"ok": False, "error": str(exc)}), 502
+91
View File
@@ -0,0 +1,91 @@
from datetime import date, datetime
from sqlalchemy import func
from .extensions import db
from .models import OrlenPrice
from .services import fetch_orlen_range
def _year_bounds(year, today=None):
today = today or date.today()
start = date(year, 1, 1)
end = min(date(year, 12, 31), today)
if end < start:
raise ValueError("Nie można synchronizować danych dla przyszłego roku")
return start, end
def _latest_date(fuel, start, end, region=""):
query = db.session.query(func.max(OrlenPrice.effective_date)).filter(
OrlenPrice.fuel_type == fuel,
OrlenPrice.effective_date.between(start, end),
)
if fuel == "LPG":
query = query.filter(OrlenPrice.region == region)
else:
query = query.filter(OrlenPrice.region == "")
return query.scalar()
def _sync_start(fuel, start, end, regions, full_refresh):
if full_refresh:
return start
if fuel != "LPG":
return _latest_date(fuel, start, end) or start
latest_dates = [_latest_date("LPG", start, end, region) for region in regions]
return min((value or start) for value in latest_dates)
def sync_orlen_prices(fuels, year, regions, default_region, full_refresh=False, today=None):
start, end = _year_bounds(year, today=today)
target_regions = [str(region).strip().lower() for region in regions if str(region).strip()]
if not target_regions:
target_regions = [str(default_region).strip().lower()]
result = {}
total = 0
for fuel in fuels:
fuel_regions = target_regions if fuel == "LPG" else []
date_from = _sync_start(fuel, start, end, fuel_regions, full_refresh)
added = 0
updated = 0
rows = fetch_orlen_range(
fuel,
date_from,
end,
"all" if fuel == "LPG" else default_region,
)
for row in rows:
if fuel == "LPG" and row.get("region") not in fuel_regions:
continue
existing = OrlenPrice.query.filter_by(
fuel_type=row["fuel_type"],
effective_date=row["effective_date"],
region=row["region"],
).first()
if existing:
for key, value in row.items():
setattr(existing, key, value)
existing.fetched_at = datetime.utcnow()
updated += 1
else:
db.session.add(OrlenPrice(**row))
added += 1
processed = added + updated
result[fuel] = {
"added": added,
"updated": updated,
"from": date_from.isoformat(),
"to": end.isoformat(),
"full_refresh": bool(full_refresh),
}
total += processed
return result, total
+1 -1
View File
@@ -1,2 +1,2 @@
(()=>{const F=FuelTrack;F.bindLpgRegionPicker=()=>{const select=F.qs('#lpg-region-select');if(!select||select.dataset.choicesBound)return;select.dataset.choicesBound='1';const choices=new Choices(select,{removeItemButton:true,searchEnabled:true,shouldSort:false,position:'bottom',placeholder:true,placeholderValue:'Wybierz województwa',noResultsText:'Brak województwa',noChoicesText:'Brak opcji',itemSelectText:'Wybierz'});const modal=F.qs('#lpgRegionsModal');modal?.addEventListener('hidden.bs.modal',()=>{const count=F.qsa('option:checked',select).length;const badge=F.qs('#lpg-region-count');if(badge)badge.textContent=count?`${count} wybrano`:'Domyślne województwo'})};F.bindOrlenSync=()=>{const btn=F.qs('#orlen-sync-btn');if(!btn||btn.dataset.bound)return;btn.dataset.bound='1';btn.addEventListener('click',async()=>{const form=F.qs('#orlen-filter-form'),fuels=F.qsa('[name=fuel]:checked',form).map(x=>x.value),year=F.qs('[name=year]',form).value,status=F.qs('#orlen-sync-status');if(!fuels.length){status.textContent='Wybierz co najmniej jedno paliwo.';return}btn.disabled=true;const old=btn.innerHTML;btn.innerHTML='Pobieranie…';try{const regions=F.qsa('[name=region] option:checked',form).map(x=>x.value);const r=await fetch('/api/orlen/sync',{method:'POST',headers:{'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest'},body:JSON.stringify({fuels,year,regions})}),d=await r.json();if(!r.ok)throw new Error(d.error||'Błąd pobierania');status.textContent=d.message;await F.replaceMain(`/orlen?${new URLSearchParams(new FormData(form))}`,{push:true});F.notify(d.message)}catch(e){status.textContent=`Błąd: ${e.message}`;F.notify(e.message,'danger')}finally{btn.disabled=false;btn.innerHTML=old}})}})();
(()=>{const F=FuelTrack;F.bindLpgRegionPicker=()=>{const select=F.qs('#lpg-region-select');if(!select||select.dataset.choicesBound)return;select.dataset.choicesBound='1';new Choices(select,{removeItemButton:true,searchEnabled:true,shouldSort:false,position:'bottom',placeholder:true,placeholderValue:'Wybierz województwa',noResultsText:'Brak województwa',noChoicesText:'Brak opcji',itemSelectText:'Wybierz'});const modal=F.qs('#lpgRegionsModal');modal?.addEventListener('hidden.bs.modal',()=>{const count=F.qsa('option:checked',select).length;const badge=F.qs('#lpg-region-count');if(badge)badge.textContent=count?`${count} wybrano`:'Domyślne województwo'})};F.bindOrlenSync=()=>{const incrementalBtn=F.qs('#orlen-sync-btn'),fullBtn=F.qs('#orlen-full-sync-btn');if(!incrementalBtn||incrementalBtn.dataset.bound)return;incrementalBtn.dataset.bound='1';if(fullBtn)fullBtn.dataset.bound='1';const sync=async fullRefresh=>{const form=F.qs('#orlen-filter-form'),fuels=F.qsa('[name=fuel]:checked',form).map(x=>x.value),year=F.qs('[name=year]',form).value,status=F.qs('#orlen-sync-status');if(!fuels.length){status.textContent='Wybierz co najmniej jedno paliwo.';return}if(fullRefresh&&!confirm(`Pobrać ponownie wszystkie dane dla ${year} roku? Operacja może potrwać dłużej.`))return;const regions=F.qsa('[name=region] option:checked',form).map(x=>x.value),old=incrementalBtn.innerHTML;incrementalBtn.disabled=true;if(fullBtn)fullBtn.disabled=true;incrementalBtn.innerHTML=fullRefresh?'Pełne pobieranie…':'Pobieranie…';try{const r=await fetch('/api/orlen/sync',{method:'POST',headers:{'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest'},body:JSON.stringify({fuels,year,regions,full_refresh:fullRefresh})}),d=await r.json();if(!r.ok||!d.ok)throw new Error(d.error||d.message||'Błąd pobierania');status.textContent=d.message;await F.replaceMain(`/orlen?${new URLSearchParams(new FormData(form))}`,{push:true});F.notify(d.message)}catch(e){status.textContent=`Błąd: ${e.message}`;F.notify(e.message,'danger')}finally{incrementalBtn.disabled=false;if(fullBtn)fullBtn.disabled=false;incrementalBtn.innerHTML=old}};incrementalBtn.addEventListener('click',()=>sync(false));fullBtn?.addEventListener('click',()=>sync(true))}})();
;(()=>{const F=FuelTrack;F.bindOrlenComparison=()=>{const type=F.qs('#comparison-type'),fuel=F.qs('#compare-fuel');if(type&&!type.dataset.compareBound){type.dataset.compareBound='1';const update=()=>{F.qs('#year-comparison-fields')?.classList.toggle('d-none',type.value!=='years');F.qs('#period-comparison-fields')?.classList.toggle('d-none',type.value!=='periods')};type.addEventListener('change',update);update()}if(fuel&&!fuel.dataset.compareBound){fuel.dataset.compareBound='1';const update=()=>F.qs('#compare-region-wrap')?.classList.toggle('d-none',fuel.value!=='LPG');fuel.addEventListener('change',update);update()}}})();
+2 -2
View File
@@ -13,7 +13,7 @@
{% else %}<tr><td colspan="8" class="text-center text-body-secondary py-4">Brak tankowań spełniających kryteria.</td></tr>{% endfor %}</tbody></table></div>
{% if entries_page.pages > 1 %}<nav><ul class="pagination mb-0">{% for p in entries_page.iter_pages() %}{% if p %}<li class="page-item {% if p==entries_page.page %}active{% endif %}"><a class="page-link ajax-nav-link" href="{{url_for('main.admin_fuel_entries',page=p,q=q,month=month,company_id=company_id)}}">{{p}}</a></li>{% else %}<li class="page-item disabled"><span class="page-link"></span></li>{% endif %}{% endfor %}</ul></nav>{% endif %}
</div></div>
<div class="modal fade" id="fuelEntryEditModal" tabindex="-1"><div class="modal-dialog modal-lg modal-dialog-scrollable"><div class="modal-content"><form id="fuel-entry-edit-form" class="ajax-form" data-method="PUT"><div class="modal-header"><h2 class="modal-title fs-5">Edytuj tankowanie</h2><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="row g-3">
<div class="modal fade" id="fuelEntryEditModal" tabindex="-1"><div class="modal-dialog modal-lg modal-dialog-scrollable"><form id="fuel-entry-edit-form" class="modal-content ajax-form" data-method="PUT"><div class="modal-header"><h2 class="modal-title fs-5">Edytuj tankowanie</h2><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="row g-3">
<div class="col-md-6"><label class="form-label">Pojazd</label><select class="form-select" name="vehicle_id" required>{% for v in vehicles %}<option value="{{v.id}}">{{v.name}} · {{v.registration}}</option>{% endfor %}</select></div>
<div class="col-md-6"><label class="form-label">Data i godzina</label><input class="form-control" type="datetime-local" name="fueled_at" required></div>
<div class="col-md-4"><label class="form-label">Paliwo</label><select class="form-select" name="fuel_type">{% for f in ['PB95','PB98','DIESEL','LPG'] %}<option>{{f}}</option>{% endfor %}</select></div>
@@ -36,5 +36,5 @@
<div class="col-md-3"><label class="form-label">Dopłata netto / l</label><input class="form-control" type="number" step="0.0001" name="snapshot_surcharge_per_liter" required></div>
<div class="col-md-6"><div class="form-check mt-4"><input class="form-check-input" type="checkbox" name="snapshot_uses_last_price" id="edit-snapshot-last-price"><label class="form-check-label" for="edit-snapshot-last-price">Użyj zapisanej ceny hurtowej jako Last Price</label></div></div>
<div class="col-md-6"><label class="form-label">Źródło / opis reguły</label><input class="form-control" name="snapshot_rule_source" maxlength="160"></div>
</div></div><div class="modal-footer"><button type="button" class="btn btn-danger me-auto" id="fuel-entry-delete-btn">Usuń tankowanie</button><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuluj</button><button class="btn btn-primary">Zapisz zmiany</button></div></form></div></div></div>
</div></div><div class="modal-footer"><button type="button" class="btn btn-danger me-auto" id="fuel-entry-delete-btn">Usuń tankowanie</button><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuluj</button><button class="btn btn-primary">Zapisz zmiany</button></div></form></div></div>
{% endblock %}
+12 -1
View File
@@ -20,7 +20,18 @@
<div class="col-md-3"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="gross" value="1" id="gross-price" {% if include_vat %}checked{% endif %}><label class="form-check-label" for="gross-price">Dolicz VAT</label></div></div>
<div class="col-md-3"><button class="btn btn-secondary w-100">Pokaż dane</button></div>
</form>
{% if current_user.role in ['boss','admin'] %}<div class="d-flex align-items-center gap-3 mt-3"><button type="button" id="orlen-sync-btn" class="btn btn-primary">Pobierz z API</button><span id="orlen-sync-status" class="small text-body-secondary" aria-live="polite"></span></div>{% endif %}
{% if current_user.role in ['boss','admin'] %}
<div class="d-flex flex-wrap align-items-center gap-3 mt-3">
<div class="btn-group">
<button type="button" id="orlen-sync-btn" class="btn btn-primary">Pobierz nowe dane</button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"><span class="visually-hidden">Opcje pobierania</span></button>
<ul class="dropdown-menu">
<li><button type="button" id="orlen-full-sync-btn" class="dropdown-item">Pobierz cały wybrany rok ponownie</button></li>
</ul>
</div>
<span id="orlen-sync-status" class="small text-body-secondary" aria-live="polite"></span>
</div>
{% endif %}
</div></div>
<div class="modal fade lpg-region-modal" id="lpgRegionsModal" tabindex="-1" aria-hidden="true"><div class="modal-dialog modal-lg modal-dialog-centered"><div class="modal-content"><div class="modal-header"><h2 class="modal-title fs-5">Województwa LPG</h2><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><label class="form-label" for="lpg-region-select">Wybierz jedno lub wiele województw</label><select class="form-select" name="region" id="lpg-region-select" form="orlen-filter-form" multiple>{% for r in regions %}<option value="{{r}}" {% if r in selected_regions %}selected{% endif %}>{{r}}</option>{% endfor %}</select></div><div class="modal-footer"><button type="button" class="btn btn-primary" data-bs-dismiss="modal">Gotowe</button></div></div></div></div>
+143
View File
@@ -362,3 +362,146 @@ def test_last_price_zero_and_full_vat_deduction():
from app.domain.costs import calculate_net_price_costs
assert round(float(calculate_net_price_costs(10, 23, 0)["effective_cost"]), 2) == 12.30
assert round(float(calculate_net_price_costs(10, 23, 100)["effective_cost"]), 2) == 10.00
def test_fuel_entry_edit_modal_uses_scrollable_bootstrap_structure(tmp_path):
app = make_app(tmp_path, "fuel-entry-modal.db")
client = app.test_client()
client.post('/login', data={'email': 'admin@example.com', 'password': 'admin123!'})
html = client.get('/admin/fuel-entries').get_data(as_text=True)
assert (
'modal-dialog modal-lg modal-dialog-scrollable">'
'<form id="fuel-entry-edit-form" class="modal-content ajax-form"'
in html
)
assert '<div class="modal-content"><form id="fuel-entry-edit-form"' not in html
def test_orlen_incremental_sync_starts_at_latest_saved_record(tmp_path, monkeypatch):
from datetime import date
from decimal import Decimal
from app.extensions import db
from app.models import OrlenPrice
from app.orlen_sync import sync_orlen_prices
import app.orlen_sync as orlen_sync_module
app = make_app(tmp_path, "orlen-incremental.db")
calls = []
def fake_fetch(fuel, date_from, date_to, region):
calls.append((fuel, date_from, date_to, region))
return []
monkeypatch.setattr(orlen_sync_module, 'fetch_orlen_range', fake_fetch)
with app.app_context():
db.session.add_all([
OrlenPrice(fuel_type='PB95', effective_date=date(2026, 1, 2), price_per_liter=Decimal('4.1000'), raw_value=Decimal('4100'), region='', product_name='PB95', source='test'),
OrlenPrice(fuel_type='PB95', effective_date=date(2026, 7, 15), price_per_liter=Decimal('4.5000'), raw_value=Decimal('4500'), region='', product_name='PB95', source='test'),
])
db.session.commit()
result, total = sync_orlen_prices(
fuels=['PB95'],
year=2026,
regions=[],
default_region='mazowieckie',
full_refresh=False,
today=date(2026, 8, 2),
)
assert calls == [('PB95', date(2026, 7, 15), date(2026, 8, 2), 'mazowieckie')]
assert result['PB95']['from'] == '2026-07-15'
assert result['PB95']['full_refresh'] is False
assert total == 0
def test_orlen_full_refresh_starts_at_beginning_of_selected_year(tmp_path, monkeypatch):
from datetime import date
from decimal import Decimal
from app.extensions import db
from app.models import OrlenPrice
from app.orlen_sync import sync_orlen_prices
import app.orlen_sync as orlen_sync_module
app = make_app(tmp_path, "orlen-full-refresh.db")
calls = []
def fake_fetch(fuel, date_from, date_to, region):
calls.append((fuel, date_from, date_to, region))
return []
monkeypatch.setattr(orlen_sync_module, 'fetch_orlen_range', fake_fetch)
with app.app_context():
db.session.add(OrlenPrice(fuel_type='DIESEL', effective_date=date(2026, 7, 20), price_per_liter=Decimal('4.9000'), raw_value=Decimal('4900'), region='', product_name='DIESEL', source='test'))
db.session.commit()
result, _ = sync_orlen_prices(
fuels=['DIESEL'],
year=2026,
regions=[],
default_region='mazowieckie',
full_refresh=True,
today=date(2026, 8, 2),
)
assert calls == [('DIESEL', date(2026, 1, 1), date(2026, 8, 2), 'mazowieckie')]
assert result['DIESEL']['from'] == '2026-01-01'
assert result['DIESEL']['full_refresh'] is True
def test_orlen_lpg_incremental_sync_uses_oldest_latest_region_date(tmp_path, monkeypatch):
from datetime import date
from decimal import Decimal
from app.extensions import db
from app.models import OrlenPrice
from app.orlen_sync import sync_orlen_prices
import app.orlen_sync as orlen_sync_module
app = make_app(tmp_path, "orlen-lpg-incremental.db")
calls = []
def fake_fetch(fuel, date_from, date_to, region):
calls.append((fuel, date_from, date_to, region))
return [
{'fuel_type': 'LPG', 'effective_date': date(2026, 8, 1), 'price_per_liter': Decimal('2.1000'), 'raw_value': Decimal('2.1000'), 'region': 'mazowieckie', 'product_name': 'LPG', 'source': 'test'},
{'fuel_type': 'LPG', 'effective_date': date(2026, 8, 1), 'price_per_liter': Decimal('2.2000'), 'raw_value': Decimal('2.2000'), 'region': 'pomorskie', 'product_name': 'LPG', 'source': 'test'},
]
monkeypatch.setattr(orlen_sync_module, 'fetch_orlen_range', fake_fetch)
with app.app_context():
db.session.add_all([
OrlenPrice(fuel_type='LPG', effective_date=date(2026, 7, 20), price_per_liter=Decimal('2.0000'), raw_value=Decimal('2.0000'), region='mazowieckie', product_name='LPG', source='test'),
OrlenPrice(fuel_type='LPG', effective_date=date(2026, 7, 25), price_per_liter=Decimal('2.0000'), raw_value=Decimal('2.0000'), region='śląskie', product_name='LPG', source='test'),
])
db.session.commit()
result, total = sync_orlen_prices(
fuels=['LPG'],
year=2026,
regions=['mazowieckie', 'śląskie'],
default_region='mazowieckie',
full_refresh=False,
today=date(2026, 8, 2),
)
db.session.commit()
assert OrlenPrice.query.filter_by(fuel_type='LPG', region='pomorskie').count() == 0
assert calls == [('LPG', date(2026, 7, 20), date(2026, 8, 2), 'all')]
assert result['LPG']['from'] == '2026-07-20'
assert total == 1
def test_orlen_page_has_incremental_and_full_refresh_controls(tmp_path):
app = make_app(tmp_path, "orlen-sync-ui.db")
client = app.test_client()
client.post('/login', data={'email': 'admin@example.com', 'password': 'admin123!'})
html = client.get('/orlen?mode=history').get_data(as_text=True)
assert 'id="orlen-sync-btn"' in html
assert 'Pobierz nowe dane' in html
assert 'id="orlen-full-sync-btn"' in html
assert 'Pobierz cały wybrany rok ponownie' in html