improve(fees): selectable recipient and email in payment reminder
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
- Portal recipient: dropdown with all users linked to company + role - Email: dropdown with company email, contacts, user emails - Editable email field for manual override - Roles shown: Właściciel, Zarządzający, Pracownik Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ce92155836
commit
496a651c45
@ -982,13 +982,32 @@ def admin_fees_reminder_preview():
|
||||
breakdown_lines.append(f'{month_name}: {int(owed)} zł')
|
||||
breakdown_html = '<br>'.join(breakdown_lines)
|
||||
|
||||
from database import UserCompany, User
|
||||
manager = db.query(UserCompany).filter(UserCompany.company_id == company_id).first()
|
||||
manager_user_id = manager.user_id if manager else None
|
||||
manager_name = None
|
||||
if manager_user_id:
|
||||
mgr = db.query(User).filter_by(id=manager_user_id).first()
|
||||
manager_name = mgr.name if mgr else None
|
||||
from database import UserCompany, User, CompanyContact
|
||||
# All users linked to this company
|
||||
ROLE_LABELS = {'OWNER': 'Właściciel', 'MANAGER': 'Zarządzający', 'EMPLOYEE': 'Pracownik', 'NONE': ''}
|
||||
company_users = db.query(UserCompany).filter(UserCompany.company_id == company_id).all()
|
||||
linked_users = []
|
||||
for cu in company_users:
|
||||
u = db.query(User).filter_by(id=cu.user_id).first()
|
||||
if u:
|
||||
role_label = ROLE_LABELS.get(cu.role or '', cu.role or '')
|
||||
linked_users.append({'id': u.id, 'name': u.name or u.email, 'email': u.email, 'role': role_label})
|
||||
|
||||
# All available emails: company email + contacts + linked users
|
||||
available_emails = []
|
||||
if company.email:
|
||||
available_emails.append({'email': company.email, 'label': f'Firma: {company.email}'})
|
||||
contacts = db.query(CompanyContact).filter(CompanyContact.company_id == company_id).all()
|
||||
for c in contacts:
|
||||
if c.email and c.email not in [e['email'] for e in available_emails]:
|
||||
name = f'{c.first_name} {c.last_name}'.strip() if c.first_name else 'Kontakt'
|
||||
available_emails.append({'email': c.email, 'label': f'{name}: {c.email}'})
|
||||
for u in linked_users:
|
||||
if u['email'] not in [e['email'] for e in available_emails]:
|
||||
available_emails.append({'email': u['email'], 'label': f'{u["name"]}: {u["email"]}'})
|
||||
|
||||
manager_user_id = linked_users[0]['id'] if linked_users else None
|
||||
manager_name = linked_users[0]['name'] if linked_users else None
|
||||
|
||||
if len(unpaid) == 1:
|
||||
period = f"{unpaid[0].fee_month:02d}/{year}"
|
||||
@ -1019,6 +1038,8 @@ def admin_fees_reminder_preview():
|
||||
'company_email': company.email,
|
||||
'manager_user_id': manager_user_id,
|
||||
'manager_name': manager_name,
|
||||
'linked_users': linked_users,
|
||||
'available_emails': available_emails,
|
||||
'total_due': int(total_due),
|
||||
'months_count': len(unpaid),
|
||||
'period': month_range + ' ' + str(year),
|
||||
|
||||
@ -528,22 +528,32 @@
|
||||
</div>
|
||||
<div style="margin-bottom:var(--spacing-sm);font-weight:600;">Podgląd wiadomości:</div>
|
||||
<div id="reminderMessagePreview" style="border:1px solid var(--border);border-radius:var(--radius);padding:var(--spacing-md);margin-bottom:var(--spacing-md);background:white;font-size:var(--font-size-sm);line-height:1.6;max-height:300px;overflow-y:auto;"></div>
|
||||
<div style="margin-bottom:var(--spacing-md);">
|
||||
<label style="display:flex;align-items:center;gap:var(--spacing-xs);font-size:var(--font-size-sm);">
|
||||
<input type="checkbox" id="reminderSendEmail" checked>
|
||||
Wyślij też email na: <span id="reminderEmail" style="color:var(--primary);"></span>
|
||||
</label>
|
||||
<div style="font-size:var(--font-size-xs);color:var(--text-secondary);margin-top:4px;" id="reminderPortalInfo"></div>
|
||||
<div style="margin-bottom:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-sm);">
|
||||
<div>
|
||||
<label style="font-size:var(--font-size-sm);font-weight:600;display:block;margin-bottom:4px;">Wiadomość na portalu do:</label>
|
||||
<select id="reminderPortalRecipient" style="width:100%;padding:6px;border:1px solid var(--border);border-radius:var(--radius);font-size:var(--font-size-sm);">
|
||||
<option value="">— nie wysyłaj na portal —</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style="display:flex;align-items:center;gap:var(--spacing-xs);font-size:var(--font-size-sm);">
|
||||
<input type="checkbox" id="reminderSendEmail" checked>
|
||||
Wyślij też email na:
|
||||
</label>
|
||||
<div style="display:flex;gap:var(--spacing-xs);margin-top:4px;">
|
||||
<select id="reminderEmailSelect" style="flex:1;padding:6px;border:1px solid var(--border);border-radius:var(--radius);font-size:var(--font-size-sm);" onchange="document.getElementById('reminderEmailCustom').value=this.value">
|
||||
</select>
|
||||
<input type="text" id="reminderEmailCustom" placeholder="lub wpisz ręcznie" style="flex:1;padding:6px;border:1px solid var(--border);border-radius:var(--radius);font-size:var(--font-size-sm);">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:var(--spacing-sm);justify-content:flex-end;">
|
||||
<button class="btn btn-secondary" onclick="closeReminderModal()">Anuluj</button>
|
||||
<button class="btn btn-primary" id="reminderSendBtn" onclick="sendReminder()">Wyślij przypomnienie</button>
|
||||
</div>
|
||||
<input type="hidden" id="reminderCompanyId">
|
||||
<input type="hidden" id="reminderManagerUserId">
|
||||
<input type="hidden" id="reminderSubject">
|
||||
<input type="hidden" id="reminderMessage">
|
||||
<input type="hidden" id="reminderCompanyEmail">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -724,17 +734,39 @@ async function generateFees() {
|
||||
document.getElementById('reminderAmount').textContent = data.total_due;
|
||||
document.getElementById('reminderPeriod').textContent = data.period;
|
||||
document.getElementById('reminderMessagePreview').innerHTML = data.message;
|
||||
document.getElementById('reminderEmail').textContent = data.company_email || 'brak adresu email';
|
||||
document.getElementById('reminderSendEmail').checked = !!data.company_email;
|
||||
document.getElementById('reminderSendEmail').disabled = !data.company_email;
|
||||
document.getElementById('reminderPortalInfo').textContent = data.manager_name
|
||||
? 'Wiadomość na portalu do: ' + data.manager_name
|
||||
: 'Brak podlinkowanego użytkownika — tylko email';
|
||||
|
||||
// Populate portal recipient dropdown
|
||||
var portalSel = document.getElementById('reminderPortalRecipient');
|
||||
portalSel.innerHTML = '<option value="">— nie wysyłaj na portal —</option>';
|
||||
(data.linked_users || []).forEach(function(u) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = u.id;
|
||||
opt.textContent = u.name + (u.role ? ' (' + u.role + ')' : '');
|
||||
portalSel.appendChild(opt);
|
||||
});
|
||||
if (data.manager_user_id) portalSel.value = data.manager_user_id;
|
||||
|
||||
// Populate email dropdown
|
||||
var emailSel = document.getElementById('reminderEmailSelect');
|
||||
emailSel.innerHTML = '';
|
||||
var emails = data.available_emails || [];
|
||||
if (emails.length === 0) {
|
||||
emailSel.innerHTML = '<option value="">brak adresu email</option>';
|
||||
document.getElementById('reminderSendEmail').checked = false;
|
||||
} else {
|
||||
emails.forEach(function(e) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = e.email;
|
||||
opt.textContent = e.label;
|
||||
emailSel.appendChild(opt);
|
||||
});
|
||||
document.getElementById('reminderSendEmail').checked = true;
|
||||
}
|
||||
document.getElementById('reminderEmailCustom').value = emails.length ? emails[0].email : '';
|
||||
|
||||
document.getElementById('reminderCompanyId').value = companyId;
|
||||
document.getElementById('reminderManagerUserId').value = data.manager_user_id || '';
|
||||
document.getElementById('reminderSubject').value = data.subject;
|
||||
document.getElementById('reminderMessage').value = data.message;
|
||||
document.getElementById('reminderCompanyEmail').value = data.company_email || '';
|
||||
document.getElementById('reminderModal').style.display = 'flex';
|
||||
} catch(e) {
|
||||
showToast('Błąd: ' + e, 'error');
|
||||
@ -751,10 +783,10 @@ async function generateFees() {
|
||||
btn.textContent = 'Wysyłanie...';
|
||||
var fd = new FormData();
|
||||
fd.append('company_id', document.getElementById('reminderCompanyId').value);
|
||||
fd.append('manager_user_id', document.getElementById('reminderManagerUserId').value);
|
||||
fd.append('manager_user_id', document.getElementById('reminderPortalRecipient').value);
|
||||
fd.append('subject', document.getElementById('reminderSubject').value);
|
||||
fd.append('message', document.getElementById('reminderMessage').value);
|
||||
fd.append('company_email', document.getElementById('reminderCompanyEmail').value);
|
||||
fd.append('company_email', document.getElementById('reminderEmailCustom').value);
|
||||
if (document.getElementById('reminderSendEmail').checked) {
|
||||
fd.append('send_email', 'on');
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user