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{}, } }