From 4d4452596d00c0fc09b99f9bcc89766bf8affe39 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Tue, 28 Jul 2026 16:14:53 +0200 Subject: [PATCH] fix(payments): stop doubling the pack size in buyer mail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catalog titles already name the pack ("50 фишек"), so appending the chip count produced "50 фишек — 50 фишек, 79.00 ₽". The letter now carries the title exactly as the storefront shows it, then the price. The tax receipt is unaffected: it states the quantity in the wording the tax service wants, which is a separate surface with separate requirements. --- backend/internal/inttest/mynalog_test.go | 5 +++++ backend/internal/mynalogsync/mailer.go | 19 +++++++++++-------- backend/internal/payments/mynalog.go | 2 -- backend/internal/payments/store_mynalog.go | 6 +++--- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/internal/inttest/mynalog_test.go b/backend/internal/inttest/mynalog_test.go index ecb6484..adff85e 100644 --- a/backend/internal/inttest/mynalog_test.go +++ b/backend/internal/inttest/mynalog_test.go @@ -661,6 +661,11 @@ func TestMyNalogBuyerMails(t *testing.T) { if !strings.Contains(first[0].Text, "не чек") { t.Errorf("the purchase letter does not say it is not the receipt:\n%s", first[0].Text) } + // The catalog title already names the pack size, so appending it again read as + // "50 фишек — 50 фишек". The line is the title verbatim, then the price, and nothing else. + if !strings.Contains(first[0].Text, "Покупка совершена: test pack, 149.00 ₽.") { + t.Errorf("the purchase line is not the catalog title plus the price:\n%s", first[0].Text) + } // 2. The receipt letter, once the income is filed, carrying the printable link. if _, err := exp.RunBatch(ctx, time.Minute); err != nil { diff --git a/backend/internal/mynalogsync/mailer.go b/backend/internal/mynalogsync/mailer.go index 16e75ee..085793f 100644 --- a/backend/internal/mynalogsync/mailer.go +++ b/backend/internal/mynalogsync/mailer.go @@ -185,15 +185,18 @@ func (m *Mailer) stampReceipt(ctx context.Context, mark func(context.Context, uu } } -// describe renders what was bought in one line. -func describe(title string, chips int, amountMinor int64, currency string) string { +// describe renders what was bought in one line: the catalog title exactly as the buyer saw it on +// the storefront, then the price. +// +// The pack size is deliberately NOT appended. Catalog titles already name it ("50 фишек"), so +// adding it produced "50 фишек — 50 фишек". The title is the single source of that wording; the +// tax receipt states the quantity separately in the form the tax service wants (see +// mynalog.IncomeName), which is a different surface with different requirements. +func describe(title string, amountMinor int64, currency string) string { what := strings.TrimSpace(title) if what == "" { what = "покупка в игре" } - if chips > 0 { - what = fmt.Sprintf("%s — %d фишек", what, chips) - } return fmt.Sprintf("%s, %s", what, formatAmount(amountMinor, currency)) } @@ -224,7 +227,7 @@ func purchaseText(p payments.PurchaseMail) string { отдельным письмом: обычно в течение часа, но иногда это занимает до нескольких дней. Спасибо, что играете в «Эрудит». -`, describe(p.Title, p.Chips, p.AmountMinor, p.Currency), p.OrderID) +`, describe(p.Title, p.AmountMinor, p.Currency), p.OrderID) } // receiptText is the fiscal receipt letter, which is how the receipt is actually handed to the @@ -240,7 +243,7 @@ func receiptText(r payments.ReceiptMail, inn, baseURL string, loc *time.Location Продавец применяет налог на профессиональный доход; чек сформирован в сервисе «Мой налог» Федеральной налоговой службы. -`, describe(r.Title, r.Chips, r.AmountMinor, r.Currency), +`, describe(r.Title, r.AmountMinor, r.Currency), r.OperationTime.In(loc).Format("02.01.2006 15:04"), mynalog.ReceiptURL(baseURL, inn, r.ReceiptUUID)) } @@ -255,6 +258,6 @@ func cancelText(r payments.ReceiptMail, loc *time.Location) string { %s Дата покупки: %s -`, describe(r.Title, r.Chips, r.AmountMinor, r.Currency), +`, describe(r.Title, r.AmountMinor, r.Currency), r.OperationTime.In(loc).Format("02.01.2006 15:04")) } diff --git a/backend/internal/payments/mynalog.go b/backend/internal/payments/mynalog.go index 29c6f46..42b78b0 100644 --- a/backend/internal/payments/mynalog.go +++ b/backend/internal/payments/mynalog.go @@ -125,7 +125,6 @@ type PurchaseMail struct { OrderID uuid.UUID Origin string Title string - Chips int AmountMinor int64 Currency string CreatedAt time.Time @@ -137,7 +136,6 @@ type ReceiptMail struct { AccountID uuid.UUID ReceiptUUID string Title string - Chips int AmountMinor int64 Currency string OperationTime time.Time diff --git a/backend/internal/payments/store_mynalog.go b/backend/internal/payments/store_mynalog.go index a0b0b74..469d54d 100644 --- a/backend/internal/payments/store_mynalog.go +++ b/backend/internal/payments/store_mynalog.go @@ -476,7 +476,7 @@ func (s *Store) pendingPurchaseMails(ctx context.Context, limit int) ([]Purchase rows, err := s.db.QueryContext(ctx, ` SELECT e.event_id, e.account_id, e.order_id, COALESCE(o.origin, ''), COALESCE(o.expected_amount, 0), COALESCE(o.currency, ''), - COALESCE(p.title, ''), COALESCE((e.payload->>'chips')::int, 0), e.created_at + COALESCE(p.title, ''), e.created_at FROM payments.payment_events e LEFT JOIN payments.orders o ON o.order_id = e.order_id LEFT JOIN payments.product p ON p.product_id = o.product_id @@ -495,7 +495,7 @@ func (s *Store) pendingPurchaseMails(ctx context.Context, limit int) ([]Purchase orderID uuid.NullUUID ) if err := rows.Scan(&m.EventID, &m.AccountID, &orderID, &m.Origin, &m.AmountMinor, - &m.Currency, &m.Title, &m.Chips, &m.CreatedAt); err != nil { + &m.Currency, &m.Title, &m.CreatedAt); err != nil { return nil, fmt.Errorf("payments: scan purchase mail: %w", err) } m.OrderID = orderID.UUID @@ -551,7 +551,7 @@ func (s *Store) pendingMyNalogMails(ctx context.Context, limit int, cancelled bo &m.AmountMinor, &m.Currency, &m.OperationTime); err != nil { return nil, fmt.Errorf("payments: scan receipt mail: %w", err) } - m.Title, m.Chips = describeSnapshot(snapshot.String) + m.Title, _ = describeSnapshot(snapshot.String) out = append(out, m) } if err := rows.Err(); err != nil {