push
This commit is contained in:
129
app/services/sync_service.py
Normal file
129
app/services/sync_service.py
Normal 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
|
||||
Reference in New Issue
Block a user