2701-есть флоу для оператора и красивый список накладных

This commit is contained in:
2026-01-27 06:31:38 +03:00
parent 8332b6ecda
commit 38a5143902
11 changed files with 1508 additions and 158 deletions

View File

@@ -689,3 +689,168 @@ func (s *Service) GetInvoiceDetails(invoiceID, userID uuid.UUID) (*invoices.Invo
return inv, photoURL, nil
}
// ============================================
// === МЕТОДЫ ДЛЯ IN-CHAT DRAFT EDITOR ===
// ============================================
// CreateDraft создаёт новый пустой черновик для пользователя
func (s *Service) CreateDraft(userID uuid.UUID) (*drafts.DraftInvoice, error) {
server, err := s.accountRepo.GetActiveServer(userID)
if err != nil || server == nil {
return nil, errors.New("нет активного сервера")
}
draft := &drafts.DraftInvoice{
ID: uuid.New(),
UserID: userID,
RMSServerID: server.ID,
Status: drafts.StatusProcessing,
DateIncoming: func() *time.Time {
t := time.Now()
return &t
}(),
}
if err := s.draftRepo.Create(draft); err != nil {
return nil, err
}
return draft, nil
}
// GetDraftForEditor возвращает черновик для отображения в редакторе.
// Доступен ВСЕМ ролям (Owner, Admin, Operator).
// Проверяет только принадлежность черновика к активному серверу пользователя.
func (s *Service) GetDraftForEditor(draftID, userID uuid.UUID) (*drafts.DraftInvoice, error) {
draft, err := s.draftRepo.GetByID(draftID)
if err != nil {
return nil, err
}
server, err := s.accountRepo.GetActiveServer(userID)
if err != nil || server == nil {
return nil, errors.New("нет активного сервера")
}
if draft.RMSServerID != server.ID {
return nil, errors.New("черновик не принадлежит активному серверу")
}
return draft, nil
}
// validateItemBelongsToDraft проверяет, что позиция принадлежит указанному черновику
func (s *Service) validateItemBelongsToDraft(item *drafts.DraftInvoiceItem, draftID uuid.UUID) error {
if item.DraftID != draftID {
return errors.New("позиция не принадлежит указанному черновику")
}
return nil
}
// UpdateItemRawName обновляет raw_name позиции черновика.
// Возвращает обновлённую позицию.
func (s *Service) UpdateItemRawName(draftID, itemID uuid.UUID, newName string) (*drafts.DraftInvoiceItem, error) {
item, err := s.draftRepo.GetItemByID(itemID)
if err != nil {
return nil, err
}
if err := s.validateItemBelongsToDraft(item, draftID); err != nil {
return nil, err
}
item.RawName = strings.TrimSpace(newName)
if item.RawName == "" {
return nil, errors.New("название позиции не может быть пустым")
}
updates := map[string]interface{}{
"raw_name": item.RawName,
}
if err := s.draftRepo.UpdateItem(itemID, updates); err != nil {
return nil, err
}
return item, nil
}
// UpdateItemQuantity обновляет количество позиции и пересчитывает сумму.
// Возвращает обновлённую позицию.
func (s *Service) UpdateItemQuantity(draftID, itemID uuid.UUID, qty decimal.Decimal) (*drafts.DraftInvoiceItem, error) {
item, err := s.draftRepo.GetItemByID(itemID)
if err != nil {
return nil, err
}
if err := s.validateItemBelongsToDraft(item, draftID); err != nil {
return nil, err
}
item.Quantity = qty
s.RecalculateItemFields(item, drafts.FieldQuantity)
updates := map[string]interface{}{
"quantity": item.Quantity,
"sum": item.Sum,
"last_edited_field1": item.LastEditedField1,
"last_edited_field2": item.LastEditedField2,
}
if err := s.draftRepo.UpdateItem(itemID, updates); err != nil {
return nil, err
}
return item, nil
}
// UpdateItemPrice обновляет цену позиции и пересчитывает сумму.
// Возвращает обновлённую позицию.
func (s *Service) UpdateItemPrice(draftID, itemID uuid.UUID, price decimal.Decimal) (*drafts.DraftInvoiceItem, error) {
item, err := s.draftRepo.GetItemByID(itemID)
if err != nil {
return nil, err
}
if err := s.validateItemBelongsToDraft(item, draftID); err != nil {
return nil, err
}
item.Price = price
s.RecalculateItemFields(item, drafts.FieldPrice)
updates := map[string]interface{}{
"price": item.Price,
"sum": item.Sum,
"last_edited_field1": item.LastEditedField1,
"last_edited_field2": item.LastEditedField2,
}
if err := s.draftRepo.UpdateItem(itemID, updates); err != nil {
return nil, err
}
return item, nil
}
// SetDraftReadyToVerify переводит черновик в статус READY_TO_VERIFY.
// Вызывается при подтверждении оператором.
// Возвращает обновлённый черновик.
func (s *Service) SetDraftReadyToVerify(draftID, userID uuid.UUID) (*drafts.DraftInvoice, error) {
draft, err := s.GetDraftForEditor(draftID, userID)
if err != nil {
return nil, err
}
if draft.Status == drafts.StatusCompleted {
return nil, errors.New("черновик уже завершён")
}
draft.Status = drafts.StatusReadyToVerify
if err := s.draftRepo.Update(draft); err != nil {
return nil, err
}
return draft, nil
}