mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
добавил редактируемую сумму и пересчет треугольником
This commit is contained in:
@@ -55,7 +55,6 @@ func NewService(
|
||||
}
|
||||
}
|
||||
|
||||
// checkWriteAccess проверяет, что пользователь имеет право редактировать данные на сервере (ADMIN/OWNER)
|
||||
func (s *Service) checkWriteAccess(userID, serverID uuid.UUID) error {
|
||||
role, err := s.accountRepo.GetUserRole(userID, serverID)
|
||||
if err != nil {
|
||||
@@ -73,8 +72,6 @@ func (s *Service) GetDraft(draftID, userID uuid.UUID) (*drafts.DraftInvoice, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Проверяем, что черновик принадлежит активному серверу пользователя
|
||||
// И пользователь не Оператор (операторы вообще не ходят в API)
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil || server == nil {
|
||||
return nil, errors.New("нет активного сервера")
|
||||
@@ -92,30 +89,24 @@ func (s *Service) GetDraft(draftID, userID uuid.UUID) (*drafts.DraftInvoice, err
|
||||
}
|
||||
|
||||
func (s *Service) GetActiveDrafts(userID uuid.UUID) ([]drafts.DraftInvoice, error) {
|
||||
// 1. Узнаем активный сервер
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil || server == nil {
|
||||
return nil, errors.New("активный сервер не выбран")
|
||||
}
|
||||
|
||||
// 2. Проверяем роль (Security)
|
||||
// Операторам список недоступен
|
||||
if err := s.checkWriteAccess(userID, server.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3. Возвращаем все черновики СЕРВЕРА
|
||||
return s.draftRepo.GetActive(server.ID)
|
||||
}
|
||||
|
||||
// GetDictionaries возвращает Склады и Поставщиков для пользователя
|
||||
func (s *Service) GetDictionaries(userID uuid.UUID) (map[string]interface{}, error) {
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil || server == nil {
|
||||
return nil, fmt.Errorf("active server not found")
|
||||
}
|
||||
|
||||
// Словари нужны только тем, кто редактирует
|
||||
if err := s.checkWriteAccess(userID, server.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,10 +125,6 @@ func (s *Service) DeleteDraft(id uuid.UUID) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// TODO: Здесь тоже бы проверить userID и права, но пока оставим как есть,
|
||||
// так как DeleteDraft вызывается из хендлера, где мы можем добавить проверку,
|
||||
// но лучше передавать userID в сигнатуру DeleteDraft(id, userID).
|
||||
// Для скорости пока оставим, полагаясь на то, что фронт не покажет кнопку.
|
||||
|
||||
if draft.Status == drafts.StatusCanceled {
|
||||
draft.Status = drafts.StatusDeleted
|
||||
@@ -168,18 +155,19 @@ func (s *Service) UpdateDraftHeader(id uuid.UUID, storeID *uuid.UUID, supplierID
|
||||
return s.draftRepo.Update(draft)
|
||||
}
|
||||
|
||||
// AddItem добавляет пустую строку в черновик
|
||||
func (s *Service) AddItem(draftID uuid.UUID) (*drafts.DraftInvoiceItem, error) {
|
||||
newItem := &drafts.DraftInvoiceItem{
|
||||
ID: uuid.New(),
|
||||
DraftID: draftID,
|
||||
RawName: "Новая позиция",
|
||||
RawAmount: decimal.NewFromFloat(1),
|
||||
RawPrice: decimal.Zero,
|
||||
Quantity: decimal.NewFromFloat(1),
|
||||
Price: decimal.Zero,
|
||||
Sum: decimal.Zero,
|
||||
IsMatched: false,
|
||||
ID: uuid.New(),
|
||||
DraftID: draftID,
|
||||
RawName: "Новая позиция",
|
||||
RawAmount: decimal.NewFromFloat(1),
|
||||
RawPrice: decimal.Zero,
|
||||
Quantity: decimal.NewFromFloat(1),
|
||||
Price: decimal.Zero,
|
||||
Sum: decimal.Zero,
|
||||
IsMatched: false,
|
||||
LastEditedField1: drafts.FieldQuantity,
|
||||
LastEditedField2: drafts.FieldPrice,
|
||||
}
|
||||
|
||||
if err := s.draftRepo.CreateItem(newItem); err != nil {
|
||||
@@ -188,7 +176,6 @@ func (s *Service) AddItem(draftID uuid.UUID) (*drafts.DraftInvoiceItem, error) {
|
||||
return newItem, nil
|
||||
}
|
||||
|
||||
// DeleteItem удаляет строку и возвращает обновленную сумму черновика
|
||||
func (s *Service) DeleteItem(draftID, itemID uuid.UUID) (float64, error) {
|
||||
if err := s.draftRepo.DeleteItem(itemID); err != nil {
|
||||
return 0, err
|
||||
@@ -212,23 +199,104 @@ func (s *Service) DeleteItem(draftID, itemID uuid.UUID) (float64, error) {
|
||||
return sumFloat, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateItem(draftID, itemID uuid.UUID, productID *uuid.UUID, containerID *uuid.UUID, qty, price decimal.Decimal) error {
|
||||
// RecalculateItemFields - логика пересчета Qty/Price/Sum
|
||||
func (s *Service) RecalculateItemFields(item *drafts.DraftInvoiceItem, editedField drafts.EditedField) {
|
||||
if item.LastEditedField1 != editedField {
|
||||
item.LastEditedField2 = item.LastEditedField1
|
||||
item.LastEditedField1 = editedField
|
||||
}
|
||||
|
||||
fieldsToKeep := map[drafts.EditedField]bool{
|
||||
item.LastEditedField1: true,
|
||||
item.LastEditedField2: true,
|
||||
}
|
||||
|
||||
var fieldToRecalc drafts.EditedField
|
||||
fieldToRecalc = drafts.FieldSum // Default fallback
|
||||
|
||||
for _, f := range []drafts.EditedField{drafts.FieldQuantity, drafts.FieldPrice, drafts.FieldSum} {
|
||||
if !fieldsToKeep[f] {
|
||||
fieldToRecalc = f
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch fieldToRecalc {
|
||||
case drafts.FieldQuantity:
|
||||
if !item.Price.IsZero() {
|
||||
item.Quantity = item.Sum.Div(item.Price)
|
||||
} else {
|
||||
item.Quantity = decimal.Zero
|
||||
}
|
||||
case drafts.FieldPrice:
|
||||
if !item.Quantity.IsZero() {
|
||||
item.Price = item.Sum.Div(item.Quantity)
|
||||
} else {
|
||||
item.Price = decimal.Zero
|
||||
}
|
||||
case drafts.FieldSum:
|
||||
item.Sum = item.Quantity.Mul(item.Price)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateItem обновлен для поддержки динамического пересчета
|
||||
func (s *Service) UpdateItem(draftID, itemID uuid.UUID, productID *uuid.UUID, containerID *uuid.UUID, qty, price, sum decimal.Decimal, editedField string) error {
|
||||
draft, err := s.draftRepo.GetByID(draftID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Автосмена статуса
|
||||
|
||||
currentItem, err := s.draftRepo.GetItemByID(itemID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if productID != nil {
|
||||
currentItem.ProductID = productID
|
||||
currentItem.IsMatched = true
|
||||
}
|
||||
|
||||
if containerID != nil {
|
||||
// Если пришел UUID.Nil, значит сброс
|
||||
if *containerID == uuid.Nil {
|
||||
currentItem.ContainerID = nil
|
||||
} else {
|
||||
currentItem.ContainerID = containerID
|
||||
}
|
||||
}
|
||||
|
||||
field := drafts.EditedField(editedField)
|
||||
switch field {
|
||||
case drafts.FieldQuantity:
|
||||
currentItem.Quantity = qty
|
||||
case drafts.FieldPrice:
|
||||
currentItem.Price = price
|
||||
case drafts.FieldSum:
|
||||
currentItem.Sum = sum
|
||||
}
|
||||
|
||||
s.RecalculateItemFields(currentItem, field)
|
||||
|
||||
if draft.Status == drafts.StatusCanceled {
|
||||
draft.Status = drafts.StatusReadyToVerify
|
||||
s.draftRepo.Update(draft)
|
||||
}
|
||||
return s.draftRepo.UpdateItem(itemID, productID, containerID, qty, price)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"product_id": currentItem.ProductID,
|
||||
"container_id": currentItem.ContainerID,
|
||||
"quantity": currentItem.Quantity,
|
||||
"price": currentItem.Price,
|
||||
"sum": currentItem.Sum,
|
||||
"last_edited_field1": currentItem.LastEditedField1,
|
||||
"last_edited_field2": currentItem.LastEditedField2,
|
||||
"is_matched": currentItem.IsMatched,
|
||||
}
|
||||
|
||||
return s.draftRepo.UpdateItem(itemID, updates)
|
||||
}
|
||||
|
||||
// CommitDraft отправляет накладную
|
||||
// CommitDraft отправляет накладную
|
||||
func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
// 1. Получаем сервер и права
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("active server not found: %w", err)
|
||||
@@ -238,18 +306,15 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// --- BILLING CHECK ---
|
||||
if can, err := s.billingService.CanProcessInvoice(server.ID); !can {
|
||||
return "", fmt.Errorf("ошибка биллинга: %w", err)
|
||||
}
|
||||
|
||||
// 2. Черновик
|
||||
draft, err := s.draftRepo.GetByID(draftID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Проверка принадлежности черновика серверу
|
||||
if draft.RMSServerID != server.ID {
|
||||
return "", errors.New("черновик принадлежит другому серверу")
|
||||
}
|
||||
@@ -258,7 +323,6 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
return "", errors.New("накладная уже отправлена")
|
||||
}
|
||||
|
||||
// 3. Клиент (использует права текущего юзера - Админа/Владельца)
|
||||
client, err := s.rmsFactory.GetClientForUser(userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -269,7 +333,6 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
targetStatus = "PROCESSED"
|
||||
}
|
||||
|
||||
// 4. Сборка Invoice
|
||||
inv := invoices.Invoice{
|
||||
ID: uuid.Nil,
|
||||
DocumentNumber: draft.DocumentNumber,
|
||||
@@ -284,7 +347,7 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
|
||||
for _, dItem := range draft.Items {
|
||||
if dItem.ProductID == nil {
|
||||
continue // Skip unrecognized
|
||||
continue
|
||||
}
|
||||
|
||||
sum := dItem.Sum
|
||||
@@ -292,28 +355,16 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
sum = dItem.Quantity.Mul(dItem.Price)
|
||||
}
|
||||
|
||||
// Инициализируем значениями из черновика (по умолчанию для базовых единиц)
|
||||
amountToSend := dItem.Quantity
|
||||
priceToSend := dItem.Price
|
||||
|
||||
// ЛОГИКА ПЕРЕСЧЕТА ДЛЯ ФАСОВОК (СОГЛАСНО ДОКУМЕНТАЦИИ IIKO)
|
||||
// Если указан ContainerID, iiko требует:
|
||||
// <amount> = кол-во упаковок * вес упаковки (итоговое кол-во в базовых единицах)
|
||||
// <price> = цена за упаковку / вес упаковки (цена за базовую единицу)
|
||||
// <containerId> = ID фасовки
|
||||
if dItem.ContainerID != nil && *dItem.ContainerID != uuid.Nil {
|
||||
// Проверяем, что Container загружен (Preload в репозитории)
|
||||
if dItem.Container != nil {
|
||||
if !dItem.Container.Count.IsZero() {
|
||||
// 1. Пересчитываем кол-во: 5 ящиков * 10 кг = 50 кг
|
||||
amountToSend = dItem.Quantity.Mul(dItem.Container.Count)
|
||||
|
||||
// 2. Пересчитываем цену: 1000 руб/ящ / 10 кг = 100 руб/кг
|
||||
priceToSend = dItem.Price.Div(dItem.Container.Count)
|
||||
}
|
||||
} else {
|
||||
// Если фасовка есть в ID, но не подгрузилась структура - это ошибка данных.
|
||||
// Логируем варнинг, но пробуем отправить как есть (iiko может отвергнуть или посчитать криво)
|
||||
logger.Log.Warn("Container struct is nil for item with ContainerID",
|
||||
zap.String("item_id", dItem.ID.String()),
|
||||
zap.String("container_id", dItem.ContainerID.String()))
|
||||
@@ -322,9 +373,9 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
|
||||
invItem := invoices.InvoiceItem{
|
||||
ProductID: *dItem.ProductID,
|
||||
Amount: amountToSend, // Отправляем ПЕРЕСЧИТАННЫЙ вес/объем
|
||||
Price: priceToSend, // Отправляем ПЕРЕСЧИТАННУЮ цену за базовую ед.
|
||||
Sum: sum, // Сумма остается неизменной (Total)
|
||||
Amount: amountToSend,
|
||||
Price: priceToSend,
|
||||
Sum: sum,
|
||||
ContainerID: dItem.ContainerID,
|
||||
}
|
||||
inv.Items = append(inv.Items, invItem)
|
||||
@@ -334,13 +385,11 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
return "", errors.New("нет распознанных позиций для отправки")
|
||||
}
|
||||
|
||||
// 5. Отправка в RMS
|
||||
docNum, err := client.CreateIncomingInvoice(inv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 6. Поиск UUID созданной накладной
|
||||
invoices, err := client.FetchInvoices(*draft.DateIncoming, *draft.DateIncoming)
|
||||
if err != nil {
|
||||
logger.Log.Warn("Не удалось получить список накладных для поиска UUID", zap.Error(err), zap.Time("date", *draft.DateIncoming))
|
||||
@@ -358,11 +407,9 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Обновление статуса черновика
|
||||
draft.Status = drafts.StatusCompleted
|
||||
s.draftRepo.Update(draft)
|
||||
|
||||
// --- БИЛЛИНГ: Списание баланса и инкремент счетчика ---
|
||||
if err := s.accountRepo.DecrementBalance(server.ID); err != nil {
|
||||
logger.Log.Error("Billing decrement failed", zap.Error(err), zap.String("server_id", server.ID.String()))
|
||||
}
|
||||
@@ -371,7 +418,6 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
logger.Log.Error("Billing increment failed", zap.Error(err))
|
||||
}
|
||||
|
||||
// 7. Запуск обучения
|
||||
go s.learnFromDraft(draft, server.ID)
|
||||
|
||||
return docNum, nil
|
||||
@@ -408,7 +454,6 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
return uuid.Nil, fmt.Errorf("error fetching product: %w", err)
|
||||
}
|
||||
|
||||
// Валидация на дубли
|
||||
targetCount, _ := count.Float64()
|
||||
for _, c := range fullProduct.Containers {
|
||||
if !c.Deleted && (c.Name == name || (c.Count == targetCount)) {
|
||||
@@ -418,7 +463,6 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
}
|
||||
}
|
||||
|
||||
// Next Num
|
||||
maxNum := 0
|
||||
for _, c := range fullProduct.Containers {
|
||||
if n, err := strconv.Atoi(c.Num); err == nil {
|
||||
@@ -429,7 +473,6 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
}
|
||||
nextNum := strconv.Itoa(maxNum + 1)
|
||||
|
||||
// Add
|
||||
newContainerDTO := rms.ContainerFullDTO{
|
||||
ID: nil,
|
||||
Num: nextNum,
|
||||
@@ -440,13 +483,11 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
}
|
||||
fullProduct.Containers = append(fullProduct.Containers, newContainerDTO)
|
||||
|
||||
// Update RMS
|
||||
updatedProduct, err := client.UpdateProduct(*fullProduct)
|
||||
if err != nil {
|
||||
return uuid.Nil, fmt.Errorf("error updating product: %w", err)
|
||||
}
|
||||
|
||||
// Find created ID
|
||||
var createdID uuid.UUID
|
||||
found := false
|
||||
for _, c := range updatedProduct.Containers {
|
||||
@@ -465,7 +506,6 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
return uuid.Nil, errors.New("container created but id not found")
|
||||
}
|
||||
|
||||
// Save Local
|
||||
newLocalContainer := catalog.ProductContainer{
|
||||
ID: createdID,
|
||||
RMSServerID: server.ID,
|
||||
@@ -478,10 +518,9 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
return createdID, nil
|
||||
}
|
||||
|
||||
// Добавим новый DTO для единого списка (Frontend Contract)
|
||||
type UnifiedInvoiceDTO struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Type string `json:"type"` // "DRAFT" или "SYNCED"
|
||||
Type string `json:"type"`
|
||||
DocumentNumber string `json:"document_number"`
|
||||
IncomingNumber string `json:"incoming_number"`
|
||||
DateIncoming time.Time `json:"date_incoming"`
|
||||
@@ -501,19 +540,16 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
return nil, errors.New("активный сервер не выбран")
|
||||
}
|
||||
|
||||
// 1. Получаем черновики (их обычно немного, берем все активные)
|
||||
draftsList, err := s.draftRepo.GetActive(server.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2. Получаем синхронизированные накладные за период
|
||||
invoicesList, err := s.invoiceRepo.GetByPeriod(server.ID, from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3. Получаем мапу rms_invoice_id -> sender_photo_url
|
||||
photoMap, err := s.draftRepo.GetRMSInvoiceIDToPhotoURLMap(server.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -521,7 +557,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
|
||||
result := make([]UnifiedInvoiceDTO, 0, len(draftsList)+len(invoicesList))
|
||||
|
||||
// Маппим черновики
|
||||
for _, d := range draftsList {
|
||||
var sum decimal.Decimal
|
||||
for _, it := range d.Items {
|
||||
@@ -538,7 +573,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
date = *d.DateIncoming
|
||||
}
|
||||
|
||||
// Формируем ItemsPreview для черновиков
|
||||
var itemsPreview string
|
||||
if len(d.Items) > 0 {
|
||||
names := make([]string, 0, 3)
|
||||
@@ -555,11 +589,11 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
ID: d.ID,
|
||||
Type: "DRAFT",
|
||||
DocumentNumber: d.DocumentNumber,
|
||||
IncomingNumber: "", // В черновиках пока не разделяем
|
||||
IncomingNumber: "",
|
||||
DateIncoming: date,
|
||||
Status: d.Status,
|
||||
TotalSum: val,
|
||||
StoreName: "", // Можно подгрузить из d.Store.Name если сделан Preload
|
||||
StoreName: "",
|
||||
ItemsCount: len(d.Items),
|
||||
CreatedAt: d.CreatedAt,
|
||||
IsAppCreated: true,
|
||||
@@ -568,7 +602,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
})
|
||||
}
|
||||
|
||||
// Маппим проведенные
|
||||
for _, inv := range invoicesList {
|
||||
var sum decimal.Decimal
|
||||
for _, it := range inv.Items {
|
||||
@@ -576,7 +609,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
}
|
||||
val, _ := sum.Float64()
|
||||
|
||||
// Определяем IsAppCreated и PhotoURL через мапу
|
||||
isAppCreated := false
|
||||
photoURL := ""
|
||||
if url, exists := photoMap[inv.ID]; exists {
|
||||
@@ -584,7 +616,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
photoURL = url
|
||||
}
|
||||
|
||||
// Формируем ItemsPreview для синхронизированных накладных
|
||||
var itemsPreview string
|
||||
if len(inv.Items) > 0 {
|
||||
names := make([]string, 0, 3)
|
||||
@@ -592,7 +623,6 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
if i >= 3 {
|
||||
break
|
||||
}
|
||||
// Предполагаем, что Product подгружен, иначе нужно добавить Preload
|
||||
if it.Product.Name != "" {
|
||||
names = append(names, it.Product.Name)
|
||||
}
|
||||
@@ -616,19 +646,15 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
})
|
||||
}
|
||||
|
||||
// Сортировка по дате накладной (desc)
|
||||
// (Здесь можно добавить библиотеку sort или оставить как есть, если БД уже отсортировала части)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetInvoiceDetails(invoiceID, userID uuid.UUID) (*invoices.Invoice, string, error) {
|
||||
// Получить накладную
|
||||
inv, err := s.invoiceRepo.GetByID(invoiceID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Проверить, что пользователь имеет доступ к серверу накладной
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil || server == nil {
|
||||
return nil, "", errors.New("нет активного сервера")
|
||||
@@ -638,7 +664,6 @@ func (s *Service) GetInvoiceDetails(invoiceID, userID uuid.UUID) (*invoices.Invo
|
||||
return nil, "", errors.New("накладная не принадлежит активному серверу")
|
||||
}
|
||||
|
||||
// Попытаться найти черновик по rms_invoice_id
|
||||
draft, err := s.draftRepo.GetByRMSInvoiceID(invoiceID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
||||
Reference in New Issue
Block a user