From 0242ecff09a3ba576ad982c020195de6c4e9904a Mon Sep 17 00:00:00 2001 From: Mina Choi Date: Wed, 22 Jul 2026 15:54:05 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20negodata:=20=EC=B4=88=EC=B2=AD=20?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=ED=97=A4=EB=8D=94=EC=97=90=20=ED=9A=8C?= =?UTF-8?q?=EC=82=AC=20=EB=B8=8C=EB=9E=9C=EB=94=A9(settings.branding.email?= =?UTF-8?q?=5Fheader)=20=EB=B0=B0=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- negodata/backend/crud/quotation_crud.py | 21 ++++++++++++++++++- negodata/backend/services/email.py | 2 ++ .../email_templates/invite_email.html | 2 +- .../backend/services/quotation_service.py | 6 ++++++ 4 files changed, 29 insertions(+), 2 deletions(-) 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)