Исходящие: удаление черновика (кнопка в карточке и в журнале) — только в статусе draft, с снятием поручения QUIK МНП, если бумаги заблокированы

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-09-25 15:22:08 +03:00
parent 4b81862a90
commit 16bfedb85f
6 changed files with 140 additions and 3 deletions
+32
View File
@@ -0,0 +1,32 @@
package m2mcore
import (
"context"
"errors"
"testing"
)
func TestMemoryDeleteDraft(t *testing.T) {
r := NewMemoryRepository()
ctx := context.Background()
d := &Deal{ID: "d1", GUID: "g1", State: StateDraft}
if _, err := r.Create(ctx, d); err != nil {
t.Fatal(err)
}
s := &Deal{ID: "d2", GUID: "g2", State: StateSubmittedToNSD}
if _, err := r.Create(ctx, s); err != nil {
t.Fatal(err)
}
if err := r.DeleteDraft(ctx, "d2"); !errors.Is(err, ErrNotDraft) {
t.Fatalf("ушедшую в НРД удалять нельзя: %v", err)
}
if err := r.DeleteDraft(ctx, "d1"); err != nil {
t.Fatal(err)
}
if _, err := r.GetByID(ctx, "d1"); err == nil {
t.Fatal("черновик должен быть удалён")
}
if _, err := r.GetByGUID(ctx, "g1"); err == nil {
t.Fatal("черновик должен быть удалён и по GUID")
}
}
+24
View File
@@ -299,3 +299,27 @@ func marshalXMLIfPresent(v any) ([]byte, error) {
var (
_ Repository = (*PostgresRepository)(nil)
)
// DeleteDraft удаляет сделку в состоянии draft вместе с её событиями (одной транзакцией; состояние
// проверяется в самом запросе — заявку, успевшую уйти в НРД, не удалить).
func (r *PostgresRepository) DeleteDraft(ctx context.Context, id string) error {
tx, err := r.pool.Begin(ctx)
if err != nil {
return fmt.Errorf("PostgresRepository.DeleteDraft: %w", err)
}
defer func() { _ = tx.Rollback(ctx) }()
var state string
if err := tx.QueryRow(ctx, `SELECT state FROM m2m_core.deals WHERE id=$1::uuid FOR UPDATE`, id).Scan(&state); err != nil {
return ErrNotFound
}
if state != string(StateDraft) {
return ErrNotDraft
}
if _, err := tx.Exec(ctx, `DELETE FROM m2m_core.deal_events WHERE deal_id=$1::uuid`, id); err != nil {
return fmt.Errorf("PostgresRepository.DeleteDraft events: %w", err)
}
if _, err := tx.Exec(ctx, `DELETE FROM m2m_core.deals WHERE id=$1::uuid AND state=$2`, id, string(StateDraft)); err != nil {
return fmt.Errorf("PostgresRepository.DeleteDraft: %w", err)
}
return tx.Commit(ctx)
}
+25
View File
@@ -153,3 +153,28 @@ func (r *MemoryRepository) EventsOf(dealID string) []Event {
copy(out, src)
return out
}
// ErrNotDraft — удалить можно только черновик (заявку, которая ещё не уходила в НРД).
var ErrNotDraft = errors.New("m2mcore: удалить можно только черновик")
// DraftDeleter — хранилище умеет удалять черновики (сделка в состоянии draft вместе с событиями).
type DraftDeleter interface {
DeleteDraft(ctx context.Context, id string) error
}
// DeleteDraft удаляет сделку, если она в состоянии draft.
func (r *MemoryRepository) DeleteDraft(_ context.Context, id string) error {
r.mu.Lock()
defer r.mu.Unlock()
d, ok := r.byID[id]
if !ok {
return ErrNotFound
}
if d.State != StateDraft {
return ErrNotDraft
}
delete(r.byID, id)
delete(r.byGUID, d.GUID)
delete(r.events, id)
return nil
}