cml04-plb018/internal/service/service.go

511 lines
16 KiB
Go
Raw Permalink Normal View History

2024-08-20 15:50:52 +02:00
package service
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
"strings"
"sync"
"time"
cml04eventer "git.espin.casa/albert/cml04-eventer"
"git.espin.casa/albert/cml04-plb018/internal/helpers"
"git.espin.casa/albert/cml04-plb018/internal/storage"
"git.espin.casa/albert/cml04-plb018/internal/types"
"gorm.io/gorm"
)
var (
NSQTOPIC = map[types.Robot]string{
types.Robot12: "ROBOT12",
types.Robot345: "ROBOT345",
}
)
type IService interface {
// Etiquetas
Etiqueta(ctx context.Context, req *types.EtiquetaReq) (res *types.EtiquetaRes, err error)
Etiquetas(ctx context.Context, req *types.EtiquetasReq) (res *types.EtiquetasRes, err error)
SalvarEtiqueta(ctx context.Context, req *types.SalvarEtiquetaReq) (res *types.SalvarEtiquetaRes, err error)
// Normas
Normas(ctx context.Context, req *types.NormasReq) (res *types.NormasRes, err error)
// Orden Produccion
OrdenProduccion(ctx context.Context, req *types.OrdenProduccionReq) (res *types.OrdenProduccionRes, err error)
OrdenesProduccion(ctx context.Context, req *types.OrdenesProduccionReq) (res *types.OrdenesProduccionRes, err error)
// Orden Cliente
OrdenCliente(ctx context.Context, req *types.OrdenClienteReq) (res *types.OrdenClienteRes, err error)
OrdenesCliente(ctx context.Context, req *types.OrdenesClienteReq) (res *types.OrdenesClienteRes, err error)
// Impresora
Impresora(ctx context.Context, req *types.ImpresoraReq) (res *types.ImpresoraRes, err error)
Impresoras(ctx context.Context, req *types.ImpresorasReq) (res *types.ImpresorasRes, err error)
SalvarImpresora(ctx context.Context, req *types.SalvarImpresoraReq) (res *types.SalvarImpresoraRes, err error)
// Paquetes
Paquete(ctx context.Context, req *types.PaqueteReq) (res *types.PaqueteRes, err error)
Paquetes(ctx context.Context, req *types.PaquetesReq) (res *types.PaquetesRes, err error)
ImprimirPaquete(ctx context.Context, req *types.ImprimirPaqueteReq) (res *types.ImprimirPaqueteRes, err error)
ImprimirPaqueteRobot(ctx context.Context, req *types.ImprimirPaqueteReq) (res *types.ImprimirPaqueteRes, err error)
// Nro Paquetes
NroPaqueteActual(ctx context.Context, req *types.NroPaqueteActualReq) (res *types.NroPaqueteActualRes, err error)
NroPaqueteSiguiente(ctx context.Context, req *types.NroPaqueteSiguienteReq) (res *types.NroPaqueteSiguienteRes, err error)
}
type service struct {
pub cml04eventer.Publisher
storage storage.Storager
mux *sync.Mutex
}
// NroPaqueteActual implements IService.
func (s *service) NroPaqueteActual(ctx context.Context, req *types.NroPaqueteActualReq) (res *types.NroPaqueteActualRes, err error) {
nroMatricula, err := helpers.NroPaqueteActual(req.PEtiquetado)
if err != nil {
return nil, err
}
return &types.NroPaqueteActualRes{
NroMatricula: nroMatricula,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// NroPaqueteSiguiente implements IService.
func (s *service) NroPaqueteSiguiente(ctx context.Context, req *types.NroPaqueteSiguienteReq) (res *types.NroPaqueteSiguienteRes, err error) {
nroMatricula, err := helpers.NroPaqueteSiguiente(req.PEtiquetado)
if err != nil {
return nil, err
}
return &types.NroPaqueteSiguienteRes{
NroMatricula: nroMatricula,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// ImprimirPaqueteRobot implements IService.
func (s *service) ImprimirPaqueteRobot(ctx context.Context, req *types.ImprimirPaqueteReq) (res *types.ImprimirPaqueteRes, err error) {
// create buffer
buff := bytes.Buffer{}
// encode request
if err := json.NewEncoder(&buff).Encode(req); err != nil {
return nil, err
}
// get nsq robot topic
topic, ok := NSQTOPIC[req.Robot]
if !ok {
return nil, fmt.Errorf("robot topic not found")
}
// create meta header event
meta := cml04eventer.NewMetaData()
// set subjet
meta.Set("subject", "pbl018.print.data")
// create event
event := cml04eventer.NewEvent("PLB018", topic, meta, buff.Bytes())
// publish event
if err := s.pub.PublishEvent(event); err != nil {
return nil, err
}
// crear paquete
paquete, err := s.CrearDatosPaquete(ctx, req)
if err != nil {
return nil, err
}
// guardar paquete
if err := s.SalvarPaquete(ctx, paquete); err != nil {
return nil, err
}
// return response
return &types.ImprimirPaqueteRes{
Respuesta: fmt.Sprintf("imprimir paquete [%s] mediante robot finalizó con éxito", req.DatosEtiqueta.NroMatricula),
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Paquete implements IService.
func (s *service) Paquete(ctx context.Context, req *types.PaqueteReq) (res *types.PaqueteRes, err error) {
paquete, err := s.storage.Paquete(ctx, req.Codigo)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.PaqueteRes{
Paquete: paquete,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Paquetes implements IService.
func (s *service) Paquetes(ctx context.Context, req *types.PaquetesReq) (res *types.PaquetesRes, err error) {
inicio, err := time.Parse("02/01/2006 15:04:05", req.Inicio)
if err != nil {
return nil, err
}
final, err := time.Parse("02/01/2006 15:04:05", req.Final)
if err != nil {
return nil, err
}
paquetes, err := s.storage.Paquetes(ctx, inicio, final)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.PaquetesRes{
Paquetes: paquetes,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Etiqueta implements IService.
func (s *service) Etiqueta(ctx context.Context, req *types.EtiquetaReq) (res *types.EtiquetaRes, err error) {
etiqueta, err := s.storage.Etiqueta(ctx, req.Codigo)
if err != nil {
return nil, err
}
return &types.EtiquetaRes{
Etiqueta: etiqueta,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Etiquetas implements IService.
func (s *service) Etiquetas(ctx context.Context, req *types.EtiquetasReq) (res *types.EtiquetasRes, err error) {
// get etiquetas
etiquetas, err := s.storage.Etiquetas(ctx)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.EtiquetasRes{
Etiquetas: etiquetas,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
func (s *service) SalvarEtiqueta(ctx context.Context, req *types.SalvarEtiquetaReq) (res *types.SalvarEtiquetaRes, err error) {
if err := s.storage.SalvarEtiqueta(ctx, req.Etiqueta); err != nil {
return nil, err
}
// return response
return &types.SalvarEtiquetaRes{
Respuesta: fmt.Sprintf("save label: %s success", req.Etiqueta.Codigo),
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// SalvarImpresora implements IService.
func (s *service) SalvarImpresora(ctx context.Context, req *types.SalvarImpresoraReq) (res *types.SalvarImpresoraRes, err error) {
if err := s.storage.SalvarImpresora(ctx, req.Impresora); err != nil {
return nil, err
}
// return response
return &types.SalvarImpresoraRes{
Message: fmt.Sprintf("save printer: %s success", req.Impresora.ID),
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Impresora implements IService.
func (s *service) Impresora(ctx context.Context, req *types.ImpresoraReq) (res *types.ImpresoraRes, err error) {
// get impresora
zebra, err := s.storage.Impresora(ctx, req.ID)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.ImpresoraRes{
Impresora: zebra,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Impresoras implements IService.
func (s *service) Impresoras(ctx context.Context, req *types.ImpresorasReq) (res *types.ImpresorasRes, err error) {
// get impresoras
zebras, err := s.storage.Impresoras(ctx)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.ImpresorasRes{
Impresoras: zebras,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, err
}
// OrdenesProduccion implements IService.
func (s *service) OrdenesProduccion(ctx context.Context, req *types.OrdenesProduccionReq) (res *types.OrdenesProduccionRes, err error) {
// get ordenes produccion
pos, err := s.storage.OrdenesProduccion(ctx, req.Limit)
// handle error
if err != nil {
return nil, err
}
// return response
return &types.OrdenesProduccionRes{
OrdenesProduccion: pos,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// OrdenesCliente implements IService.
func (s *service) OrdenesCliente(ctx context.Context, req *types.OrdenesClienteReq) (res *types.OrdenesClienteRes, err error) {
// get ordenes clientes
cos, err := s.storage.OrdenesCliente(ctx, req.PoID)
// handle error
if err != nil {
return nil, err
}
return &types.OrdenesClienteRes{OrdenesCliente: cos, TimeStamp: time.Now().UTC().Format(time.RFC3339)}, nil
}
// OrdenCliente implements IService.
func (s *service) OrdenCliente(ctx context.Context, req *types.OrdenClienteReq) (res *types.OrdenClienteRes, err error) {
// get orden cliente
co, err := s.storage.OrdenCliente(ctx, req.CoID)
// handle error
if err != nil {
return nil, err
}
return &types.OrdenClienteRes{
OrdenCliente: co,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// OrdenProduccion implements IService.
func (s *service) OrdenProduccion(ctx context.Context, req *types.OrdenProduccionReq) (res *types.OrdenProduccionRes, err error) {
// get orden produccion
po, err := s.storage.OrdenProduccion(ctx, req.PoID)
if err != nil {
return nil, err
}
return &types.OrdenProduccionRes{
OrdenProduccion: po,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
// Normas implements IService.
func (s *service) Normas(ctx context.Context, req *types.NormasReq) (res *types.NormasRes, err error) {
normas, err := s.storage.Normas(ctx, req.Codigo)
if err != nil {
return nil, err
}
return &types.NormasRes{
Normas: normas,
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
func TipoEtiqueta(producto string) types.EtiquetaTipo {
if strings.HasPrefix(producto, "W") {
return types.EtiquetaAmericana
} else if strings.HasPrefix(producto, "UB") || strings.HasPrefix(producto, "UC") || strings.HasPrefix(producto, "UPF") {
return types.EtiquetaBritanica
} else {
return types.EtiquetaEuropea
}
}
func (s *service) SalvarPaquete(ctx context.Context, paquete *types.Paquete) error {
// check if bundle already exists
_, err := s.storage.Paquete(ctx, paquete.NroPaquete)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// doesn't exists create it!
return s.CrearPaquete(ctx, paquete)
} else {
// error
return err
}
} else {
// exists update it!
return s.storage.ActualizarPaquete(ctx, paquete)
}
}
func (s *service) CrearDatosPaquete(ctx context.Context, req *types.ImprimirPaqueteReq) (*types.Paquete, error) {
// get orden cliente
co, err := s.storage.OrdenCliente(ctx, req.DatosEtiqueta.CoID)
if err != nil {
return nil, err
}
// get material code
matnr := co.MaterCode
// get norma code
matnr = strings.Split(matnr, "-")[0]
// get normas
normas, err := s.storage.Normas(ctx, matnr)
if err != nil {
return nil, err
}
// get production order
po, err := s.storage.OrdenProduccion(ctx, co.POrderNo)
if err != nil {
return nil, err
}
// barras
barras, err := strconv.Atoi(req.DatosEtiqueta.NrBarras)
if err != nil {
return nil, err
}
// get longitud
longitud, err := strconv.ParseFloat(co.OrdLen, 64)
if err != nil {
return nil, err
}
longitud = longitud / 1000
// create bundle data
return &types.Paquete{
NroPaquete: req.DatosEtiqueta.NroMatricula,
OrdenProduccion: po.POrderNo,
OrdenCliente: co.COrderNo,
Producto: po.FSection,
Colada: req.DatosEtiqueta.Colada,
Calidad: normas.Calidad,
Matnr: co.MaterCode,
Operador: req.DatosEtiqueta.Operador,
Turno: req.DatosEtiqueta.Turno,
Longitud: longitud,
Barras: uint8(barras),
Peso: req.DatosEtiqueta.Peso,
PesoTeorico: req.DatosEtiqueta.PesoTeorico,
Normed: normas.NormaMedida,
Norpro: normas.NormaProducto,
Nortol: normas.NormaTolerancia,
DesvioPeso: req.DatosEtiqueta.DesvioPeso,
Confirmado: false,
Procesado: false,
Observaciones: "",
Reclasificado: false,
}, nil
}
// ImprimirPaquete implements IService.
func (s *service) ImprimirPaquete(ctx context.Context, req *types.ImprimirPaqueteReq) (res *types.ImprimirPaqueteRes, err error) {
// crear datos paq
paquete, err := s.CrearDatosPaquete(ctx, req)
if err != nil {
return nil, err
}
// salvar paquete
if err := s.SalvarPaquete(ctx, paquete); err != nil {
return nil, err
}
// create label zpl code
contenido, err := s.CrearEtiqueta(
ctx,
req.DatosEtiqueta.NroMatricula,
// convert longitud to string
strconv.FormatFloat(paquete.Longitud, 'f', 2, 64),
req.DatosEtiqueta.Colada,
paquete.Normed,
paquete.Norpro,
paquete.Nortol,
paquete.Calidad,
time.Now().Format("02/01/2006"),
req.DatosEtiqueta.NrBarras,
paquete.Producto,
req.DatosEtiqueta.NrEtiquetas,
TipoEtiqueta(paquete.Producto),
)
// handle error
if err != nil {
return nil, err
}
// imprimir
if err := s.Imprimir(ctx, contenido, req.ImpresoraID); err != nil {
return nil, err
}
// return response
return &types.ImprimirPaqueteRes{
Respuesta: "imprimir etiqueta success",
TimeStamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
func (s *service) CrearPaquete(ctx context.Context, paquete *types.Paquete) error {
return s.storage.SalvarPaquete(ctx, paquete)
}
func (s *service) ActualizarPaquete(ctx context.Context, paquete *types.Paquete) error {
return s.storage.ActualizarPaquete(ctx, paquete)
}
func (s *service) CrearEtiqueta(ctx context.Context, nroMatricula, longitud, colada, normaa, normab, normac, calidad, fecha, barras, producto, nroEtis string, tipo types.EtiquetaTipo) ([]byte, error) {
// get formato
formato, ok := types.TipoFormato[tipo]
if !ok {
return nil, fmt.Errorf("tipo formato etiqueta no existe")
}
// get etiqueta
etiqueta, err := s.storage.Etiqueta(ctx, formato)
if err != nil {
return nil, err
}
// si el producto es americano la longitud se convierte a pies
if helpers.EsAmericano(producto) {
// convertir longitud a pies
l, err := strconv.ParseFloat(longitud, 64)
if err != nil {
return nil, err
}
longitud = strconv.FormatFloat(l, 'f', 2, 64)
}
// replace all template with label data
contenido := etiqueta.Contenido
contenido = strings.ReplaceAll(contenido, "9999999999", nroMatricula)
contenido = strings.ReplaceAll(contenido, "{{longitud}}", longitud)
contenido = strings.ReplaceAll(contenido, "{{colada}}", colada)
contenido = strings.ReplaceAll(contenido, "{{normaA}}", normaa)
contenido = strings.ReplaceAll(contenido, "{{normaB}}", normab)
contenido = strings.ReplaceAll(contenido, "{{normaC}}", normac)
contenido = strings.ReplaceAll(contenido, "{{calidad}}", calidad)
contenido = strings.ReplaceAll(contenido, "{{fecha}}", fecha)
contenido = strings.ReplaceAll(contenido, "{{barras}}", barras)
contenido = strings.ReplaceAll(contenido, "{{producto}}", producto)
contenido = strings.ReplaceAll(contenido, "{{etiquetas}}", nroEtis)
// return
return []byte(contenido), nil
}
func (s *service) Imprimir(ctx context.Context, data []byte, ImpresoraID string) error {
if strings.ToUpper(ImpresoraID) == "NONE" {
return nil
}
s.mux.Lock()
defer s.mux.Unlock()
impresora, err := s.storage.Impresora(ctx, ImpresoraID)
if err != nil {
return err
}
// connect to printer
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", impresora.Address, impresora.Port))
if err != nil {
return err
}
// close connection
defer conn.Close()
// send data
_, err = conn.Write(data)
if err != nil {
return err
}
return nil
}
func NewService(storage storage.Storager, pub cml04eventer.Publisher) IService {
return &service{
pub: pub,
storage: storage,
mux: &sync.Mutex{},
}
}