diff --git a/negodata/backend/crud/quotation_crud.py b/negodata/backend/crud/quotation_crud.py index 4a9a011..aa1f6ec 100644 --- a/negodata/backend/crud/quotation_crud.py +++ b/negodata/backend/crud/quotation_crud.py @@ -8,7 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from common.database.db_session_manager import DB_SESSION_MNG from common.database.model.models import ( quotations, sessions, chats, nego_cards, wild_cards, items, suppliers, quotation_settings, - version_nego_cards, version_wild_cards, users, supplier_items, + version_nego_cards, version_wild_cards, users, supplier_items, companies, ) from common.enums import CloseReason, ErrorType, QuotationStatus, SessionStatus from common.logger import LOG @@ -384,6 +384,25 @@ class QuotationCRUD(IQuotationCRUD): LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, {} + async def get_email_header(self, cdb: AsyncSession, user_id): + # 견적 소유 유저 → 회사 → companies.settings.branding.email_header. 초청 메일 헤더 브랜딩용. 없으면 None. + try: + query = ( + select(companies.settings) + .select_from(users) + .join(companies, companies.company_id == users.company_id) + .where(users.user_id == user_id, companies.deleted == False) # noqa: E712 + .limit(1) + ) + err, rows = await DB_SESSION_MNG.execute(cdb, query) + if err != ErrorType.SUCCESS or not rows: + return None + settings = rows[0] or {} + return (settings.get("branding") or {}).get("email_header") + except Exception as ex: + LOG.e_no_callstack(ex) + return None + async def get_item_companies(self, cdb: AsyncSession, item_ids) -> Tuple[ErrorType, dict]: """item_id -> company_id(소유 회사) 매핑. 앵커링 칸(회사×유형×가격구간) 해석 입력.""" try: diff --git a/negodata/backend/services/email.py b/negodata/backend/services/email.py index eaea9ed..9f2db41 100644 --- a/negodata/backend/services/email.py +++ b/negodata/backend/services/email.py @@ -96,6 +96,7 @@ def build_invite_email( qt_number: str, end_time: datetime | None, chat_url: str, + email_header: str | None = None, ) -> tuple[str, str, str]: """협상 초청 메일 (제목/HTML/텍스트) 생성. @@ -113,6 +114,7 @@ def build_invite_email( qt_number=escape(qt_number), deadline=escape(deadline), chat_url=escape(chat_url), + email_header=escape(email_header or "NEGODATA"), ) text = ( diff --git a/negodata/backend/services/email_templates/invite_email.html b/negodata/backend/services/email_templates/invite_email.html index aa79386..7aa45dd 100644 --- a/negodata/backend/services/email_templates/invite_email.html +++ b/negodata/backend/services/email_templates/invite_email.html @@ -6,7 +6,7 @@ - NEGODATA + $email_header diff --git a/negodata/backend/services/quotation_service.py b/negodata/backend/services/quotation_service.py index 7ea2b7c..a921b3f 100644 --- a/negodata/backend/services/quotation_service.py +++ b/negodata/backend/services/quotation_service.py @@ -962,6 +962,11 @@ class QuotationService: async def _send_invites(self, quotation, targets: list, res: Res_NotifySessions) -> list: """targets [(session, supplier_name, email)] 에 초청 메일 발송. res.sent/failed 를 채우고 성공한 session_id 목록을 반환. ACS/SMTP 미설정이면 첫 발송에서 중단(EMAIL_NOT_CONFIGURED).""" + # 회사 브랜딩(초청 메일 헤더) 한 번 조회 — 견적당 동일. + email_header = await DB_SESSION_MNG.execute_lambda( + quotations.DBType(), DBWRType.DB_READ.value, + lambda s: self.quotation_crud.get_email_header(s, quotation.user_id), + ) sent_ids = [] for sess, sp_name, email in targets: subject, html, text = build_invite_email( @@ -970,6 +975,7 @@ class QuotationService: qt_number=quotation.number, end_time=quotation.end_time, chat_url=self._session_chat_url(sess.session_id), + email_header=email_header, ) try: await send_email(email, subject, html, text)