mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-05 03:12:34 -06:00
0202-финиш перед десктопом
пересчет поправил редактирование с перепроведением галка автопроведения работает рекомендации починил
This commit is contained in:
@@ -157,9 +157,6 @@ func (s *Service) UpdateDraftHeader(id uuid.UUID, storeID *uuid.UUID, supplierID
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if draft.Status == drafts.StatusCompleted {
|
||||
return errors.New("черновик уже отправлен")
|
||||
}
|
||||
draft.StoreID = storeID
|
||||
draft.SupplierID = supplierID
|
||||
draft.DateIncoming = &date
|
||||
@@ -227,7 +224,7 @@ func (s *Service) DeleteItem(draftID, itemID uuid.UUID) (float64, error) {
|
||||
return sumFloat, nil
|
||||
}
|
||||
|
||||
// RecalculateItemFields - логика пересчета Qty/Price/Sum
|
||||
// RecalculateItemFields - логика пересчета Q->P->S->Q (Quantity -> Price -> Sum -> Quantity) с использованием decimal для точности
|
||||
func (s *Service) RecalculateItemFields(item *drafts.DraftInvoiceItem, editedField drafts.EditedField) {
|
||||
if item.LastEditedField1 != editedField {
|
||||
item.LastEditedField2 = item.LastEditedField1
|
||||
@@ -265,6 +262,29 @@ func (s *Service) RecalculateItemFields(item *drafts.DraftInvoiceItem, editedFie
|
||||
case drafts.FieldSum:
|
||||
item.Sum = item.Quantity.Mul(item.Price)
|
||||
}
|
||||
|
||||
// Дополнительная проверка для гарантии консистентности всех полей (Q->P->S->Q)
|
||||
// Используется только для обеспечения точности, не влияет на логику выбора пересчитываемого поля
|
||||
if !item.Price.IsZero() && !item.Quantity.IsZero() {
|
||||
calculatedSum := item.Quantity.Mul(item.Price)
|
||||
if !calculatedSum.Equal(item.Sum) {
|
||||
item.Sum = calculatedSum
|
||||
}
|
||||
}
|
||||
|
||||
if !item.Price.IsZero() && !item.Sum.IsZero() {
|
||||
calculatedQuantity := item.Sum.Div(item.Price)
|
||||
if !calculatedQuantity.Equal(item.Quantity) {
|
||||
item.Quantity = calculatedQuantity
|
||||
}
|
||||
}
|
||||
|
||||
if !item.Quantity.IsZero() && !item.Sum.IsZero() {
|
||||
calculatedPrice := item.Sum.Div(item.Quantity)
|
||||
if !calculatedPrice.Equal(item.Price) {
|
||||
item.Price = calculatedPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateItem обновлен для поддержки динамического пересчета
|
||||
@@ -293,17 +313,10 @@ func (s *Service) UpdateItem(draftID, itemID uuid.UUID, productID *uuid.UUID, co
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
// Просто присваиваем значения от фронтенда без пересчета
|
||||
currentItem.Quantity = qty
|
||||
currentItem.Price = price
|
||||
currentItem.Sum = sum
|
||||
|
||||
if draft.Status == drafts.StatusCanceled {
|
||||
draft.Status = drafts.StatusReadyToVerify
|
||||
@@ -311,20 +324,18 @@ func (s *Service) UpdateItem(draftID, itemID uuid.UUID, productID *uuid.UUID, co
|
||||
}
|
||||
|
||||
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,
|
||||
"product_id": currentItem.ProductID,
|
||||
"container_id": currentItem.ContainerID,
|
||||
"quantity": currentItem.Quantity,
|
||||
"price": currentItem.Price,
|
||||
"sum": currentItem.Sum,
|
||||
"is_matched": currentItem.IsMatched,
|
||||
}
|
||||
|
||||
return s.draftRepo.UpdateItem(itemID, updates)
|
||||
}
|
||||
|
||||
func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
func (s *Service) CommitDraft(draftID, userID uuid.UUID, isProcessed bool) (string, error) {
|
||||
server, err := s.accountRepo.GetActiveServer(userID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("active server not found: %w", err)
|
||||
@@ -347,17 +358,13 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
return "", errors.New("черновик принадлежит другому серверу")
|
||||
}
|
||||
|
||||
if draft.Status == drafts.StatusCompleted {
|
||||
return "", errors.New("накладная уже отправлена")
|
||||
}
|
||||
|
||||
client, err := s.rmsFactory.GetClientForUser(userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetStatus := "NEW"
|
||||
if server.AutoProcess {
|
||||
if isProcessed {
|
||||
targetStatus = "PROCESSED"
|
||||
}
|
||||
|
||||
@@ -373,6 +380,11 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
Items: make([]invoices.InvoiceItem, 0, len(draft.Items)),
|
||||
}
|
||||
|
||||
// Если черновик уже был отправлен ранее, передаем RMSInvoiceID для обновления
|
||||
if draft.RMSInvoiceID != nil {
|
||||
inv.ID = *draft.RMSInvoiceID
|
||||
}
|
||||
|
||||
for _, dItem := range draft.Items {
|
||||
if dItem.ProductID == nil {
|
||||
continue
|
||||
@@ -405,6 +417,12 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
Price: priceToSend,
|
||||
Sum: sum,
|
||||
ContainerID: dItem.ContainerID,
|
||||
Product: func() catalog.Product {
|
||||
if dItem.Product != nil {
|
||||
return *dItem.Product
|
||||
}
|
||||
return catalog.Product{}
|
||||
}(),
|
||||
}
|
||||
inv.Items = append(inv.Items, invItem)
|
||||
}
|
||||
@@ -415,7 +433,22 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
|
||||
docNum, err := client.CreateIncomingInvoice(inv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// Если накладная уже проведена, пробуем распровести и повторить
|
||||
if strings.Contains(err.Error(), "Changing processed") {
|
||||
logger.Log.Info("Накладная проведена, выполняю распроведение...", zap.String("doc_num", draft.DocumentNumber))
|
||||
|
||||
if unprocessErr := client.UnprocessIncomingInvoice(inv); unprocessErr != nil {
|
||||
return "", fmt.Errorf("не удалось распровести накладную: %w", unprocessErr)
|
||||
}
|
||||
|
||||
// Повторяем попытку создания накладной после распроведения
|
||||
docNum, err = client.CreateIncomingInvoice(inv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
invoices, err := client.FetchInvoices(*draft.DateIncoming, *draft.DateIncoming)
|
||||
@@ -434,6 +467,7 @@ func (s *Service) CommitDraft(draftID, userID uuid.UUID) (string, error) {
|
||||
for _, invoice := range invoices {
|
||||
if invoice.DocumentNumber == docNum {
|
||||
draft.RMSInvoiceID = &invoice.ID
|
||||
draft.DocumentNumber = invoice.DocumentNumber
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@@ -555,19 +589,20 @@ func (s *Service) CreateProductContainer(userID uuid.UUID, productID uuid.UUID,
|
||||
}
|
||||
|
||||
type UnifiedInvoiceDTO struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Type string `json:"type"`
|
||||
DocumentNumber string `json:"document_number"`
|
||||
IncomingNumber string `json:"incoming_number"`
|
||||
DateIncoming time.Time `json:"date_incoming"`
|
||||
Status string `json:"status"`
|
||||
TotalSum float64 `json:"total_sum"`
|
||||
StoreName string `json:"store_name"`
|
||||
ItemsCount int `json:"items_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
IsAppCreated bool `json:"is_app_created"`
|
||||
PhotoURL string `json:"photo_url"`
|
||||
ItemsPreview string `json:"items_preview"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
Type string `json:"type"`
|
||||
DocumentNumber string `json:"document_number"`
|
||||
IncomingNumber string `json:"incoming_number"`
|
||||
DateIncoming time.Time `json:"date_incoming"`
|
||||
Status string `json:"status"`
|
||||
TotalSum float64 `json:"total_sum"`
|
||||
StoreName string `json:"store_name"`
|
||||
ItemsCount int `json:"items_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
IsAppCreated bool `json:"is_app_created"`
|
||||
PhotoURL string `json:"photo_url"`
|
||||
ItemsPreview string `json:"items_preview"`
|
||||
DraftID *uuid.UUID `json:"draft_id,omitempty"` // ID черновика для SYNCED накладных
|
||||
}
|
||||
|
||||
func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]UnifiedInvoiceDTO, error) {
|
||||
@@ -586,7 +621,7 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
return nil, err
|
||||
}
|
||||
|
||||
photoMap, err := s.draftRepo.GetRMSInvoiceIDToPhotoURLMap(server.ID)
|
||||
linkedDraftsMap, err := s.draftRepo.GetLinkedDraftsMap(server.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -647,9 +682,11 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
|
||||
isAppCreated := false
|
||||
photoURL := ""
|
||||
if url, exists := photoMap[inv.ID]; exists {
|
||||
var draftID *uuid.UUID
|
||||
if linkedInfo, exists := linkedDraftsMap[inv.ID]; exists {
|
||||
isAppCreated = true
|
||||
photoURL = url
|
||||
photoURL = linkedInfo.PhotoURL
|
||||
draftID = &linkedInfo.DraftID
|
||||
}
|
||||
|
||||
var itemsPreview string
|
||||
@@ -679,6 +716,7 @@ func (s *Service) GetUnifiedList(userID uuid.UUID, from, to time.Time) ([]Unifie
|
||||
IsAppCreated: isAppCreated,
|
||||
PhotoURL: photoURL,
|
||||
ItemsPreview: itemsPreview,
|
||||
DraftID: draftID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -739,6 +777,14 @@ func (s *Service) CreateDraft(userID uuid.UUID) (*drafts.DraftInvoice, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Обновляем время последней активности сервера
|
||||
if err := s.accountRepo.UpdateLastActivity(server.ID); err != nil {
|
||||
logger.Log.Warn("Не удалось обновить время активности",
|
||||
zap.String("server_id", server.ID.String()),
|
||||
zap.Error(err))
|
||||
// Не возвращаем ошибку - это некритично
|
||||
}
|
||||
|
||||
return draft, nil
|
||||
}
|
||||
|
||||
@@ -967,11 +1013,6 @@ func (s *Service) SaveDraftFull(draftID, userID uuid.UUID, req drafts.UpdateDraf
|
||||
return err
|
||||
}
|
||||
|
||||
// Проверяем, что черновик не завершен
|
||||
if draft.Status == drafts.StatusCompleted {
|
||||
return errors.New("черновик уже отправлен")
|
||||
}
|
||||
|
||||
// Обновляем шапку черновика, если переданы поля
|
||||
headerUpdated := false
|
||||
|
||||
@@ -1084,54 +1125,17 @@ func (s *Service) SaveDraftFull(draftID, userID uuid.UUID, req drafts.UpdateDraf
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем, какое поле редактируется
|
||||
editedField := itemReq.EditedField
|
||||
if editedField == "" {
|
||||
if itemReq.Sum != nil {
|
||||
editedField = "sum"
|
||||
} else if itemReq.Price != nil {
|
||||
editedField = "price"
|
||||
} else if itemReq.Quantity != nil {
|
||||
editedField = "quantity"
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем числовые поля
|
||||
qty := decimal.Zero
|
||||
// Просто присваиваем значения от фронтенда без пересчета
|
||||
if itemReq.Quantity != nil {
|
||||
qty = decimal.NewFromFloat(*itemReq.Quantity)
|
||||
} else {
|
||||
qty = currentItem.Quantity
|
||||
currentItem.Quantity = decimal.NewFromFloat(*itemReq.Quantity)
|
||||
}
|
||||
|
||||
price := decimal.Zero
|
||||
if itemReq.Price != nil {
|
||||
price = decimal.NewFromFloat(*itemReq.Price)
|
||||
} else {
|
||||
price = currentItem.Price
|
||||
currentItem.Price = decimal.NewFromFloat(*itemReq.Price)
|
||||
}
|
||||
|
||||
sum := decimal.Zero
|
||||
if itemReq.Sum != nil {
|
||||
sum = decimal.NewFromFloat(*itemReq.Sum)
|
||||
} else {
|
||||
sum = currentItem.Sum
|
||||
currentItem.Sum = decimal.NewFromFloat(*itemReq.Sum)
|
||||
}
|
||||
|
||||
// Применяем изменения в зависимости от редактируемого поля
|
||||
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
|
||||
@@ -1142,14 +1146,12 @@ func (s *Service) SaveDraftFull(draftID, userID uuid.UUID, req drafts.UpdateDraf
|
||||
|
||||
// Сохраняем обновленную позицию
|
||||
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,
|
||||
"product_id": currentItem.ProductID,
|
||||
"container_id": currentItem.ContainerID,
|
||||
"quantity": currentItem.Quantity,
|
||||
"price": currentItem.Price,
|
||||
"sum": currentItem.Sum,
|
||||
"is_matched": currentItem.IsMatched,
|
||||
}
|
||||
|
||||
if err := s.draftRepo.UpdateItem(itemID, updates); err != nil {
|
||||
@@ -1158,5 +1160,13 @@ func (s *Service) SaveDraftFull(draftID, userID uuid.UUID, req drafts.UpdateDraf
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем время последней активности сервера
|
||||
if err := s.accountRepo.UpdateLastActivity(draft.RMSServerID); err != nil {
|
||||
logger.Log.Warn("Не удалось обновить время активности",
|
||||
zap.String("server_id", draft.RMSServerID.String()),
|
||||
zap.Error(err))
|
||||
// Не возвращаем ошибку - это некритично
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user