закуп
This commit is contained in:
@@ -232,6 +232,12 @@ func (b *Bot) Update(u *echotron.Update) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Игнорируем edited_message обновления, чтобы не сбрасывать состояние после наших Edit операций.
|
||||||
|
if u.EditedMessage != nil || u.EditedChannelPost != nil || u.EditedBusinessMessage != nil {
|
||||||
|
log.Debug().Msg("Ignoring edited message update")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
exec := &Exec{}
|
exec := &Exec{}
|
||||||
|
|
||||||
b.exec = exec
|
b.exec = exec
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package screens
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"example.com/m/bot"
|
"example.com/m/bot"
|
||||||
@@ -17,40 +19,53 @@ type PurchaseOptionalDetails struct {
|
|||||||
Channels []PurchaseChannelInput
|
Channels []PurchaseChannelInput
|
||||||
PlacementDateTime *time.Time
|
PlacementDateTime *time.Time
|
||||||
PaymentDate *time.Time
|
PaymentDate *time.Time
|
||||||
|
CostType string
|
||||||
|
CostValue *float64
|
||||||
|
CostBeforeBargain *float64
|
||||||
|
PurchaseType string
|
||||||
|
Format string
|
||||||
|
Comment string
|
||||||
|
InputMode string
|
||||||
BackState bot.State
|
BackState bot.State
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PurchaseOptionalDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
|
func (s *PurchaseOptionalDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||||
|
if s.InputMode != "" {
|
||||||
|
s.renderInputPrompt(b, mode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
text := "<i>… › Создание › Дополнительно</i>\n\n"
|
text := "<i>… › Создание › Дополнительно</i>\n\n"
|
||||||
text += "<b>Страница создания закупа и необязательные составляющие</b>"
|
text += "<b>Страница создания закупа и необязательные составляющие</b>"
|
||||||
|
|
||||||
placementLabel := "☑ Дата и время размещения"
|
text += "\n\n"
|
||||||
if s.PlacementDateTime != nil {
|
text += s.formatOptionalSummary()
|
||||||
placementLabel = fmt.Sprintf("✅ Дата и время размещения: %s", s.PlacementDateTime.In(mskLocation).Format("02.01 15:04"))
|
|
||||||
}
|
|
||||||
paymentLabel := "Дата оплаты"
|
|
||||||
if s.PaymentDate != nil {
|
|
||||||
paymentLabel = fmt.Sprintf("✅ Дата оплаты: %s", s.PaymentDate.In(mskLocation).Format("02.01 15:04"))
|
|
||||||
}
|
|
||||||
|
|
||||||
keyboard := bot.Keyboard(
|
var rows [][]echotron.InlineKeyboardButton
|
||||||
bot.Row(
|
rows = append(rows, bot.Row(
|
||||||
bot.Button(placementLabel, "opt_datetime"),
|
s.placementButton(),
|
||||||
bot.Button("☑ Стоимость: [цена)", "opt_cost"),
|
s.costButton(),
|
||||||
),
|
))
|
||||||
bot.Row(
|
rows = append(rows, bot.Row(
|
||||||
bot.Button(paymentLabel, "opt_payment_date"),
|
s.paymentButton(),
|
||||||
bot.Button("Тип закупа [вп / стандарт]", "opt_type"),
|
s.typeButton(),
|
||||||
),
|
))
|
||||||
bot.Row(
|
rows = append(rows, bot.Row(
|
||||||
bot.Button("Формат: [формат)", "opt_format"),
|
s.costBeforeButton(),
|
||||||
bot.Button("Комментарий", "opt_comment"),
|
s.formatButton(),
|
||||||
),
|
))
|
||||||
bot.Row(
|
rows = append(rows, bot.Row(
|
||||||
bot.Button("Назад", "back"),
|
s.commentButton(),
|
||||||
bot.Button("Далее", "next"),
|
))
|
||||||
),
|
if row := s.costBeforeDeleteRow(); row != nil {
|
||||||
)
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
if row := s.commentDeleteRow(); row != nil {
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
rows = append(rows, bot.Row(bot.Button("Назад", "back"), bot.Button("Далее", "next")))
|
||||||
|
|
||||||
|
keyboard := bot.Keyboard(rows...)
|
||||||
|
|
||||||
b.Render(text, keyboard, mode)
|
b.Render(text, keyboard, mode)
|
||||||
}
|
}
|
||||||
@@ -79,6 +94,54 @@ func (s *PurchaseOptionalDetails) HandleCallback(b *bot.Bot, u *echotron.Update)
|
|||||||
BackState: s,
|
BackState: s,
|
||||||
}), bot.EditMessage)
|
}), bot.EditMessage)
|
||||||
|
|
||||||
|
case "opt_cost":
|
||||||
|
s.InputMode = "cost_select"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "opt_type":
|
||||||
|
s.InputMode = "type"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "opt_format":
|
||||||
|
s.InputMode = "format_select"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "opt_comment":
|
||||||
|
s.InputMode = "comment"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "delete_comment":
|
||||||
|
s.Comment = ""
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "format_custom":
|
||||||
|
s.InputMode = "format_custom"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "cost_type_toggle":
|
||||||
|
if s.costTypeLabel() == "СРМ" {
|
||||||
|
s.CostType = "fixed"
|
||||||
|
} else {
|
||||||
|
s.CostType = "cpm"
|
||||||
|
}
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "cost_value":
|
||||||
|
s.InputMode = "cost_value"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "cost_before":
|
||||||
|
s.InputMode = "cost_before"
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "delete_cost_before":
|
||||||
|
s.CostBeforeBargain = nil
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
|
case "back_to_optional":
|
||||||
|
s.InputMode = ""
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
case "back":
|
case "back":
|
||||||
b.SetState(&SelectChannelsForPurchase{
|
b.SetState(&SelectChannelsForPurchase{
|
||||||
WorkspaceID: s.WorkspaceID,
|
WorkspaceID: s.WorkspaceID,
|
||||||
@@ -90,11 +153,53 @@ func (s *PurchaseOptionalDetails) HandleCallback(b *bot.Bot, u *echotron.Update)
|
|||||||
BackState: s.BackState,
|
BackState: s.BackState,
|
||||||
}, bot.EditMessage)
|
}, bot.EditMessage)
|
||||||
default:
|
default:
|
||||||
|
if strings.HasPrefix(u.CallbackQuery.Data, "format:") {
|
||||||
|
value := strings.TrimPrefix(u.CallbackQuery.Data, "format:")
|
||||||
|
s.Format = value
|
||||||
|
s.InputMode = ""
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
s.Enter(b, bot.EditMessage)
|
s.Enter(b, bot.EditMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PurchaseOptionalDetails) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
|
func (s *PurchaseOptionalDetails) HandleMessage(b *bot.Bot, u *echotron.Update) {
|
||||||
|
if s.InputMode == "" || u.Message == nil || u.Message.Text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
text := strings.TrimSpace(u.Message.Text)
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s.InputMode {
|
||||||
|
case "type":
|
||||||
|
s.PurchaseType = text
|
||||||
|
case "format_custom":
|
||||||
|
s.Format = text
|
||||||
|
case "comment":
|
||||||
|
s.Comment = text
|
||||||
|
case "cost_value":
|
||||||
|
value, err := strconv.ParseFloat(strings.ReplaceAll(text, ",", "."), 64)
|
||||||
|
if err != nil {
|
||||||
|
s.renderInputPrompt(b, bot.EditMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.CostValue = &value
|
||||||
|
case "cost_before":
|
||||||
|
value, err := strconv.ParseFloat(strings.ReplaceAll(text, ",", "."), 64)
|
||||||
|
if err != nil {
|
||||||
|
s.renderInputPrompt(b, bot.EditMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.CostBeforeBargain = &value
|
||||||
|
}
|
||||||
|
|
||||||
|
s.InputMode = ""
|
||||||
|
s.Enter(b, bot.NewMessage)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PurchaseOptionalDetails) Handle(_ *bot.Bot, _ *echotron.Update) { return }
|
func (s *PurchaseOptionalDetails) Handle(_ *bot.Bot, _ *echotron.Update) { return }
|
||||||
|
|
||||||
@@ -106,3 +211,189 @@ func (s *PurchaseOptionalDetails) SetDateTimeSelection(key string, value time.Ti
|
|||||||
s.PaymentDate = &value
|
s.PaymentDate = &value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatOptionalSummary() string {
|
||||||
|
lines := []string{
|
||||||
|
fmt.Sprintf("Дата размещения: <b>%s</b>", s.formatDateTime(s.PlacementDateTime)),
|
||||||
|
fmt.Sprintf("Дата оплаты: <b>%s</b>", s.formatDateTime(s.PaymentDate)),
|
||||||
|
fmt.Sprintf("Стоимость: <b>%s</b>", s.formatCostValue()),
|
||||||
|
fmt.Sprintf("Стоимость до торга: <b>%s</b>", s.formatCostBefore()),
|
||||||
|
fmt.Sprintf("Тип закупа: <b>%s</b>", s.formatText(s.PurchaseType)),
|
||||||
|
fmt.Sprintf("Формат: <b>%s</b>", s.formatText(s.Format)),
|
||||||
|
fmt.Sprintf("Комментарий: <b>%s</b>", s.formatText(s.Comment)),
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatDateTime(value *time.Time) string {
|
||||||
|
if value == nil {
|
||||||
|
return "—"
|
||||||
|
}
|
||||||
|
local := value.In(mskLocation)
|
||||||
|
return fmt.Sprintf("%s %02d %s %s", weekdayName(local.Weekday()), local.Day(), monthShort(local.Month()), local.Format("15:04"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatText(value string) string {
|
||||||
|
if value == "" {
|
||||||
|
return "—"
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatCostValue() string {
|
||||||
|
if s.CostValue == nil {
|
||||||
|
return "—"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %.0f₽", s.costTypeLabel(), *s.CostValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatCostBefore() string {
|
||||||
|
if s.CostBeforeBargain == nil {
|
||||||
|
return "—"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.0f₽", *s.CostBeforeBargain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) costTypeLabel() string {
|
||||||
|
if s.CostType == "cpm" {
|
||||||
|
return "СРМ"
|
||||||
|
}
|
||||||
|
return "Фикс"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) placementButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.PlacementDateTime == nil {
|
||||||
|
return bot.Button("+ Дата размещения", "opt_datetime")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Дата размещения", "opt_datetime")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) paymentButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.PaymentDate == nil {
|
||||||
|
return bot.Button("+ Дата оплаты", "opt_payment_date")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Дата оплаты", "opt_payment_date")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) costButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.CostValue == nil {
|
||||||
|
return bot.Button("+ Стоимость", "opt_cost")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Стоимость", "opt_cost")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) costBeforeButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.CostBeforeBargain == nil {
|
||||||
|
return bot.Button("+ До торга", "cost_before")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ До торга", "cost_before")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) typeButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.PurchaseType == "" {
|
||||||
|
return bot.Button("+ Тип", "opt_type")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Тип", "opt_type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) formatButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.Format == "" {
|
||||||
|
return bot.Button("+ Формат", "opt_format")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Формат", "opt_format")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) commentButton() echotron.InlineKeyboardButton {
|
||||||
|
if s.Comment == "" {
|
||||||
|
return bot.Button("+ Комментарий", "opt_comment")
|
||||||
|
}
|
||||||
|
return bot.Button("✎ Комментарий", "opt_comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) commentDeleteRow() []echotron.InlineKeyboardButton {
|
||||||
|
if s.Comment == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bot.Row(bot.Button("⌫ Удалить комментарий", "delete_comment"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) costBeforeDeleteRow() []echotron.InlineKeyboardButton {
|
||||||
|
if s.CostBeforeBargain == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bot.Row(bot.Button("⌫ Удалить до торга", "delete_cost_before"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderMode) {
|
||||||
|
text := "<i>… › Создание › Дополнительно</i>\n\n"
|
||||||
|
switch s.InputMode {
|
||||||
|
case "type":
|
||||||
|
text += "<b>Введите тип закупа</b>\n\nНапример: <code>вп</code> или <code>стандарт</code>"
|
||||||
|
case "cost_select":
|
||||||
|
text = "<i>Страница ввода стоимости</i>\n\n"
|
||||||
|
text += "Ставка СРМ / Фикс цена (по умолчанию фикс)\n\n"
|
||||||
|
keyboard := bot.Keyboard(
|
||||||
|
bot.Row(bot.Button(fmt.Sprintf("Тип: %s", s.costTypeLabel()), "cost_type_toggle")),
|
||||||
|
bot.Row(bot.Button(fmt.Sprintf("Стоимость: %s", s.formatCostValue()), "cost_value")),
|
||||||
|
bot.Row(bot.Button("Назад", "back_to_optional")),
|
||||||
|
)
|
||||||
|
if mode == bot.EditMessage {
|
||||||
|
b.Edit(text, keyboard)
|
||||||
|
} else {
|
||||||
|
b.SendNew(text, keyboard)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case "cost_value":
|
||||||
|
text += "<b>Ввод стоимости (СРМ / фикс)</b>\n\nНапример: <code>15000</code>"
|
||||||
|
case "cost_before":
|
||||||
|
text += "<b>Ввод стоимости до торга</b>\n\nНапример: <code>20000</code>"
|
||||||
|
case "format_select":
|
||||||
|
text = "<i>Страница ввода формата размещения</i>\n\n"
|
||||||
|
text += "Выберите формат\n\n"
|
||||||
|
keyboard := bot.Keyboard(
|
||||||
|
bot.Row(
|
||||||
|
bot.Button("1 / 24", "format:1 / 24"),
|
||||||
|
bot.Button("1/48", "format:1/48"),
|
||||||
|
bot.Button("1 / 72", "format:1 / 72"),
|
||||||
|
),
|
||||||
|
bot.Row(
|
||||||
|
bot.Button("1 / (7 дней)", "format:1 / (7 дней)"),
|
||||||
|
bot.Button("1 / (30 дней)", "format:1 / (30 дней)"),
|
||||||
|
bot.Button("1 / (без удаления)", "format:1 / (без удаления)"),
|
||||||
|
),
|
||||||
|
bot.Row(
|
||||||
|
bot.Button("2 / 24", "format:2 / 24"),
|
||||||
|
bot.Button("2/48", "format:2/48"),
|
||||||
|
bot.Button("2/72", "format:2/72"),
|
||||||
|
),
|
||||||
|
bot.Row(
|
||||||
|
bot.Button("Назад", "back_to_optional"),
|
||||||
|
bot.Button("Свой формат", "format_custom"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if mode == bot.EditMessage {
|
||||||
|
b.Edit(text, keyboard)
|
||||||
|
} else {
|
||||||
|
b.SendNew(text, keyboard)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case "format_custom":
|
||||||
|
text += "<b>Введите формат</b>\n\nНапример: <code>пост</code>"
|
||||||
|
case "comment":
|
||||||
|
text += "<b>Введите комментарий</b>"
|
||||||
|
default:
|
||||||
|
s.InputMode = ""
|
||||||
|
s.Enter(b, mode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard := bot.Keyboard(
|
||||||
|
bot.Row(bot.Button("← Назад", "back_to_optional")),
|
||||||
|
)
|
||||||
|
|
||||||
|
if mode == bot.EditMessage {
|
||||||
|
b.Edit(text, keyboard)
|
||||||
|
} else {
|
||||||
|
b.SendNew(text, keyboard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,9 +13,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PurchaseChannelInput struct {
|
type PurchaseChannelInput struct {
|
||||||
Username string
|
Username string
|
||||||
PlannedCost *float64
|
PlannedCost *float64
|
||||||
Comment *string
|
Comment *string
|
||||||
|
PlacementDateTime *time.Time
|
||||||
|
CostType string
|
||||||
|
CostValue *float64
|
||||||
|
CostBeforeBargain *float64
|
||||||
|
Format string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectChannelsForPurchase struct {
|
type SelectChannelsForPurchase struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user