Commit d102e9d1 by Juan

Merge branch 'pagoavanzado' into 'master'

Pagoavanzado

See merge request !3
2 parents 3ac9329a ad666c58
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml # Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
# for the full list # for the full list
'category': 'Localization/Argentina', 'category': 'Localization/Argentina',
'version': '1.07.01.21', 'version': '1.08.02.27',
# any module necessary for this one to work correctly # any module necessary for this one to work correctly
'depends': ['base', 'depends': ['base',
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
'asw_afipws_fe', 'asw_afipws_fe',
'asw_tpv_impuestos', 'asw_tpv_impuestos',
'asw_cajas', 'asw_cajas',
'asw_tpv_cheques',
], ],
# always loaded # always loaded
...@@ -45,8 +46,11 @@ ...@@ -45,8 +46,11 @@
'views/gastosmobil.xml', 'views/gastosmobil.xml',
'views/comprobante.xml', 'views/comprobante.xml',
'views/referencia_compra.xml', 'views/referencia_compra.xml',
'views/relaciones_talonarios.xml',
'views/menu_compras.xml', 'views/menu_compras.xml',
'views/pago_discriminado.xml',
'wizard/vista_cobro_discriminado.xml', 'wizard/vista_cobro_discriminado.xml',
'wizard/vista_pago_discriminado.xml',
], ],
# only loaded in demonstration mode # only loaded in demonstration mode
'demo': [ 'demo': [
......
...@@ -4,4 +4,9 @@ from . import comprobante ...@@ -4,4 +4,9 @@ from . import comprobante
from . import referencias from . import referencias
from . import referencia_compra from . import referencia_compra
from . import gastosmobil from . import gastosmobil
from . import currency
\ No newline at end of file \ No newline at end of file
from . import currency
from . import pago_discriminado
from . import cheque
from . import valor
from . import cta_cte
from . import ralaciones_talonarios
\ No newline at end of file \ No newline at end of file
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
class asw_cheque(models.Model):
_inherit = 'asw.cheque'
che_estado = fields.Selection(
string = 'Estado',
selection = [
('b', 'Borrador'),
('en_mano', 'En mano'),
('reservado', 'Reservado'),
('entregado', 'Entregado'),
('depositado', 'Depositado'),
('devuelto', 'Devuelto'),
('cambiado', 'Cambiado'),
('r', 'Rechazado')
],
compute = '_compute_estado_cheque',
store = True
)
\ No newline at end of file \ No newline at end of file
# -*- coding: utf-8 -*-
from odoo import models, fields, api, exceptions
from .qr import qrfiscal
from odoo.exceptions import UserError, ValidationError, Warning, RedirectWarning
import base64, datetime
class res_currency(models.Model):
_inherit = 'asw.cta_cte'
"""Heredo esta classe para modificar el comportamiento de la cuenta corriente
No quiero que se afecte Factura - Factura"""
@api.multi
def afectar(self, id_afectada=False):
""" si se pasa un id_afectada la afectacion se realiza sobre este
de otra manera se afecta a todos los comprobantes en orden de aparicion """
afectaciones = []
if (id_afectada is not False and isinstance(id_afectada, int)):
# Solo debe afectar por el monto adeudado del comprobante
cc_afectadora = self.search([('cc_comprobante', '=', id_afectada)])
afectacion = self.genera_afectacion(cc_afectadora)
afectaciones.append(afectacion)
elif (self.cc_cliente.id is False):
return
fac = ["fac","rem","rve","ped","pre","inv"]#Esto cambio el metodo de reintegro hay q pensar q hacemos
res = ["rec","rem","rve","ped","pre","inv"]
blokear = [] #Por puta seguridad
if self.cc_comprobante.comp_talonario.tal_menu == "fac":
blokear = fac # Si es factura blokea factura
else:
blokear = res # si no es factura es recibo blokero recibos
cc_cliente = self.search([('cc_cliente','=', self.cc_cliente.id),
('cc_tipo_relacionado','!=', self.cc_tipo_relacionado),
('cc_monto_adeudado','!=', 0)])
for cc_afectada in cc_cliente:
if cc_afectada.cc_comprobante.comp_talonario.tal_menu in blokear:#si esta en la lista me lo salto
break
cc_afectada._compute_cc_monto_adeudado()
if(self.cc_monto_adeudado != 0):
afectacion = self.genera_afectacion(cc_afectada)
afectaciones.append(afectacion)
else:
break
return afectaciones
\ No newline at end of file \ No newline at end of file
# -*- coding: utf-8 -*-
###############################################################################
# License, author and contributors information in: #
# __manifest__.py file at the root folder of this module. #
###############################################################################
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError, Warning
import datetime
class hgt_cobro_multiple(models.Model):
_name = 'hgt.pago_multiple_avanzado'
_description = 'Pago multiple avanzado'
_inherit = ['asw.action.manager']
_order = "id desc"
name = fields.Char(string='Codigo',
default="Pago en borrador")
estado = fields.Selection(string='Estado', selection=[('borrador', 'Borrador'),
('aprobado', 'Aprobado'),
('pagado', 'Pagado'),
('cancelado', 'Cancelado'),
], default="borrador")
# Informacion pago
pcw_cliente = fields.Many2one(
comodel_name='asw.cliente',
string='Cliente',
readonly=True,
compute='_compute_cliente',
required=True
)
caja_efectivo = fields.Many2one(
comodel_name='asw.caja',
string='Caja Efectivo',
)
caja_cheque = fields.Many2one(
comodel_name='asw.caja',
string='Caja Cheques',
)
caja_cheque_terceros = fields.Many2one(
comodel_name='asw.caja',
string='Caja Cheques',
)
nota = fields.Text(string='nota',readonly=True,
compute='_compute_nota')
nota_fija = fields.Char(string='Nota Fija')
pcw_referencia = fields.Char(
string=u'Referencia',
)
pcw_efectivo = fields.Monetary(
string='Efectivo',
currency_field='cli_moneda_empresa'
)
pcw_retencion_emitida = fields.Monetary(
string='Retención',
currency_field='cli_moneda_empresa'
)
pcw_cli_razon_social = fields.Char(
string='Razon Social',
related='pcw_cliente.cli_razon_social'
)
# Currency field
cli_moneda_empresa = fields.Many2one(
string=u'Moneda del saldo',
comodel_name='res.currency',
ondelete='set null',
related='pcw_cliente.create_uid.company_id.currency_id',
)
# Formas de pago
pcw_chq_emitidos = fields.Many2many(
comodel_name='asw.cheque',
relation='pago_discriminado_chq_emitido_pagos', #ya lo edite
column1='asw_cheque_id',
column2='asw_cliente_id',
string='Cheques Propios'
)
multicheques = fields.Boolean(string='Interno')
buscador_cheque = fields.Many2one(comodel_name='asw.cheque', string='Buscador de cheques en mano')
pcw_chq_terceros = fields.Many2many(
comodel_name='asw.cheque',
relation='pago_discriminado_chq_terceros_pagos', #ya lo edite
column1='asw_cheque_id',
column2='asw_cliente_id',
string='Cheques Propios',
)
pcw_transferencias_emitidas = fields.Many2many(
comodel_name='asw.valores',
relation='pago_discriminado_transferencia_emitida_pagos', #Ya lo edite
column1='pago_cliente_id',
column2='asw_valores_id',
string='Transferencias Emitidas'
)
pcw_tarjetas = fields.Many2many(
comodel_name='asw.valores',
relation='hgt_pago_discriminado_tarjeta_pagos', #ya lo edite
column1='pago_cliente_id',
column2='asw_valores_id',
string='Tarjetas'
)
pcw_retenciones = fields.Many2many(
comodel_name='asw.valores',
relation='pago_discriminado_retencion_pagos',#ya lo edite
column1='pago_cliente_id',
column2='asw_valores_id',
string='Retenciones'
)
# Total de pago
pcw_total = fields.Monetary(
string='Total a entregar',
compute='_calcular_total',
currency_field='cli_moneda_empresa'
)
pcw_recibo = fields.Many2one(
string='Recibo',
comodel_name='asw.comprobante'
)
comprobantes = fields.Many2many(comodel_name='asw.comprobante', string='Comprobantes')
pcw_total_balance = fields.Monetary(
string='Diferencia',
compute='_calcular_diferencia',
currency_field='cli_moneda_empresa'
)
pcw_total_pagar = fields.Monetary(
string='Total deuda',
compute='_calcular_pago',
currency_field='cli_moneda_empresa'
)
###########################################Validaciones y calculos ######################################
@api.depends('pcw_total')
def _compute_cliente(self):
for record in self:
record.correccion_compute_cliente()
def correccion_compute_cliente(self,raiser=False):
if not len(self.pcw_cliente) == 0:
return(None)
context = self.env.context
if len(self.comprobantes) == 0:
active_ids = context.get('active_ids')
facturas = self.env['asw.comprobante'].search([('id', 'in', active_ids)])
self.comprobantes = facturas.ids
else:
facturas = self.comprobantes
idc = []
arroz = []
for factura in facturas:
if not factura.comp_cliente.id in idc:
idc.append(factura.comp_cliente.id)
if not factura.comp_adeudado > 0:
arroz.append(f"Factura {factura.display_name} ya esta pagada o valor nulo\n")
if factura.comp_estado in ["b","c"]:
arroz.append(f"Factura {factura.display_name} en estado invalido para pagar\n")
if (factura.comp_talonario.tal_tipo == "e"):
arroz.append(f"El comprobante {factura.display_name} es de venta\n")
if not len(idc) == 1:
arroz.append(f"Solo se puede cobrar a un cliente a la vez, cierre la ventana y seleccione de vuelta los comprobantes a operar")
if (arroz != []) and (raiser == True):
mensaje_error = ""
for text_arr in arroz:
mensaje_error = f"{mensaje_error}{text_arr}"
raise Warning(mensaje_error)
try:
self.pcw_cliente = idc[0]
except :
pass
@api.depends('comprobantes')
def _compute_nota(self):
for record in self:
#print(record)
record.compute_nota()
def compute_nota(self):
if self.nota_fija != False:
self.nota = self.nota_fija
return(None)
self.nota = self.calcular_nota()
def calcular_nota(self):
context = self.env.context
Texto = "Facturas de proveedores a pagar:"
Total = 0
for factura in self.comprobantes:
Total = Total + factura.comp_adeudado
Texto = f"{Texto}\n{factura.display_name} {factura.comp_adeudado}"
Texto = f"{Texto}\nTotal {Total}".replace(".",self.PuntoDecimal())
#print(self.nota)
return(Texto)
@api.depends('pcw_efectivo','pcw_tarjetas','pcw_transferencias_emitidas','pcw_retenciones','pcw_chq_emitidos','pcw_chq_terceros')
def _calcular_total(self):
total = 0
for record in self.pcw_chq_emitidos:
total = total + record.che_monto
for record in self.pcw_chq_terceros:
total = total + record.che_monto
for record in self.pcw_transferencias_emitidas:
total = total + record.val_monto
for record in self.pcw_tarjetas:
total = total + record.val_monto
for record in self.pcw_retenciones:
total = total + record.val_monto
self.pcw_total = total + self.pcw_efectivo #+ self.pcw_retencion_recibida
@api.depends('pcw_total')
def _calcular_diferencia(self):
total = self.pcw_total_pagar - self.pcw_total #+ self.pcw_retencion_recibida
self.pcw_total_balance = total
@api.depends('comprobantes')
def _calcular_pago(self):
total = 0
for fac in self.comprobantes:
total = total + fac.comp_total
self.pcw_total_pagar = total
################################# LOGICA DE PAGO #################
@api.multi
def pago_facturas_masivo(self):
facturas = self.comprobantes
self.FijarNota()#Acienta el estado del detalle
if len(facturas) == 0:#no hay facturas seleccionadas
raise Warning("No se seleccionaron facturas")
resivo = self.CrearValidarResivoMultiple()#revisado
if self.pcw_total != resivo.comp_total:#Error en el calculo
raise Warning("Error al procesar el pago informar a soporte")
##############Aca terminan las validaciones y empesamos a asignaR############################################
total = self.pcw_total
for factura in facturas:
if factura.comp_estado in ["c","b"]:
raise Warning(f"Factura {factura.display_name} esta cancelada o sin validar no se puede Cobrar")
if not factura.comp_adeudado > 0:
raise Warning(f"Factura {factura.display_name} en cero revisar")
if(not factura.comp_talonario.tal_genera_cta_cte):
raise Warning(f"Factura {factura.display_name} no mueve cuenta corriente no puedo manejarla")
total = self.LogicaFacturas(factura,resivo,total)
#obserbacion = f"""{obserbacion}\n{factura.display_name}"""
resivo.aumentar_numeracion_talonario()
resivo.comp_fecha_validacion = fields.Datetime.now()
resivo.generar_monto_adeudado()
resivo.validar_nro_comprobante()
self.ArmarObservaciones(facturas,resivo)
resivo.generar_monto_adeudado()#a prueba
return(resivo)
def ArmarObservaciones(self,facturas,resivo):
obserbacion = "\nFacturas Afectadas | Total | Saldo"
for factura in facturas:
obserbacion = f"""{obserbacion}\n{factura.display_name} | {factura.comp_total} | {factura.comp_adeudado}""".replace(".",self.PuntoDecimal())
resivo.comp_observaciones = obserbacion
@api.multi
def generar_recibo(self):
resivo = self.pago_facturas_masivo()
self.ValoresEnCajas(resivo)
self.estado = "aprobado"
self.BuscarChequesCajas()#Saca el cheque de la caja donde esta
return {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'asw.comprobante',
'type': 'ir.actions.act_window',
'target': 'self',
'res_id': resivo.id,
}
def LogicaFacturas(self,factura,resivo,total):
tmp = total
total = total - factura.comp_adeudado
if tmp >= 0.0:
self.MovimientoCuentaCorriente(factura,resivo,tmp)
return(total)
#Nombre heredado no me gusto pero bue....
def MovimientoCuentaCorriente(self,factura,resivo,total):
#Total = total
#diff = total - factura.comp_adeudado
movimiento = factura.comp_adeudado
if factura.comp_adeudado >= total: #diff < 0.0:
movimiento = total
#print( movimiento, factura.display_name)
if movimiento < 0.0:
raise Warning("debug")
cta_cte = self.env['asw.cta_cte'].create({
'cc_cliente' : resivo.comp_cliente.id,
'cc_comprobante' : resivo.id,
'cc_moneda' : resivo.comp_moneda.id,
'cc_monto' : movimiento
})
cta_cte.afectar(factura.id)
resivo.generar_monto_adeudado()
#print()
def CrearValidarResivoMultiple(self,tipo="r"):
talonario = self.env['asw.talonario'].search([('tal_menu','=','rec'),
('tal_tipo','=',tipo),('tal_mov_caja', '!=', 'sn'),
("tal_codigo","!=","OP")], limit=1)
self.pcw_recibo = self.env['asw.comprobante'].generar_comprobante(
talonario,
self.pcw_cliente,
self.pcw_total,
self.pcw_referencia
)
self.agregar_efectivo()#OK
self.agregar_retencion()#OK
self.agregar_cheques()#OK
self.agregar_transferencias_emitidas()#ok 350
self.agregar_tarjetas()#OK
self.pcw_recibo._onchange_comp_linea_facturas()
self.pcw_recibo.realizar_validaciones()#
self.pcw_recibo._onchange_talonario()
return(self.pcw_recibo)
#################################################LOGICA CREACION RESIVO##################################################
def agregar_efectivo(self):
if self.pcw_efectivo != 0:
efectivo = self.env['asw.tipos_valores'].search([('tv_tipo', '=', 'ef')])
self.pcw_recibo.agregar_valor(self.pcw_efectivo, efectivo, self.caja_efectivo.id)
def agregar_retencion(self):
if(len(self.pcw_retenciones) == 0):
return(None)#Si no hay alguna retencion
for linea in self.pcw_retenciones:
self.pcw_recibo.agregar_valor(linea.val_monto, linea.val_tipo, False)
def agregar_cheques(self):# aca cambia la cosa, lo voy a usar como cheque propio
if(len(self.pcw_chq_emitidos) > 0):
cheque_propio = self.env['asw.tipos_valores'].search([('tv_tipo', '=', 'che')])
for cheque in self.pcw_chq_emitidos: #Cheques propios
self.pcw_recibo.agregar_cheque(cheque, cheque_propio, False)#No afecta caja despues manejo
if(len(self.pcw_chq_terceros) > 0):
cheque_terceros = self.env['asw.tipos_valores'].search([('tv_tipo', '=', 'che')])
for cheque in self.pcw_chq_terceros: #Cheques terceros
self.pcw_recibo.agregar_cheque(cheque, cheque_terceros, False)#No afecta caja despues manejo
def agregar_transferencias_emitidas(self):
transferencia_recibida = self.env['asw.tipos_valores'].search([('tv_tipo','=','tre')],limit=1)
self.pcw_transferencias_emitidas.write({'val_comprobante' : self.pcw_recibo.id, 'val_tipo' : transferencia_recibida.id})
def agregar_tarjetas(self):
tarjeta_credito = self.env['asw.tipos_valores'].search([('tv_tipo','=','ta'),('tv_descripcion','ilike','credito')])
tarjeta_debito = self.env['asw.tipos_valores'].search([('tv_tipo','=','ta'),('tv_descripcion','ilike','débito')])
for record in self.pcw_tarjetas:
if(record.val_tipo.tv_descripcion == 'Tarjeta de Credito'):
record.write({'val_comprobante' : self.pcw_recibo.id, 'val_tipo' : tarjeta_credito.id})
if(record.val_tipo.tv_descripcion == 'Tarjeta de Débito'):
record.write({'val_comprobante' : self.pcw_recibo.id, 'val_tipo' : tarjeta_debito.id})
########################################LOGICA GESTION DE VALORES Y CAJAS ######################################################
#Buscar cheques y desvincular de la caja
#los cheques propios no impactan en las cajas
#las tarjetas de credito no0 impactan en las cajas luego se creare un modelo de convenios de pagos
def BuscarChequesCajas(self):
"""Busco los cheques de terceros de las cajas y los desvinculo para que no afecten la sumatoria"""
for cheque in self.pcw_chq_terceros:
valores = self.env["asw.valores"].search([["val_cheque","=",cheque.id]])
self.BuscarValorEnCaja(valores,cheque)
def BuscarValorEnCaja(self,valores,cheque):
mod_cajas = self.env["asw.caja"]
for valor in valores:
cajas = mod_cajas.search([["caja_valor_ids","in",valor.id]])
for caja in cajas: # por si acaso, se puede dar que no tenga caja asignado
caja.caja_valor_ids = [(3,valor.id)]
def ValoresEnCajas(self,resivo):
#Clasifico el tipo de valor
efectivo = []
cheques = []
transferencias = []
tarjetas = []
for valor in resivo.comp_valores:
if valor.val_tipo.tv_tipo == "ef":
efectivo.append(valor)
if valor.val_tipo.tv_tipo == "chr":
cheques.append(valor)
if valor.val_tipo.tv_tipo == "trr":
transferencias.append(valor)
if valor.val_tipo.tv_tipo == "ta":
tarjetas.append(valor)
#Asocio segun el tipo de valor
self.MoverValores(efectivo, self.caja_efectivo.id)
#self.MoverValores(cheques, self.caja_cheque.id)
#self.MoverValores(transferencias)
#self.MoverValores(tarjetas)
def MoverValores(self, valores, caja_destino_id=True):
"""mueve los valores de los recivos segun se configure"""
for valor in valores:
if caja_destino_id == True:
caja_destino_id = valor.valor_caja_id.id
#if caja_destino_id == False:
#raise Warning("No esta definida una de las cajas, todos los valores deven tener cajas asignadas")
valor.valor_caja_id.caja_valor_ids = [(3,valor.id,)]#elimino el valor de la caja actual
valor.valor_caja_id = caja_destino_id #cambio la caja en el valor
valor.valor_caja_id.caja_valor_ids = [(4,valor.id,)]#agrego el valor a la caja destino
############################################Asistente##################################################################
def PuntoDecimal(self):
"""devuelve si es coma o punto segun la configuracion del sistema"""
idioma = self.env.user.lang
res_lang = self.env["res.lang"].sudo().search([["code","=",idioma]])
decimal = res_lang.decimal_point
#print(decimal)
return(decimal)
#@api.depends('buscador_cheque','pcw_chq_terceros')
@api.onchange('buscador_cheque','pcw_chq_terceros')
def _compute_cheques_terceros(self):
for record in self:
if record.buscador_cheque.id == False:
return(None)
origen = self._origin
#rec = self.env["hgt.pago_multiple_avanzado"].browse(origen)
ids = record.pcw_chq_terceros.ids
ids.append(record.buscador_cheque.id)
record.buscador_cheque = False
record.pcw_chq_terceros = ids
#print(record, record)
def AgregarChequesMultiples(self):
self.multicheques = True
#self.comprobantes = context.get('active_ids')
#print(self)
return {
'context': self.env.context,
'view_type': 'form',
'view_mode': 'form',
'res_model': 'hgt.pago_multiple',
'res_id': self.id,
'view_id': False,
'type': 'ir.actions.act_window',
'target': 'new',
}
def LimpiarChequesTerceros(self):
cheques = self.pcw_chq_terceros.ids
cheques.pop
self.pcw_chq_terceros = False
self.env.cr.commit()
def FijarNota(self):
"""esta funcion debe ser llamada antes de realizar la gestion de recibo"""
self.nota_fija = self.calcular_nota()
@api.multi
def write(self, values):
name = f"ERO {str(self.id).zfill(5)}"#Genero el codigo del pago
values["name"] = name
return super(hgt_cobro_multiple, self).write(values)
def cancelar(self):
if self.pcw_recibo.comp_estado in ["a","p"]:
self.pcw_recibo.cancelar()
self.estado = "cancelado"
def Ayuda(self):
return {
'type': 'ir.actions.act_url',
'url': f"""https://hgt.hgt.com.ar/markdown_read?id=22&tipo=publicado""",
'target': 'new',
}
\ No newline at end of file \ No newline at end of file
# -*- coding: utf-8 -*-
###############################################################################
# License, author and contributors information in: #
# __manifest__.py file at the root folder of this module. #
###############################################################################
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError, Warning
import datetime
class hgt_cobro_multiple(models.Model):
_name = 'hgt.relaciones_talonarios'
_description = 'Relaciones de cancelacion en talonarios'
_order = "id desc"
talonario_origen = fields.Many2one('asw.talonario',
string='Talonario Origen',)
talonario_debito = fields.Many2one('asw.talonario',
string='Talonario Nota Debito',)
talonario_credito = fields.Many2one('asw.talonario',
string='Talonario Nota Credito',)
habilitado = fields.Boolean(string='Activo', default=False)
def Credito(self,origen_id):
rta = self.BuscarRelacion(origen_id)
return(rta)
def Dedito(self,origen_id):
rta = self.BuscarRelacion(origen_id,talonario="debito")
return(rta)
def BuscarRelacion(self,origen_id,talonario="credito"):
rta = self.sudo().search([["habilitado","=",True],
["talonario_origen","=",origen_id]
], limit=1)
if len(rta) == 0:
rta = False
else:
sb = {"credito":rta.talonario_credito,
"debito":rta.talonario_debito}
return(sb[talonario])
return(rta)
\ No newline at end of file \ No newline at end of file
# -*- coding: utf-8 -*-
###############################################################################
# License, author and contributors information in: #
# __manifest__.py file at the root folder of this module. #
###############################################################################
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
class asw_valores(models.Model):
_inherit = 'asw.valores'
val_estado_cheque = fields.Selection(
string='Estado cheque',
selection=[
('b', 'Borrador'),
('en_mano', 'En mano'),
('reservado', 'Reservado'),
('entregado', 'Entregado'),
('depositado', 'Depositado'),
('devuelto', 'Devuelto'),
('cambiado', 'Cambiado'),
('r', 'Rechazado')
],
related='val_cheque.che_estado',
store=True
)
\ No newline at end of file \ No newline at end of file
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_asw_referencias,access_asw_referencias,model_asw_referencias,asw_tpv.module_category_asw_falange_usuario,1,1,1,0 access_asw_referencias,access_asw_referencias,model_asw_referencias,asw_tpv.module_category_asw_falange_usuario,1,1,1,0
access_hgt_refcompra,access_hgt_refcompra,model_hgt_refcompra,asw_tpv.module_category_asw_falange_usuario,1,1,1,0 access_hgt_refcompra,access_hgt_refcompra,model_hgt_refcompra,asw_tpv.module_category_asw_falange_usuario,1,1,1,0
access_hgt_mobil_gastos_config,access_hgt_mobil_gastos_config,model_hgt_mobil_gastos_config,,1,1,1,0
\ No newline at end of file \ No newline at end of file
access_hgt_mobil_gastos_config,access_hgt_mobil_gastos_config,model_hgt_mobil_gastos_config,,1,1,1,0
access_hgt_pago_multiple_avanzado,access_hgt_pago_multiple_avanzado,model_hgt_pago_multiple_avanzado,asw_tpv.module_category_asw_falange_usuario,1,1,1,0
access_hgt_relaciones_talonarios,access_hgt_relaciones_talonarios,model_hgt_relaciones_talonarios,,1,0,0,0
access_hgt_relaciones_talonarios_admin,access_hgt_relaciones_talonarios_admin,model_hgt_relaciones_talonarios,asw_tpv.module_category_asw_falange_administrador,1,1,1,1
\ No newline at end of file \ No newline at end of file
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<xpath expr="//button[@name='validar']" position="before"> <xpath expr="//button[@name='validar']" position="before">
<button name="abrir_wizard_ppf" class='btn btn-success' string="+ Producto por precio final" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/> <button name="abrir_wizard_ppf" class='btn btn-success' string="+ Producto por precio final" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/>
<button name="recalcularTotal" class='btn btn-warning' string="Recalcular Monto" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/> <button name="recalcularTotal" class='btn btn-warning' string="Recalcular Monto" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/>
<button name="generarNota" class='btn btn-success' string="Generar Nota" type="object" attrs="{'invisible':[('comp_estado','in',['b','c','r'])]}"/> <button name="generarNota" class='btn btn-success' string="Generar Nota" type="object" attrs="{'invisible':[('comp_estado','in',['b','c'])]}"/>
</xpath> </xpath>
<xpath expr="//group[1]" position="after"> <xpath expr="//group[1]" position="after">
<group string="Referencias" col="2"> <group string="Referencias" col="2">
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<button name="recalcularTotal" class='btn btn-warning' string="Recalcular Monto" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/> <button name="recalcularTotal" class='btn btn-warning' string="Recalcular Monto" type="object" attrs="{'invisible':[('comp_estado','!=','b')]}"/>
<button name="impresion_directa" class='btn btn-info' string="Impresion directa" type="object" attrs="{'invisible':[('comp_estado','=','b')]}"/> <button name="impresion_directa" class='btn btn-info' string="Impresion directa" type="object" attrs="{'invisible':[('comp_estado','=','b')]}"/>
<button name="abrir_wizard_cn" type="object" class="btn btn-info" string="Corregir núm. AFIP"/>> <button name="abrir_wizard_cn" type="object" class="btn btn-info" string="Corregir núm. AFIP"/>>
<button name="generarNota" class='btn btn-success' string="Generar Nota" type="object" attrs="{'invisible':[('comp_estado','in',['b','c','r'])]}"/> <button name="generarNota" class='btn btn-success' string="Generar Nota" type="object" attrs="{'invisible':[('comp_estado','in',['b','c'])]}"/>
</xpath> </xpath>
</data> </data>
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
<data> <data>
<menuitem name="Referencias" sequence="90" id="asw_tpv_compras_referencias" parent="asw_tpv.asw_tpv_compras"/> <menuitem name="Referencias" sequence="90" id="asw_tpv_compras_referencias" parent="asw_tpv.asw_tpv_compras"/>
<menuitem name="Referencias de Compras" id="asw_tpv_compras_referencias_refcompras" parent="asw_tpv_compras_referencias" action="hgt_refcompra_list_action"/> <menuitem name="Referencias de Compras" id="asw_tpv_compras_referencias_refcompras" parent="asw_tpv_compras_referencias" action="hgt_refcompra_list_action"/>
<!-- <menuitem name="Configuracion Mobil" sequence="99" id="asw_tpv_gasto_mobil_config" parent="asw_tpv.asw_tpv_compras_gastos" action="hgt_gastos_mobil_list_action" groups="hgt_dispositivos.group_administrador_dispositivos"/> --> <menuitem name="Pagos avanzados" sequence="70" id="hgt_pago_proveedor_avanzado_menu" parent="asw_tpv.asw_tpv_compras_compras" action="hgt_pago_proveedor_avanzado_accion"/>/>
<!-- <menuitem name="Configuracion Mobil" sequence="99" id="asw_tpv_gasto_mobil_config" parent="asw_tpv.asw_tpv_compras_gastos" action="hgt_gastos_mobil_list_action" groups="hgt_dispositivos.group_administrador_dispositivos"/> -->
<menuitem sequence='55' name="Talonario cancelacion" id="hgt_tpv_fcamcelcacionm_talonario" parent="asw_tpv.asw_tpv_configuracion_gestion" action="hgt_relaciones_talonarios_list_action"/>
</data> </data>
</odoo> </odoo>
<odoo>
<data>
<record id="hgt_pago_proveedor_avanzado_form" model="ir.ui.view">
<field name="name">Pago a proveedor avanzado</field>
<field name="model">hgt.pago_multiple_avanzado</field>
<field name="arch" type="xml">
<form>
<header>
<field name="estado" widget="statusbar" />
</header>
<br></br>
<h4>Nota 1: Si el monto a pagar no llega a cubrir totalmente el saldo de las facturas elegidas, saldará completamente de la más vieja a la más nueva quedando la última con un saldo parcial.</h4>
<h4>Nota 2: Si el pago excede a las facturas elegidas, el excedente se destinará automáticamente a saldar (total o parcialmente) a la factura con saldos pendientes más antigua en existencia.</h4>
<h4>Nota 3: Si el pago excede a las facturas elegidas y no existen facturas con saldos pendientes, por fuera de las elegidas, el excedente quedará a favor de la empresa en la cuenta corriente y podrá ser imputado a la primer factura nueva de forma automática.</h4>
<field name="pcw_cli_razon_social" invisible='1'/>
<group col="2">
<group col="2" >
<field name="pcw_cliente" colspan='2' domain="[('cli_es_cliente','=', True )]"/>
<field name="pcw_referencia" />
<field name="pcw_efectivo" colspan='2'/>
<field name="caja_efectivo"/>
<field name="pcw_recibo" readonly="1"/>
</group>
<group string="Detalles" >
<field name="nota" nolabel="1"/>
</group>
</group>
<group col="4">
<!-- <field name="pcw_retencion_recibida" colspan="2"/>-->
<notebook colspan="4">
<page string="Cheques Propios">
<group col="4">
<field name="pcw_chq_emitidos" colspan='4' nolabel='1' widget='one2many'
context="{'default_val_razon_social' : pcw_cli_razon_social}">
<tree create="1" delete="1" edit="1" editable="bottom">
<field name="che_banco"/>
<field name="che_nro_cheque"/>
<field name="che_fecha_emision" required='1'/>
<field name="che_fecha_cobro" required='1'/>
<field name="che_fecha_vencimiento" required='1'/>
<field name="che_monto"/>
</tree>
</field>
</group>
</page>
<page string="Cheques Terceros">
<group col="4">
<field name="buscador_cheque" colspan='4' domain="[('che_estado', '=','en_mano')]" />
<field name="pcw_chq_terceros" colspan='4' nolabel='1'
options="{'create_edit': True, 'readonly': False}" />
<button name="LimpiarChequesTerceros" class="btn btn-danger" string="Liberar Cheques" type="object" />
</group>
</page>
<page string="Transferencias Emitidas">
<group col="4">
<field name="pcw_transferencias_emitidas" colspan='4' nolabel='1' widget='one2many' >
<tree create="1" delete="1" edit="1" editable="bottom">
<field name="val_cuenta_bancaria"/>
<field name="val_fecha_emision"/>
<field name="valor_caja_id"/>
<field name="val_nro_pago"/>
<field name="val_monto" required='1'/>
</tree>
</field>
</group>
</page>
<page string="Pagado con tarjeta">
<group col="4">
<field name="pcw_tarjetas" colspan='4' nolabel='1' widget='one2many' >
<tree create="1" delete="1" edit="1" editable="bottom">
<field name="val_tipo" domain="[('tv_tipo','=', 'ta' )]"
string='Tarjeta' options="{'no_create': True, 'no_create_edit':True}"/>
<field name="val_monto" required='1'/>
</tree>
</field>
</group>
</page>
<page string="Retenciones Emitidas">
<group col="4">
<field name="pcw_retenciones" colspan='4' nolabel='1' widget='one2many' >
<tree create="1" delete="1" edit="1" editable="bottom">
<field name="val_tipo" domain="[('tv_tipo','=', 're' )]"
string='Retencion' options="{'no_create': True, 'no_create_edit':True}"/>
<field name="val_nro_pago"/>
<field name="val_monto" required='1'/>
</tree>
</field>
</group>
</page>
<page string="Facturas">
<group col="4">
<field name="comprobantes" colspan='4' readonly="1" nolabel="1" >
</field>
</group>
</page>
</notebook>
<br/>
</group>
<group id='total' col='6'>
<field name="pcw_total" colspan='6'/>
<field name="pcw_total_pagar" colspan='6'/>
<field name="pcw_total_balance" colspan='6'/>
</group>
<h4>Nota 4: La diferencia si es negativa implica que se esta abonando más que lo adeudado en las facturas seleccionadas impactando un saldo a favor de la empresa en la cta cte del proveedor, de existir facturas pendientes no seleccionadas dicho saldo se aplicará a la más vieja</h4>
<footer>
<button name="generar_recibo" string="Generar Recibo" type="object" class="btn btn-success"
attrs="{ 'invisible':[('estado', '!=', 'borrador')]}"/>
<!--<button special="cancel" string="Cancel" attrs="{ 'invisible':[('estado', '=', 'cancelado')]}"/>-->
<button name="cancelar" class="btn btn-danger" string="Cancelar operacion" type="object"
attrs="{ 'invisible':[('estado', '=', 'cancelado')]}"/>
<button name="Ayuda" class="oe_right oe_button_box" icon='fa-exclamation-circle' string="Ayuda" type="object" />
</footer>
</form>
</field>
</record>
<record model="ir.ui.view" id="hgt_pago_proveedor_avanzado_tree">
<field name="name">Pagos a proveedores avanzados</field>
<field name="model">hgt.pago_multiple_avanzado</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="pcw_cliente"/>
<field name="estado"/>
</tree>
</field>
</record>
<!-- accion con reglas de acceso -->
<record model="ir.actions.act_window" id="hgt_pago_proveedor_avanzado_accion">
<field name="name">Pagos a proveedores avanzados</field>
<field name="res_model">hgt.pago_multiple_avanzado</field>
<field name="view_mode">tree,form</field>
</record>
<!--<act_window id="hgt_pago_masivo_avanzado_wizzard_action" name="Pago masivo Avanzado" src_model="asw.comprobante"
res_model="hgt.pago_multiple" view_mode="form" target="new" multi="True"/>-->
</data>
</odoo>
\ No newline at end of file \ No newline at end of file
<odoo>
<data>
<record model="ir.actions.act_window" id="hgt_relaciones_talonarios_list_action">
<field name="name">Relaciones de cancelacion de facturas</field>
<field name="res_model">hgt.relaciones_talonarios</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Configure las cancelaciones de facturas</p>
</field>
</record>
<record id="view_tree_hgt_relaciones_talonarios_ui" model="ir.ui.view">
<field name="name">Relaciones de cancelacion de facturas</field>
<field name="model">hgt.relaciones_talonarios</field>
<field name="arch" type="xml">
<tree>
<field name="talonario_origen"/>
<field name="talonario_credito"/>
<field name="talonario_debito"/>
<field name="habilitado" />
</tree>
</field>
</record>
<record id="view_form_hgt_relaciones_talonarios_ui" model="ir.ui.view">
<field name="name">Relaciones de cancelacion de facturas</field>
<field name="model">hgt.relaciones_talonarios</field>
<field name="arch" type="xml">
<form>
<sheet>
<group col='4'>
<field name="habilitado" colspan="2"/>
</group>
<group col='2' string="Talonarios a relacionar">
<field name="talonario_origen"/>
<field name="talonario_credito"/>
<field name="talonario_debito"/>
</group>
</sheet>
</form>
</field>
</record>
</data>
</odoo>
\ No newline at end of file \ No newline at end of file
...@@ -2,4 +2,5 @@ from . import prod_pfinal ...@@ -2,4 +2,5 @@ from . import prod_pfinal
from . import corregir_nro_tal from . import corregir_nro_tal
from . import generar_nota from . import generar_nota
from . import cobro_discriminado from . import cobro_discriminado
from . import pago_discriminado
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError
class asw_corrector_numtal(models.TransientModel):
_name = 'hgt.agregar_cheque_terceroasis'
_description = "Agregarcheque de tercero"
pcw_chq_terceros = fields.Many2many(
comodel_name='asw.cheque',
relation='pago_discriminado_chq_terceros', #ya lo edite
column1='asw_cheque_id',
column2='asw_cliente_id',
string='Cheques Propios',
domain=[],
readonly=False
)
\ No newline at end of file \ No newline at end of file
File mode changed
...@@ -13,11 +13,16 @@ class tpv_generar_nota(models.TransientModel): ...@@ -13,11 +13,16 @@ class tpv_generar_nota(models.TransientModel):
comodel_name='asw.comprobante', comodel_name='asw.comprobante',
) )
def iva_21(self):
return(self.env["asw.impuesto"].search([["name","=","21%"]], limit=1).id)
def generar_notaCred(self): def generar_notaCred(self):
comprobante = self.gn_comprobante comprobante = self.gn_comprobante
referencia = 'Reintegro del comprobante : ' + comprobante.comp_talonario.tal_codigo + "/" +comprobante.comp_nro_letras referencia = 'Reintegro del comprobante : ' + comprobante.comp_talonario.tal_codigo + "/" +comprobante.comp_nro_letras
talonario = self.env['asw.talonario'].search([('tal_pto_vta','=',comprobante.comp_talonario.tal_pto_vta),('tal_letra','=', comprobante.comp_talonario.tal_letra),('tal_codigo','in',['NCAE','NCBE'])])#es variable mover a preferecias talonario = self.env["hgt.relaciones_talonarios"].Credito(comprobante.comp_talonario.id)
if talonario == False:
talonario = self.env['asw.talonario'].search([('tal_pto_vta','=',comprobante.comp_talonario.tal_pto_vta),('tal_letra','=', comprobante.comp_talonario.tal_letra),('tal_codigo','in',['NCAE','NCBE'])])#es variable mover a preferecias
if talonario: if talonario:
contra_comprobante = self.generar_comprobante( contra_comprobante = self.generar_comprobante(
...@@ -47,11 +52,13 @@ class tpv_generar_nota(models.TransientModel): ...@@ -47,11 +52,13 @@ class tpv_generar_nota(models.TransientModel):
linea_comp = self.env['asw.linea_comprobante'].create({ linea_comp = self.env['asw.linea_comprobante'].create({
'lcp_producto': inter_prod.id, 'lcp_producto': inter_prod.id,
'lcp_comprobante': contra_comprobante.id, 'lcp_comprobante': contra_comprobante.id,
'lcp_precio': comprobante.comp_total, 'lcp_precio': comprobante.comp_total / (121/100),
'lcp_importe': comprobante.comp_total, 'lcp_importe': comprobante.comp_total / (121/100),
'lcp_iva': self.iva_21(),
}) })
linea_comp._onchange_precio_venta()
#contra_comprobante.validar() contra_comprobante.recalcularTotal()
#contra_comprobante._onchange_precio_venta()
#se agrega al recibo original #se agrega al recibo original
#comprobante.write({ #comprobante.write({
...@@ -65,7 +72,7 @@ class tpv_generar_nota(models.TransientModel): ...@@ -65,7 +72,7 @@ class tpv_generar_nota(models.TransientModel):
'view_mode': 'form', 'view_mode': 'form',
'view_type': 'form', 'view_type': 'form',
'res_id': contra_comprobante.id, 'res_id': contra_comprobante.id,
'target': 'new', 'target': 'self',
} }
else: else:
raise UserError('No existe talonario para la Nota de Crédito correspondiente. Créelo o diríjase a soporte para que lo haga, y vuelva a este menú') raise UserError('No existe talonario para la Nota de Crédito correspondiente. Créelo o diríjase a soporte para que lo haga, y vuelva a este menú')
...@@ -74,7 +81,9 @@ class tpv_generar_nota(models.TransientModel): ...@@ -74,7 +81,9 @@ class tpv_generar_nota(models.TransientModel):
comprobante = self.gn_comprobante comprobante = self.gn_comprobante
referencia = 'Nota de débito del comprobante : ' + comprobante.comp_talonario.tal_codigo + "/" +comprobante.comp_nro_letras referencia = 'Nota de débito del comprobante : ' + comprobante.comp_talonario.tal_codigo + "/" +comprobante.comp_nro_letras
talonario = self.env['asw.talonario'].search([('tal_pto_vta','=',comprobante.comp_talonario.tal_pto_vta),('tal_letra','=', comprobante.comp_talonario.tal_letra),('tal_codigo','in',['NDAN','NDBN','NDAE', 'NDBE'])]) talonario = self.env["hgt.relaciones_talonarios"].Dedito(comprobante.comp_talonario.id)
if talonario == False:
talonario = self.env['asw.talonario'].search([('tal_pto_vta','=',comprobante.comp_talonario.tal_pto_vta),('tal_letra','=', comprobante.comp_talonario.tal_letra),('tal_codigo','in',['NDAN','NDBN','NDAE', 'NDBE'])])
if talonario: if talonario:
contra_comprobante = self.generar_comprobante( contra_comprobante = self.generar_comprobante(
...@@ -103,12 +112,12 @@ class tpv_generar_nota(models.TransientModel): ...@@ -103,12 +112,12 @@ class tpv_generar_nota(models.TransientModel):
linea_comp = self.env['asw.linea_comprobante'].create({ linea_comp = self.env['asw.linea_comprobante'].create({
'lcp_producto': inter_prod.id, 'lcp_producto': inter_prod.id,
'lcp_comprobante': contra_comprobante.id, 'lcp_comprobante': contra_comprobante.id,
'lcp_precio': comprobante.comp_total, 'lcp_precio': comprobante.comp_total / (121/100),
'lcp_importe': comprobante.comp_total, 'lcp_importe': comprobante.comp_total / (121/100),
}) })
#contra_comprobante.validar() linea_comp._onchange_precio_venta()
contra_comprobante.recalcularTotal()
return { return {
'name': "Nota de Débito", 'name': "Nota de Débito",
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
...@@ -116,7 +125,7 @@ class tpv_generar_nota(models.TransientModel): ...@@ -116,7 +125,7 @@ class tpv_generar_nota(models.TransientModel):
'view_mode': 'form', 'view_mode': 'form',
'view_type': 'form', 'view_type': 'form',
'res_id': contra_comprobante.id, 'res_id': contra_comprobante.id,
'target': 'new', 'target': 'self',
} }
else: else:
raise UserError('No existe talonario para la Nota de Débito correspondiente. Créelo o diríjase a soporte para que lo haga, y vuelva a este menú') raise UserError('No existe talonario para la Nota de Débito correspondiente. Créelo o diríjase a soporte para que lo haga, y vuelva a este menú')
# -*- coding: utf-8 -*-
###############################################################################
# License, author and contributors information in: #
# __manifest__.py file at the root folder of this module. #
###############################################################################
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError, Warning
import datetime
class hgt_cobro_multiple(models.TransientModel):
_name = 'hgt.pago_multiple'
_description = 'Pago multiple avanzado'
_inherit = ['asw.action.manager']
# Informacion pago
pcw_cliente = fields.Many2one(
comodel_name='asw.cliente',
string='Cliente',
readonly=True,
compute='_compute_cliente',
required=True
)
nota = fields.Text(string='nota',readonly=True)
comprobantes = fields.Many2many(comodel_name='asw.comprobante', string='Comprobantes')
###########################################Validaciones y calculos ######################################
@api.depends('comprobantes')
def _compute_cliente(self):
if not len(self.pcw_cliente) == 0:
return(None)
context = self.env.context
if len(self.comprobantes) == 0:
active_ids = context.get('active_ids')
facturas = self.env['asw.comprobante'].search([('id', 'in', active_ids)])
self.comprobantes = facturas.ids
else:
facturas = self.comprobantes
idc = []
Texto = "Facturas de proveedores a pagar:"
Total = 0
for factura in facturas:
if not factura.comp_cliente.id in idc:
idc.append(factura.comp_cliente.id)
if not factura.comp_adeudado > 0:
raise UserError(f"Factura {factura.display_name} ya esta pagada o valor nulo")
if factura.comp_estado in ["b","c"]:
raise UserError(f"Factura {factura.display_name} en estado invalido para pagar")
if (factura.comp_talonario.tal_tipo == "e"):
raise UserError(f"El comprobante {factura.display_name} es de venta")
Total = Total + factura.comp_adeudado
Texto = f"{Texto}\n{factura.display_name} {factura.comp_adeudado}"
if not len(idc) == 1:
raise UserError("Solo se puede cobrar a un cliente a la vez, cierre la ventana y seleccione de vuelta los comprobantes a operar")
Texto = f"{Texto}\nTotal {Total}".replace(".",self.PuntoDecimal())
self.pcw_cliente = idc[0]
self.nota = Texto
############################################Asistente##################################################################
def PuntoDecimal(self):
"""devuelve si es coma o punto segun la configuracion del sistema"""
idioma = self.env.user.lang
res_lang = self.env["res.lang"].sudo().search([["code","=",idioma]])
decimal = res_lang.decimal_point
#print(decimal)
return(decimal)
def AgregarChequesMultiples(self):
#print(self.comprobantes, self.pcw_cliente)
val = {"pcw_cliente":self.pcw_cliente,
"comprobantes": self.comprobantes.ids}
pg = self.env["hgt.pago_multiple_avanzado"].create(val)
pg.comprobantes = self.comprobantes.ids
#print(pg)
return {
'context': self.env.context,
'view_type': 'form',
'view_mode': 'form',
'res_model': 'hgt.pago_multiple_avanzado',
'res_id': pg.id,
'view_id': False,
'type': 'ir.actions.act_window',
'target': 'self',
}
<odoo>
<data>
<record id="hgt_pago_proveedor_wizzard_form" model="ir.ui.view">
<field name="name">Pago a proveedor avanzado</field>
<field name="model">hgt.pago_multiple</field>
<field name="arch" type="xml">
<form>
<group col="2">
<group col="2" >
<field name="pcw_cliente" colspan='2' domain="[('cli_es_cliente','=', True )]"/>
</group>
<group string="Detalles" >
<field name="nota" nolabel="1"/>
</group>
</group>
<group string="Facturas a pagar" col="4">
<field name="comprobantes" colspan="4" nolabel="1"/>
</group>
<footer>
<button name="AgregarChequesMultiples" string="Continuar" type="object" class="oe_highlight btn-generar-recibo"/>
<button special="cancel" string="Cancelar accion"/>
</footer>
</form>
</field>
</record>
<act_window id="hgt_pago_masivo_avanzado_wizzard_action" name="Pago masivo Avanzado" src_model="asw.comprobante"
res_model="hgt.pago_multiple" view_mode="form" target="new" multi="True"/>
</data>
</odoo>
\ No newline at end of file \ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!