129 lines
4.9 KiB
Python
129 lines
4.9 KiB
Python
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 |