company flows
This commit is contained in:
parent
6541284a4c
commit
cc3f3f3de5
17 changed files with 1960 additions and 66 deletions
109
backend/app/services/mail_groups.py
Normal file
109
backend/app/services/mail_groups.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
"""Distribution group management."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models import Account, MailGroup, MailGroupMember
|
||||
from app.schemas import MailGroupCreate, MailGroupRead
|
||||
|
||||
|
||||
def _group_read(group: MailGroup) -> MailGroupRead:
|
||||
return MailGroupRead(
|
||||
id=group.id,
|
||||
name=group.name,
|
||||
email=group.email,
|
||||
group_type=group.group_type,
|
||||
member_count=len(group.members),
|
||||
created_at=group.created_at,
|
||||
)
|
||||
|
||||
|
||||
async def list_mail_groups(
|
||||
session: AsyncSession,
|
||||
company_domain: str,
|
||||
) -> tuple[list[MailGroupRead], dict[str, int]]:
|
||||
result = await session.scalars(
|
||||
select(MailGroup)
|
||||
.options(selectinload(MailGroup.members))
|
||||
.where(MailGroup.company_domain == company_domain)
|
||||
.order_by(MailGroup.created_at.desc())
|
||||
)
|
||||
groups = list(result.all())
|
||||
items = [_group_read(group) for group in groups]
|
||||
counts = {
|
||||
"all": len(items),
|
||||
"distribution": sum(1 for item in items if item.group_type == "distribution"),
|
||||
"dynamic": sum(1 for item in items if item.group_type == "dynamic"),
|
||||
}
|
||||
return items, counts
|
||||
|
||||
|
||||
async def _validate_member_ids(
|
||||
session: AsyncSession,
|
||||
company_domain: str,
|
||||
member_ids: list,
|
||||
) -> list[Account]:
|
||||
if not member_ids:
|
||||
return []
|
||||
result = await session.scalars(
|
||||
select(Account).where(
|
||||
Account.id.in_(member_ids),
|
||||
Account.company_domain == company_domain,
|
||||
Account.member_status != "suspended",
|
||||
)
|
||||
)
|
||||
members = list(result.all())
|
||||
if len(members) != len(set(member_ids)):
|
||||
raise ValueError("One or more selected members are invalid.")
|
||||
return members
|
||||
|
||||
|
||||
async def create_mail_group(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
manager: Account,
|
||||
payload: MailGroupCreate,
|
||||
) -> MailGroupRead:
|
||||
company_domain = manager.company_domain
|
||||
if company_domain is None:
|
||||
raise ValueError("Company domain is required.")
|
||||
|
||||
email = str(payload.email).lower()
|
||||
if not email.endswith(f"@{company_domain}"):
|
||||
raise ValueError(f"Group email must use @{company_domain}.")
|
||||
|
||||
existing = await session.scalar(
|
||||
select(MailGroup).where(func.lower(MailGroup.email) == email)
|
||||
)
|
||||
if existing:
|
||||
raise ValueError("That group email address is already in use.")
|
||||
|
||||
members = await _validate_member_ids(
|
||||
session,
|
||||
company_domain,
|
||||
payload.member_ids,
|
||||
)
|
||||
|
||||
group = MailGroup(
|
||||
company_domain=company_domain,
|
||||
name=payload.name.strip(),
|
||||
email=email,
|
||||
group_type=payload.group_type,
|
||||
created_by_id=manager.id,
|
||||
)
|
||||
session.add(group)
|
||||
await session.flush()
|
||||
|
||||
for member in members:
|
||||
session.add(
|
||||
MailGroupMember(
|
||||
group_id=group.id,
|
||||
account_id=member.id,
|
||||
)
|
||||
)
|
||||
await session.flush()
|
||||
await session.refresh(group, attribute_names=["members"])
|
||||
return _group_read(group)
|
||||
Loading…
Add table
Add a link
Reference in a new issue