mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
Добавил черновики накладных и OCR через Яндекс. LLM для расшифровки универсальный
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"rmser/internal/domain/catalog"
|
||||
"rmser/internal/domain/drafts"
|
||||
"rmser/internal/domain/invoices"
|
||||
"rmser/internal/domain/ocr"
|
||||
"rmser/internal/domain/operations"
|
||||
@@ -48,10 +49,13 @@ func NewPostgresDB(dsn string) *gorm.DB {
|
||||
&catalog.Product{},
|
||||
&catalog.MeasureUnit{},
|
||||
&catalog.ProductContainer{},
|
||||
&catalog.Store{},
|
||||
&recipes.Recipe{},
|
||||
&recipes.RecipeItem{},
|
||||
&invoices.Invoice{},
|
||||
&invoices.InvoiceItem{},
|
||||
&drafts.DraftInvoice{},
|
||||
&drafts.DraftInvoiceItem{},
|
||||
&operations.StoreOperation{},
|
||||
&recommendations.Recommendation{},
|
||||
&ocr.ProductMatch{},
|
||||
|
||||
@@ -116,3 +116,19 @@ func (r *pgRepository) GetActiveGoods() ([]catalog.Product, error) {
|
||||
Find(&products).Error
|
||||
return products, err
|
||||
}
|
||||
|
||||
func (r *pgRepository) SaveStores(stores []catalog.Store) error {
|
||||
if len(stores) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.db.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "id"}},
|
||||
UpdateAll: true,
|
||||
}).CreateInBatches(stores, 100).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) GetActiveStores() ([]catalog.Store, error) {
|
||||
var stores []catalog.Store
|
||||
err := r.db.Where("is_deleted = ?", false).Order("name ASC").Find(&stores).Error
|
||||
return stores, err
|
||||
}
|
||||
|
||||
85
internal/infrastructure/repository/drafts/postgres.go
Normal file
85
internal/infrastructure/repository/drafts/postgres.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package drafts
|
||||
|
||||
import (
|
||||
"rmser/internal/domain/drafts"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewRepository(db *gorm.DB) drafts.Repository {
|
||||
return &pgRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *pgRepository) Create(draft *drafts.DraftInvoice) error {
|
||||
return r.db.Create(draft).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) GetByID(id uuid.UUID) (*drafts.DraftInvoice, error) {
|
||||
var draft drafts.DraftInvoice
|
||||
err := r.db.
|
||||
Preload("Items", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("draft_invoice_items.raw_name ASC")
|
||||
}).
|
||||
Preload("Items.Product").
|
||||
Preload("Items.Product.MainUnit"). // Нужно для отображения единиц
|
||||
Preload("Items.Container").
|
||||
Where("id = ?", id).
|
||||
First(&draft).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &draft, nil
|
||||
}
|
||||
|
||||
func (r *pgRepository) Update(draft *drafts.DraftInvoice) error {
|
||||
// Обновляем только основные поля шапки
|
||||
return r.db.Model(draft).Updates(map[string]interface{}{
|
||||
"status": draft.Status,
|
||||
"document_number": draft.DocumentNumber,
|
||||
"date_incoming": draft.DateIncoming,
|
||||
"supplier_id": draft.SupplierID,
|
||||
"store_id": draft.StoreID,
|
||||
"comment": draft.Comment,
|
||||
"rms_invoice_id": draft.RMSInvoiceID,
|
||||
"updated_at": gorm.Expr("NOW()"),
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) CreateItems(items []drafts.DraftInvoiceItem) error {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.db.CreateInBatches(items, 100).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) UpdateItem(itemID uuid.UUID, productID *uuid.UUID, containerID *uuid.UUID, qty, price decimal.Decimal) error {
|
||||
// Пересчитываем сумму
|
||||
sum := qty.Mul(price)
|
||||
|
||||
// Определяем статус IsMatched: если productID задан - значит сматчено
|
||||
isMatched := productID != nil
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"product_id": productID,
|
||||
"container_id": containerID,
|
||||
"quantity": qty,
|
||||
"price": price,
|
||||
"sum": sum,
|
||||
"is_matched": isMatched,
|
||||
}
|
||||
|
||||
return r.db.Model(&drafts.DraftInvoiceItem{}).
|
||||
Where("id = ?", itemID).
|
||||
Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) Delete(id uuid.UUID) error {
|
||||
return r.db.Delete(&drafts.DraftInvoice{}, id).Error
|
||||
}
|
||||
@@ -45,6 +45,11 @@ func (r *pgRepository) SaveMatch(rawName string, productID uuid.UUID, quantity d
|
||||
})
|
||||
}
|
||||
|
||||
func (r *pgRepository) DeleteMatch(rawName string) error {
|
||||
normalized := strings.ToLower(strings.TrimSpace(rawName))
|
||||
return r.db.Where("raw_name = ?", normalized).Delete(&ocr.ProductMatch{}).Error
|
||||
}
|
||||
|
||||
func (r *pgRepository) FindMatch(rawName string) (*ocr.ProductMatch, error) {
|
||||
normalized := strings.ToLower(strings.TrimSpace(rawName))
|
||||
var match ocr.ProductMatch
|
||||
|
||||
@@ -32,6 +32,7 @@ type ClientI interface {
|
||||
Auth() error
|
||||
Logout() error
|
||||
FetchCatalog() ([]catalog.Product, error)
|
||||
FetchStores() ([]catalog.Store, error)
|
||||
FetchMeasureUnits() ([]catalog.MeasureUnit, error)
|
||||
FetchRecipes(dateFrom, dateTo time.Time) ([]recipes.Recipe, error)
|
||||
FetchInvoices(from, to time.Time) ([]invoices.Invoice, error)
|
||||
@@ -337,6 +338,52 @@ func (c *Client) FetchCatalog() ([]catalog.Product, error) {
|
||||
return products, nil
|
||||
}
|
||||
|
||||
// FetchStores загружает список складов (Account -> INVENTORY_ASSETS)
|
||||
func (c *Client) FetchStores() ([]catalog.Store, error) {
|
||||
resp, err := c.doRequest("GET", "/resto/api/v2/entities/list", map[string]string{
|
||||
"rootType": "Account",
|
||||
"includeDeleted": "false",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get stores error: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var dtos []AccountDTO
|
||||
if err := json.NewDecoder(resp.Body).Decode(&dtos); err != nil {
|
||||
return nil, fmt.Errorf("json decode stores error: %w", err)
|
||||
}
|
||||
|
||||
var stores []catalog.Store
|
||||
for _, d := range dtos {
|
||||
// Фильтруем только склады
|
||||
if d.Type != "INVENTORY_ASSETS" {
|
||||
continue
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(d.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var parentCorpID uuid.UUID
|
||||
if d.ParentCorporateID != nil {
|
||||
if parsed, err := uuid.Parse(*d.ParentCorporateID); err == nil {
|
||||
parentCorpID = parsed
|
||||
}
|
||||
}
|
||||
|
||||
stores = append(stores, catalog.Store{
|
||||
ID: id,
|
||||
Name: d.Name,
|
||||
ParentCorporateID: parentCorpID,
|
||||
IsDeleted: d.Deleted,
|
||||
})
|
||||
}
|
||||
|
||||
return stores, nil
|
||||
}
|
||||
|
||||
// FetchMeasureUnits загружает справочник единиц измерения
|
||||
func (c *Client) FetchMeasureUnits() ([]catalog.MeasureUnit, error) {
|
||||
// rootType=MeasureUnit согласно документации iiko
|
||||
|
||||
@@ -28,6 +28,15 @@ type GenericEntityDTO struct {
|
||||
Deleted bool `json:"deleted"`
|
||||
}
|
||||
|
||||
// AccountDTO используется для парсинга складов (INVENTORY_ASSETS)
|
||||
type AccountDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` // Нас интересует "INVENTORY_ASSETS"
|
||||
ParentCorporateID *string `json:"parentCorporateId"`
|
||||
Deleted bool `json:"deleted"`
|
||||
}
|
||||
|
||||
// ContainerDTO - фасовка из iiko
|
||||
type ContainerDTO struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
Reference in New Issue
Block a user