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,104 @@
from datetime import datetime
from decimal import Decimal
import requests
from app.extensions import db
from app.models.company import UserCompanyAccess
from app.models.invoice import NotificationLog
from app.models.user import User
from app.services.mail_service import MailService
from app.services.settings_service import SettingsService
class NotificationService:
def __init__(self, company_id=None):
self.company_id = company_id
def should_notify(self, invoice):
if SettingsService.get_effective('notify.enabled', 'false', company_id=self.company_id) != 'true':
return False
min_amount = Decimal(SettingsService.get_effective('notify.min_amount', '0', company_id=self.company_id) or '0')
return Decimal(invoice.gross_amount) >= min_amount
def _pushover_credentials(self):
return {
'token': SettingsService.get_effective_secret('notify.pushover_api_token', '', company_id=self.company_id),
'user': SettingsService.get_effective('notify.pushover_user_key', '', company_id=self.company_id),
}
def _email_recipients(self):
if not self.company_id:
return []
rows = (
db.session.query(User.email)
.join(UserCompanyAccess, UserCompanyAccess.user_id == User.id)
.filter(UserCompanyAccess.company_id == self.company_id, User.is_blocked.is_(False))
.order_by(User.email.asc())
.all()
)
recipients = []
seen = set()
for (email,) in rows:
email = (email or '').strip().lower()
if not email or email in seen:
continue
seen.add(email)
recipients.append(email)
return recipients
def notify_new_invoice(self, invoice):
if not self.should_notify(invoice):
return []
message = f'Nowa faktura {invoice.invoice_number} / {invoice.contractor_name} / {invoice.gross_amount} PLN'
logs = [self._send_email_notification(invoice, message)]
logs.append(self._send_pushover(invoice.id, message))
return logs
def log_channel(self, invoice_id, channel, status, message):
log = NotificationLog(invoice_id=invoice_id, channel=channel, status=status, message=message, sent_at=datetime.utcnow())
db.session.add(log)
db.session.commit()
return log
def _send_email_notification(self, invoice, message):
recipients = self._email_recipients()
if not recipients:
return self.log_channel(invoice.id, 'email', 'skipped', 'Brak odbiorców e-mail przypisanych do aktywnej firmy')
mailer = MailService(company_id=self.company_id)
subject = f'Nowa faktura: {invoice.invoice_number}'
body = (
'W systemie KSeF Manager pojawiła się nowa faktura.\n\n'
f'Numer: {invoice.invoice_number}\n'
f'Kontrahent: {invoice.contractor_name}\n'
f'Kwota brutto: {invoice.gross_amount} PLN\n'
)
sent = 0
errors = []
for recipient in recipients:
try:
mailer.send_mail(recipient, subject, body)
sent += 1
except Exception as exc:
errors.append(f'{recipient}: {exc}')
if sent and not errors:
return self.log_channel(invoice.id, 'email', 'sent', f'{message} · odbiorców: {sent}')
if sent and errors:
return self.log_channel(invoice.id, 'email', 'error', f"Wysłano do {sent}/{len(recipients)} odbiorców. Błędy: {'; '.join(errors[:3])}")
return self.log_channel(invoice.id, 'email', 'error', 'Nie udało się wysłać powiadomień e-mail: ' + '; '.join(errors[:3]))
def _send_pushover(self, invoice_id, message):
creds = self._pushover_credentials()
if not creds['token'] or not creds['user']:
return self.log_channel(invoice_id, 'pushover', 'skipped', 'Brak konfiguracji Pushover')
try:
response = requests.post('https://api.pushover.net/1/messages.json', data={'token': creds['token'], 'user': creds['user'], 'message': message}, timeout=10)
response.raise_for_status()
return self.log_channel(invoice_id, 'pushover', 'sent', message)
except Exception as exc:
return self.log_channel(invoice_id, 'pushover', 'error', str(exc))
def send_test_pushover(self):
return self._send_pushover(None, 'KSeF Manager - test Pushover')