mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
0202-финиш перед десктопом
пересчет поправил редактирование с перепроведением галка автопроведения работает рекомендации починил
This commit is contained in:
@@ -40,6 +40,7 @@ type ClientI interface {
|
||||
FetchInvoices(from, to time.Time) ([]invoices.Invoice, error)
|
||||
FetchStoreOperations(presetID string, from, to time.Time) ([]StoreReportItemXML, error)
|
||||
CreateIncomingInvoice(inv invoices.Invoice) (string, error)
|
||||
UnprocessIncomingInvoice(inv invoices.Invoice) error
|
||||
GetProductByID(id uuid.UUID) (*ProductFullDTO, error)
|
||||
UpdateProduct(product ProductFullDTO) (*ProductFullDTO, error)
|
||||
}
|
||||
@@ -555,9 +556,24 @@ func (c *Client) FetchStoreOperations(presetID string, from, to time.Time) ([]St
|
||||
return report.Items, nil
|
||||
}
|
||||
|
||||
// CreateIncomingInvoice отправляет накладную в iiko
|
||||
func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
// 1. Маппинг Domain -> XML DTO
|
||||
// buildInvoiceXML формирует XML payload для накладной на основе доменной сущности
|
||||
func (c *Client) buildInvoiceXML(inv invoices.Invoice) ([]byte, error) {
|
||||
// Защита от паники с recover
|
||||
var panicErr error
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Log.Error("Паника в buildInvoiceXML",
|
||||
zap.Any("panic", r),
|
||||
zap.Stack("stack"),
|
||||
)
|
||||
panicErr = fmt.Errorf("panic recovered: %v", r)
|
||||
}
|
||||
}()
|
||||
if panicErr != nil {
|
||||
return nil, panicErr
|
||||
}
|
||||
|
||||
// Маппинг Domain -> XML DTO
|
||||
|
||||
// Статус по умолчанию NEW, если не передан
|
||||
status := inv.Status
|
||||
@@ -592,7 +608,21 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
reqDTO.ID = inv.ID.String()
|
||||
}
|
||||
|
||||
// Логирование перед циклом по Items
|
||||
logger.Log.Debug("Начинаем формирование XML для позиций накладной",
|
||||
zap.Int("items_count", len(inv.Items)),
|
||||
)
|
||||
|
||||
for i, item := range inv.Items {
|
||||
// Проверка что продукт загружен (по полю ID)
|
||||
if item.Product.ID == uuid.Nil {
|
||||
logger.Log.Warn("Пропуск позиции: Product не загружен",
|
||||
zap.String("product_id", item.ProductID.String()),
|
||||
zap.Int("index", i),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
amount, _ := item.Amount.Float64()
|
||||
price, _ := item.Price.Float64()
|
||||
sum, _ := item.Sum.Float64()
|
||||
@@ -610,18 +640,47 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
xmlItem.ContainerId = item.ContainerID.String()
|
||||
}
|
||||
|
||||
// Проверка MainUnitID перед обращением
|
||||
if item.Product.MainUnitID != nil {
|
||||
xmlItem.AmountUnit = item.Product.MainUnitID.String()
|
||||
}
|
||||
|
||||
// Логирование каждого добавленного item
|
||||
logger.Log.Debug("Добавление позиции в XML",
|
||||
zap.String("product_id", item.ProductID.String()),
|
||||
zap.Float64("amount", amount),
|
||||
zap.String("product_name", item.Product.Name),
|
||||
)
|
||||
|
||||
reqDTO.ItemsWrapper.Items = append(reqDTO.ItemsWrapper.Items, xmlItem)
|
||||
}
|
||||
|
||||
// 2. Маршалинг в XML
|
||||
// Маршалинг в XML
|
||||
xmlBytes, err := xml.Marshal(reqDTO)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("xml marshal error: %w", err)
|
||||
return nil, fmt.Errorf("xml marshal error: %w", err)
|
||||
}
|
||||
// Добавляем XML header вручную
|
||||
xmlPayload := []byte(xml.Header + string(xmlBytes))
|
||||
|
||||
// 3. Получение токена
|
||||
// Логирование XML перед отправкой
|
||||
logger.Log.Debug("XML payload подготовлен",
|
||||
zap.String("xml_payload", string(xmlPayload)),
|
||||
zap.Int("payload_size", len(xmlPayload)),
|
||||
)
|
||||
|
||||
return xmlPayload, nil
|
||||
}
|
||||
|
||||
// CreateIncomingInvoice отправляет накладную в iiko
|
||||
func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
// 1. Формирование XML payload
|
||||
xmlPayload, err := c.buildInvoiceXML(inv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка формирования XML: %w", err)
|
||||
}
|
||||
|
||||
// 2. Получение токена
|
||||
if err := c.ensureToken(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -630,7 +689,7 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
token := c.token
|
||||
c.mu.RUnlock()
|
||||
|
||||
// 4. Формирование URL
|
||||
// 3. Формирование URL
|
||||
endpoint, _ := url.Parse(c.baseURL + "/resto/api/documents/import/incomingInvoice")
|
||||
q := endpoint.Query()
|
||||
q.Set("key", token)
|
||||
@@ -646,7 +705,7 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
zap.String("body_payload", string(xmlPayload)),
|
||||
)
|
||||
|
||||
// 5. Отправка
|
||||
// 4. Отправка
|
||||
req, err := http.NewRequest("POST", fullURL, bytes.NewReader(xmlPayload))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -666,9 +725,9 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
}
|
||||
|
||||
// Логируем ответ для симметрии
|
||||
logger.Log.Info("RMS POST Response Debug",
|
||||
logger.Log.Debug("Получен ответ от iiko",
|
||||
zap.Int("status_code", resp.StatusCode),
|
||||
zap.String("response_body", string(respBody)),
|
||||
zap.String("raw_response", string(respBody)),
|
||||
)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
@@ -691,6 +750,89 @@ func (c *Client) CreateIncomingInvoice(inv invoices.Invoice) (string, error) {
|
||||
return result.DocumentNumber, nil
|
||||
}
|
||||
|
||||
// UnprocessIncomingInvoice выполняет распроведение накладной в iiko
|
||||
func (c *Client) UnprocessIncomingInvoice(inv invoices.Invoice) error {
|
||||
// 1. Формирование XML payload
|
||||
xmlPayload, err := c.buildInvoiceXML(inv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка формирования XML: %w", err)
|
||||
}
|
||||
|
||||
// 2. Получение токена
|
||||
if err := c.ensureToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
token := c.token
|
||||
c.mu.RUnlock()
|
||||
|
||||
// 3. Формирование URL
|
||||
endpoint, _ := url.Parse(c.baseURL + "/resto/api/documents/unprocess/incomingInvoice")
|
||||
q := endpoint.Query()
|
||||
q.Set("key", token)
|
||||
endpoint.RawQuery = q.Encode()
|
||||
|
||||
fullURL := endpoint.String()
|
||||
|
||||
// Логирование запроса
|
||||
logger.Log.Info("RMS Unprocess Request",
|
||||
zap.String("method", "POST"),
|
||||
zap.String("url", fullURL),
|
||||
zap.String("document_number", inv.DocumentNumber),
|
||||
zap.String("invoice_id", inv.ID.String()),
|
||||
)
|
||||
|
||||
// 4. Отправка POST запроса
|
||||
req, err := http.NewRequest("POST", fullURL, bytes.NewReader(xmlPayload))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания запроса: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/xml")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка сети: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Читаем ответ
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения ответа: %w", err)
|
||||
}
|
||||
|
||||
// Логируем ответ
|
||||
logger.Log.Debug("Получен ответ от iiko на распроведение",
|
||||
zap.Int("status_code", resp.StatusCode),
|
||||
zap.String("raw_response", string(respBody)),
|
||||
)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("http error %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// Проверка результата валидации
|
||||
var result DocumentValidationResult
|
||||
if err := xml.Unmarshal(respBody, &result); err != nil {
|
||||
return fmt.Errorf("ошибка разбора XML ответа: %w", err)
|
||||
}
|
||||
|
||||
if !result.Valid {
|
||||
logger.Log.Warn("RMS Invoice Unprocess Failed",
|
||||
zap.String("error", result.ErrorMessage),
|
||||
zap.String("additional", result.AdditionalInfo),
|
||||
)
|
||||
return fmt.Errorf("распроведение не удалось: %s (info: %s)", result.ErrorMessage, result.AdditionalInfo)
|
||||
}
|
||||
|
||||
logger.Log.Info("RMS Invoice Unprocess Success",
|
||||
zap.String("document_number", result.DocumentNumber),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProductByID получает полную структуру товара по ID (через /list?ids=...)
|
||||
func (c *Client) GetProductByID(id uuid.UUID) (*ProductFullDTO, error) {
|
||||
// Параметр ids должен быть списком. iiko ожидает ids=UUID
|
||||
|
||||
Reference in New Issue
Block a user