This commit is contained in:
Mateusz Gruszczyński
2026-03-13 11:03:13 +01:00
commit 35571df778
132 changed files with 11197 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
from datetime import datetime
from threading import Thread
from flask import current_app
from app.extensions import db
from app.models.company import Company
from app.models.setting import AppSetting
from app.models.sync_log import SyncLog
from app.services.company_service import CompanyService
from app.services.invoice_service import InvoiceService
from app.services.ksef_service import KSeFService
from app.services.notification_service import NotificationService
from app.services.settings_service import SettingsService
from app.services.redis_service import RedisService
class SyncService:
def __init__(self, company=None):
self.company = company or CompanyService.get_current_company()
self.ksef = KSeFService(company_id=self.company.id if self.company else None)
self.invoice_service = InvoiceService()
self.notification_service = NotificationService(company_id=self.company.id if self.company else None)
def _run(self, sync_type='manual', existing_log=None):
log = existing_log or SyncLog(
company_id=self.company.id if self.company else None,
sync_type=sync_type,
status='started',
started_at=datetime.utcnow(),
message='Rozpoczęto synchronizację',
)
db.session.add(log)
db.session.commit()
since_raw = SettingsService.get('ksef.last_sync_at', company_id=self.company.id if self.company else None)
since = datetime.fromisoformat(since_raw) if since_raw else None
created = updated = errors = 0
try:
documents = self.ksef.list_documents(since=since)
log.total = len(documents)
db.session.commit()
for idx, document in enumerate(documents, start=1):
invoice, was_created = self.invoice_service.upsert_from_ksef(document, self.company)
if was_created:
created += 1
self.notification_service.notify_new_invoice(invoice)
else:
updated += 1
log.processed = idx
log.created = created
log.updated = updated
log.message = f'Przetworzono {idx}/{len(documents)}'
db.session.commit()
log.status = 'finished'
log.message = 'Synchronizacja zakończona'
SettingsService.set_many(
{
'ksef.status': 'ready',
'ksef.last_sync_at': datetime.utcnow().isoformat(),
},
company_id=self.company.id if self.company else None,
)
except RuntimeError as exc:
message = str(exc)
if 'HTTP 429' in message or 'ogranicza liczbę zapytań' in message:
current_app.logger.warning('Synchronizacja KSeF wstrzymana przez limit API: %s', message)
else:
current_app.logger.error('Sync failed: %s', message)
log.status = 'error'
log.message = message
errors += 1
SettingsService.set_many(
{'ksef.status': 'error'},
company_id=self.company.id if self.company else None,
)
except Exception as exc:
current_app.logger.exception('Sync failed: %s', exc)
log.status = 'error'
log.message = str(exc)
errors += 1
SettingsService.set_many(
{'ksef.status': 'error'},
company_id=self.company.id if self.company else None,
)
RedisService.delete(f'dashboard.summary.company.{self.company.id if self.company else "global"}')
RedisService.delete(f'health.status.company.{self.company.id if self.company else "global"}')
log.errors = errors
log.finished_at = datetime.utcnow()
db.session.commit()
return log
def run_manual_sync(self):
return self._run('manual')
def run_scheduled_sync(self):
return self._run('scheduled')
@staticmethod
def start_manual_sync_async(app, company_id):
company = db.session.get(Company, company_id)
log = SyncLog(
company_id=company_id,
sync_type='manual',
status='queued',
started_at=datetime.utcnow(),
message='Zadanie zakolejkowane',
total=1,
)
db.session.add(log)
db.session.commit()
log_id = log.id
def worker():
with app.app_context():
company_local = db.session.get(Company, company_id)
log_local = db.session.get(SyncLog, log_id)
log_local.status = 'started'
log_local.message = 'Start pobierania'
db.session.commit()
SyncService(company_local)._run('manual', existing_log=log_local)
Thread(target=worker, daemon=True).start()
return log_id