from openerp import models, fields, api, exceptions, _
from openerp.exceptions import ValidationError
import openerp.addons.grp_account.models.grp_aprobacion_pagos as grp_aprobacion_pagos
from lxml import etree
from openerp.tools import float_round
from openerp import SUPERUSER_ID
import logging

# TODO: SPRING 10 GAP 266 C

class grp_account_voucher(models.Model):
    _inherit = 'account.voucher'

    # TODO: SPRING 11 GAP 292 M
    @api.model
    def fields_view_get(self, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
        res = super(grp_account_voucher, self).fields_view_get(view_id=view_id, view_type=view_type,
                                                               context=context,
                                                               toolbar=toolbar, submenu=submenu)
        doc = etree.XML(res['arch'])
        if self._context.get('default_caja_recaudadora_id'):
            for node in doc.xpath("//field[@name='journal_id']"):
                node.set('domain', "[('id', '=', %s)]" % (self.env['grp.caja.recaudadora.tesoreria'].search(
                    [('id', '=', self._context.get('default_caja_recaudadora_id'))]).box_id.journal_id.id))
        elif self._context.get('opi'):
            operating_unit_ids = self.env.user.operating_unit_ids.ids
            operating_unit_ids.append(self.env.user.default_operating_unit_id.id)
            for node in doc.xpath("//field[@name='journal_id']"):
                node.set('domain', "[('type','in',['purchase'])]")
            for node in doc.xpath("//field[@name='operating_unit_id']"):
                node.set('domain', "[('id','in',%s)]" % (operating_unit_ids))

        elif self._context.get('invoice_type') and self._context.get('invoice_type') == 'out_invoice':
            for node in doc.xpath("//field[@name='journal_id']"):
                node.set('domain', "[('type','in',['bank'])]")
        res['arch'] = etree.tostring(doc)
        return res

    @api.one
    @api.depends('line_dr_ids.reconcile','line_dr_ids.move_line_id')
    def _compute_show_field_vc(self):
        if self.suspend_security().line_dr_ids.filtered(lambda x: x.reconcile and x.move_line_id.vc):
            self.show_field_vc = True
        else:
            self.show_field_vc = False

    payment_method = fields.Selection([('check', 'Cheque'), ('cash', 'Efectivo'), ('transfer', 'Transferencia')],
                                      string="Medio de pago", readonly=True,
                                      states={'draft': [('readonly', False)]})

    checkbook_id = fields.Many2one('grp.checkbook', string=u'Chequera', readonly=True,
                                   states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
    serial = fields.Char(string=u'Serie', related='checkbook_id.serial', readonly=True)
    check_id = fields.Many2one('grp.checkbook.line', u'Cheque', readonly=True,
                               states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
    state = fields.Selection([('draft', 'Borrador'),
                              ('confirm', 'Confirmado'),
                              ('issue', 'Emitido'),
                              ('proforma', 'Pro-forma'),
                              ('posted', 'Pago'),
                              ('cancel', 'Cancelado')
                              ])
    endorse = fields.Boolean('Endoso', default=False, readonly=True,
                             states={'issue': [('readonly', False)], 'posted': [('readonly', False)]})
    endorse_date = fields.Date('Fecha de endoso', readonly=True,
                               states={'issue': [('readonly', False)], 'posted': [('readonly', False)]})
    cancel_motive = fields.Char(string=u'Motivo de anulación', readonly=True, states={'cancel': [('readonly', False)]})
    show_motive = fields.Boolean(string=u'Mostrar motivo', default=False)

    caja_chica = fields.Boolean(string=u'Caja chica tesorería', related='journal_id.caja_chica_t', readonly=True)
    caja_recaudadora = fields.Boolean(string=u'Caja chica tesorería', related='journal_id.caja_recaudadora', readonly=True)
    no_operation = fields.Char(string=u'Nro. de Operación')
    serial = fields.Char(string=u'Serie', related='checkbook_id.serial', readonly=True)

    show_cancel_voucher = fields.Boolean(string=u'Mostrar botón', compute='_compute_show_cancel_voucher')
    show_change_voucher = fields.Boolean(string=u'Mostrar botón', compute='_compute_show_change_voucher')
    receipt_serial = fields.Char(string=u'Serie', size=10)  # TODO: SPRING 11 GAP 292 M
    # receipt_check = fields.Integer(string=u'Nº cheque')  # TODO: SPRING 11 GAP 292 M
    receipt_check = fields.Char(string=u'Nº cheque', size=20)
    check_date = fields.Date('Fecha cheque')  # TODO: M SPRING 11 GAP 292.A
    bank_id = fields.Many2one('res.bank', string=u'Banco')  # TODO: M SPRING 11 GAP 292.A
    # no_bank_account = fields.Integer(string=u'Nº cuenta bancaria')  # TODO: SPRING 11 GAP 292 M
    no_bank_account = fields.Char(string=u'Nº cuenta bancaria', size=20)
    # currency_id = fields.Many2one('res.currency', u'Divisa')  # TODO: SPRING 11 GAP 292 M
    check_amount = fields.Float(compute='_compute_check_amount', string=u'Monto cheque',
                                store=True)  # TODO: M SPRING 11 GAP 292.A

    caja_recaudadora_id = fields.Many2one('grp.caja.recaudadora.tesoreria',
                                          string=u'Caja recaudadora')  # TODO: SPRING 11 GAP 292 M
    # TODO: M INCIDENCIA 692
    payment_method_alert = fields.Boolean(u"Mostar alerta medio de pago", compute='_compute_payment_method_alert')
    show_warning = fields.Boolean(u"Mostar alerta", default=True)

    # TODO: SPRING 10 GAP 222 K
    pago_ret_sueldo = fields.Boolean(string=u'Pago retención de sueldo', default=False)
    no_recibo = fields.Char('Nro. Recibo', size=10)
    # RAGU alterando comportamiento
    in_cashbox = fields.Boolean(string=u'En caja', store=True)

    valor_custodia_id = fields.Many2one('grp.valores_custodia', string='Valor en custodia',
                                        readonly=True,
                                        states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
                                        domain="[('state','=','entrega_autorizada'),('tipo_id.efectivo','=',True),('readonly','=',False),('partner_id','=',partner_id)]")
    show_field_vc = fields.Boolean(string='Mostrar campo VC', compute='_compute_show_field_vc')
    nro_documento = fields.Char(string=u'Nro. Documento', compute='_compute_nro_documento', compute_sudo=True, store=False)

    @api.one
    @api.depends('line_ids')
    def _compute_nro_documento(self):
        if self.line_ids:
            if isinstance(self.id, (int, long)):
                # asegurar que no ejecute la función en las líneas
                query = """
                    SELECT string_agg(l.supplier_invoice_number::text,', ')
                    FROM account_voucher_line l
                        inner join account_voucher v on (l.voucher_id=v.id)
                    WHERE v.id=%s and l.amount>0 and l.supplier_invoice_number is not null
                """
                self._cr.execute(query, (self.id,))
                res = self._cr.fetchone()
                self.nro_documento = res and res[0] or False
            else:
                self.nro_documento = ', '.join(filter(None, self.line_ids.filtered(lambda x: x.amount > 0).mapped('supplier_invoice_number')))

    @api.onchange('show_field_vc')
    def _onchange_show_field_vc(self):
        if not self.show_field_vc and self.state == 'draft':
            self.valor_custodia_id = False

    # TODO: SPRING 10 GAP 222 K
    def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, amount,
                            currency_id, ttype, date, context=None):
        if context.get('pago_ret_sueldo', False):
            return self.onchange_pago_ret_sueldo(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date,
                                                 context.get('pago_ret_sueldo'), context=context)
        else:
            return super(grp_account_voucher, self).onchange_partner_id(
                cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype,
                date, context=context)

    # TODO: SPRING 10 GAP 222 K
    def onchange_pago_ret_sueldo(self, cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date,
                                 pago_ret_sueldo=False, context=None):
        if not journal_id:
            return {}
        if context is None:
            context = {}
        # TODO: comment me and use me directly in the sales/purchases views
        res = {'value': {}}
        if pago_ret_sueldo:
            if ttype in ['sale', 'purchase']:
                return res
            ctx = context.copy()
            # not passing the payment_rate currency and the payment_rate in the context but it's ok because they are reset in recompute_payment_rate
            ctx.update({'date': date})
            ctx.update({'pago_ret_sueldo': pago_ret_sueldo})
            vals = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date,
                                                context=ctx)
            vals2 = self.recompute_payment_rate(cr, uid, ids, vals, currency_id, date, ttype, journal_id, amount,
                                                context=context)
            for key in vals.keys():
                res[key].update(vals[key])
            for key in vals2.keys():
                res[key].update(vals2[key])
            if ttype == 'sale':
                del (res['value']['line_dr_ids'])
                del (res['value']['pre_line'])
                del (res['value']['payment_rate'])
            elif ttype == 'purchase':
                del (res['value']['line_cr_ids'])
                del (res['value']['pre_line'])
                del (res['value']['payment_rate'])
        else:
            res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date,
                                           context)
        return res

    # RAGU limpiando chequeras
    def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id,
                         context=None):
        to_return = super(grp_account_voucher, self).onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id,
                                                                      partner_id, date, amount, ttype, company_id,
                                                                      context=context)
        if context.get('default_opi') and isinstance(to_return,dict) and 'operating_unit_id' in to_return.get('value',{}):
            journal_currency_id = self.pool.get('account.journal').browse(cr,uid,journal_id,context=context).currency
            company_currency_id = self.pool.get('res.company').browse(cr,SUPERUSER_ID,company_id,context=context).currency_id
            if journal_currency_id and journal_currency_id.id != company_currency_id.id:
                to_return['value']['account_id'] = self.pool.get('res.partner').browse(cr,uid,partner_id,context=context).cuenta_a_pagar_me.id
            del to_return['value']['operating_unit_id']
        if to_return:
            to_return['value'].update({
                'checkbook_id': False,
                'check_id': False
            })
        return to_return

    # TODO: SPRING 10 GAP 222 K
    def _filter_voucher_lines_pr(self, line_ids, partner_id):
        line_retenciones_manuales = self.env['grp.lineas.retenciones.manuales']

        lines_filtered = [(5,)]
        line_retenciones_manuales_ids = line_retenciones_manuales.search(
            [('empresa','=',partner_id), ('retencion_manual_id.state', '=', 'aprobacion_pago'), ('pagado', '=', False),
             ('cheque_emitido', '=', False),
             ('resumen_id.cancelado', '=', False), ('resumen_id.move_id', '!=', False)])
        if line_retenciones_manuales_ids:
            move_line_id_rm = [x.id for x in
                               line_retenciones_manuales_ids.mapped('resumen_id').mapped('move_id').mapped(
                                   lambda x: x.line_id)]
            for line in line_ids:
                if isinstance(line, dict) and line['move_line_id'] in move_line_id_rm:
                    lines_filtered.append(line)
        return lines_filtered

    # TODO: SPRING 10 GAP 222 K
    def _filter_voucher_lines_not_pr(self, line_ids, ttype, partner_id):
        line_retenciones_manuales = self.env['grp.lineas.retenciones.manuales.resumen']

        lines_filtered = [(5,)]
        resumenes = line_retenciones_manuales.search([('empresa','=',partner_id), ('move_id', '!=', False)])
        if resumenes:
            move_line_id_rm = [x.id for x in resumenes.mapped('move_id').mapped(lambda x: x.line_id)]
            new_lines_filtered = (list(filter(lambda x: isinstance(x, dict) and x['move_line_id'] not in move_line_id_rm, line_ids)))
            # lines_filtered.extend(list(filter(lambda x: isinstance(x, dict) and x['move_line_id'] not in move_line_id_rm, line_ids)))
        else:
            new_lines_filtered = line_ids

        if ttype == 'receipt':
            # APUNTES YA INCLUIDOS EN DEVOLUCIONES EN CAJA
            move_line_ids = self.env['account.voucher'].search([('in_cashbox', '=', True), ('state', '=', 'pagado')]).mapped('move_ids').ids
            lines_filtered.extend(list(filter(lambda x: isinstance(x, dict) and x['move_line_id'] not in move_line_ids, new_lines_filtered)))
        else:
            lines_filtered = new_lines_filtered

        return lines_filtered

    # RAGU filtrando lineas en caso de caja recaudadora
    # def _filter_voucher_lines_cajarecaudadora(self, cajarecaudadora_id, line_ids):
    #     move_line_obj = self.env['account.move.line']
    #     #MVARELA: Se buscan todas las operating unit ya que se pueden pagar de otras
    #     operating_units = self.env['grp.caja.recaudadora.tesoreria'].sudo().search([('id','=',cajarecaudadora_id)], limit=1).box_id.operating_unit_ids.ids
    #     lines_filtered = [(5,)]
    #     for line in line_ids:
    #         if isinstance(line, dict) and move_line_obj.search([('id', '=', line['move_line_id'])], limit=1).operating_unit_id.id in operating_units:
    #             lines_filtered.append(line)
    #     return lines_filtered

    def _filter_voucher_lines_cajarecaudadora(self, cajarecaudadora_id, line_ids):
        move_line_obj = self.env['account.move.line']
        voucher_obj = self.env['account.voucher']
        operating_units = self.env['grp.caja.recaudadora.tesoreria'].sudo().search([('id','=',cajarecaudadora_id)], limit=1).box_id.operating_unit_ids.ids
        lines_filtered = [(5,)]
        origin_vouchers = []
        for line in line_ids:
            if isinstance(line, dict) and line['origin_voucher_id']:
                origin_vouchers.append(line['origin_voucher_id'])
        origin_voucher_ids = voucher_obj.search([('id','in',origin_vouchers)])
        for line in list(filter(lambda x: isinstance(x, dict), line_ids)):
            origin_voucher_id = origin_voucher_ids.filtered(lambda x: x.id == line['origin_voucher_id'])
            move_line = move_line_obj.suspend_security().search([('id', '=', line['move_line_id'])], limit=1)
            if move_line.operating_unit_id.id in operating_units:
                if self.env.user.company_id.box_refund_in_selection == 'out_box' and not origin_voucher_id.rendicion_viaticos_id and not origin_voucher_id.rendicion_anticipos_id:
                    lines_filtered.append(line)
                elif self.env.user.company_id.box_refund_in_selection == 'in_box':
                    lines_filtered.append(line)
        return lines_filtered

    # TODO: SPRING 10 GAP 222 K
    def onchange_line_ids_pr(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount, voucher_currency, type,
                             pago_ret_sueldo, no_recibo, context=None):
        res = super(grp_account_voucher, self).onchange_line_ids(cr, uid, ids, line_dr_ids, line_cr_ids, amount,
                                                                 voucher_currency, type, context=context)
        move_line_pool = self.pool.get('account.move.line')
        line_retenciones_manuales = self.pool.get('grp.lineas.retenciones.manuales')

        if pago_ret_sueldo and (no_recibo == '' or not no_recibo) and len(line_dr_ids) > 0:
            no_recibo = False
            flag_no_recibo_dif = False
            for line_dr_id in line_dr_ids:
                if len(line_dr_id) < 3:
                    continue
                if line_dr_id[0] == 0 and line_dr_id[2] and len(line_dr_id[2]) > 0 and line_dr_id[2]['move_line_id'] and \
                        line_dr_id[2]['amount']:
                    line = move_line_pool.browse(cr, uid, line_dr_id[2]['move_line_id'], context=context)
                    line_retenciones_manuales_ids = line_retenciones_manuales.search(cr, uid, [
                        ('retencion_manual_id.state', '=', 'aprobacion_pago'),
                        ('resumen_id.move_id.id', '=', line.move_id.id),
                        ('resumen_id.cancelado', '=', False), ('importe', '=', line.credit or line.debit or 0.0),
                        ('operating_unit_id.id', '=', line.move_id.operating_unit_id.id)], context=context)
                    for line_retenciones_manuales_value in line_retenciones_manuales.browse(cr, uid,
                                                                                line_retenciones_manuales_ids,
                                                                                context=context):
                        if not line_retenciones_manuales_value.resumen_id.pagado and not line_retenciones_manuales_value.resumen_id.cheque_emitido and line_retenciones_manuales_value.resumen_id.no_recibo != '' and line_retenciones_manuales_value.resumen_id.no_recibo != False:
                            if not no_recibo:
                                no_recibo = line_retenciones_manuales_value.resumen_id.no_recibo
                            elif no_recibo != line_retenciones_manuales_value.resumen_id.no_recibo:
                                flag_no_recibo_dif = True
            if no_recibo and not flag_no_recibo_dif:
                res['value']['no_recibo'] = no_recibo
        return res

    @api.multi
    def _compute_show_cancel_voucher(self):
        for rec in self:
            _show_cancel_voucher = True
            if rec.payment_method == 'cash':
                caja_id = self.env['grp.caja.chica.tesoreria.line'].search([('voucher_id', '=', rec.id)], limit=1)
                if not (not caja_id or (caja_id and caja_id.state_caja in ['end', 'check'])):
                    _show_cancel_voucher = False
            elif rec.payment_method == 'check':
                caja_id = self.env['grp.caja.recaudadora.tesoreria.line'].search([('voucher_id', '=', rec.id)], limit=1)
                if not (not caja_id or (caja_id and caja_id.caja_recaudadora_id.state in ['close', 'checked'])):
                    _show_cancel_voucher = False
                caja_id = self.env['grp.caja.pagadora.tesoreria.line'].search([('voucher_id', '=', rec.id)], limit=1)
                if not (not caja_id or (caja_id and caja_id.payment_box_id.state in ['close', 'checked'])):
                    _show_cancel_voucher = False
            rec.show_cancel_voucher = _show_cancel_voucher

    @api.multi
    def _compute_show_change_voucher(self):
        for rec in self:
            _show_change_voucher = False
            if rec.payment_method == 'check':
                rec.show_change_voucher = False
                if not rec.check_id.in_cashbox:
                    _show_change_voucher = True
                else:
                    caja = self.env['grp.caja.pagadora.tesoreria.line'].search([('voucher_id', '=', rec.id)], limit=1)
                    _show_change_voucher = caja and caja.payment_box_id.state == 'open' or False
            rec.show_change_voucher = _show_change_voucher

    @api.multi
    def action_move_line_create(self):
        res = super(grp_account_voucher, self).action_move_line_create()
        for voucher in self:
            if voucher.state == 'posted' and voucher.valor_custodia_id:
                voucher.valor_custodia_id.write({ 'state': 'entrega_tesoreria', 'fecha_baja': fields.Date.today(),
                                                  'asiento_baja': voucher.move_id.id, 'diario_baja': voucher.move_id.journal_id.id })

        #     RAGU cambiado ref a asientos
            if voucher.caja_recaudadora_id:
                move_ids = self.env['account.move']
                move_ids += voucher.move_id
                move_ids += voucher.line_ids.filtered(lambda x: x.amount != 0).mapped('invoice_id.move_id')
                move_ids.write({'ref':voucher.caja_recaudadora_id.box_id.name_get()[0][1]})
        return res

    @api.onchange('checkbook_id')
    def onchange_checkbook_id(self):
        checks = self.checkbook_id.check_ids.filtered(lambda x: x.state == 'available' and not x.cancel_use).ids
        self.check_id = checks and checks[0] or False

    @api.onchange('caja_chica')
    def onchange_caja_chica(self):
        self.payment_method = False
        if self.caja_chica:
            self.payment_method = 'cash'

    @api.onchange('payment_method')
    def onchange_payment_method(self):
        if self.payment_method != 'check':
            self.check_id = False
            self.checkbook_id = False

    @api.onchange('no_operation')
    def onchange_no_operation(self):
        self.reference = self.no_operation

    def _check_values_dict(self, values):
        if self._context.get('opi') or self.opi:
            return True
        elif (self._context.get('default_caja_recaudadora_id') or self._context.get('caja_recaudadora_id')) and values.get('payment_method') == 'transfer':
            raise ValidationError(
                _("Un pago asociado a una Caja recaudadora no puede ser de tipo 'Transferencia'!"))
        elif self._context.get('invoice_type') and self._context.get('invoice_type') == 'out_invoice' and values.get('payment_method') != 'transfer':
            raise ValidationError(_("Un pago asociado a una Factura de cliente debe ser de tipo 'Transferencia'!"))
        return True

    @api.model
    def create(self, vals):
        if vals.get('check_id'):
            vals['reference'] = vals['check_id']
        res = super(grp_account_voucher, self).create(vals)
        if vals.get('valor_custodia_id', False):
            res.valor_custodia_id.write({'readonly': True})
        return res

    @api.multi
    def write(self, values):
        if values.get('check_id'):
            values['reference'] = values['check_id']
        if 'endorse_date' in values and values['endorse_date']:
            self.mapped('check_id').write({'check_date': values['endorse_date']})
        if values.get('state',False):
            self.change_state_caja_pagadora(values.get('state'))
        res = super(grp_account_voucher, self).write(values)
        if values.get('valor_custodia_id', False):
            for rec in self:
                rec.valor_custodia_id.write({'readonly': True})
        return res

    @api.multi
    def unlink(self):
        self.mapped(lambda x: x.valor_custodia_id).write({'readonly': False})
        super(grp_account_voucher, self).unlink()

    def change_state_caja_pagadora(self, state):
        for rec in self:
            caja_id = self.env['grp.caja.pagadora.tesoreria.line'].search([('voucher_id', '=', rec.id),('payment_box_id.state','not in', ['close', 'checked'])])
            if caja_id:
                caja_id.write({'state':state})

    # TODO: SPRING 10 GAP 222 K
    @api.multi
    def actualizar_retenciones_manuales(self, vals):
        line_retenciones_manuales = self.env['grp.lineas.retenciones.manuales']
        for voucher in self:
            if voucher.pago_ret_sueldo:
                for line in voucher.line_dr_ids:
                    if line.amount > 0:
                        line_retenciones_manuales_ids = line_retenciones_manuales.search(
                            [('retencion_manual_id.state', '=', 'aprobacion_pago'),
                             ('resumen_id.move_id.id', '=', line.move_line_id.move_id.id),
                             ('importe', '=', line.move_line_id.credit or line.move_line_id.debit or 0.0)])
                        for line_retenciones_manuales_value in line_retenciones_manuales_ids:
                            if vals.has_key('pagado'):
                                if vals.get('pagado', False):
                                    line_retenciones_manuales_value.write(vals)
                                    lines = line_retenciones_manuales.search(
                                        [('resumen_id', '=', line_retenciones_manuales_value.resumen_id.id),
                                         ('pagado', '!=', True)])
                                    if not lines:
                                        line_retenciones_manuales_value.resumen_id.write(vals)
                                else:
                                    line_retenciones_manuales_value.write(vals)
                                    # lines = line_retenciones_manuales.search(
                                    #     ['|', ('pagado', '=', True), ('cheque_emitido', '=', True),
                                    #      ('resumen_id', '=', line_retenciones_manuales_value.resumen_id.id)])
                                    # if lines:
                                    #     vals['cheque_emitido'] = True
                                    line_retenciones_manuales_value.resumen_id.retencion_manual_id.write({'state':'aprobacion_pago'})
                                    line_retenciones_manuales_value.resumen_id.write(vals)
                            else:
                                line_retenciones_manuales_value.write(vals)
                                line_retenciones_manuales_value.resumen_id.write(vals)
                                if line_retenciones_manuales_value.resumen_id.retencion_manual_id.state == 'cerrado':
                                    line_retenciones_manuales_value.resumen_id.retencion_manual_id.write(
                                        {'state': 'aprobacion_pago'})

    # RAGU validando monto al confirmar
    @api.multi
    def _check_valid_amount(self):
        for rec in self:
            if rec.amount == 0:
                raise ValidationError(_(u"No se puede procesar un Comprobante con 'Total' en 0!"))
            if rec.payment_option != u'with_writeoff' and float_round(abs(rec.amount), 2) != float_round(abs(rec.get_topay_amount()), 2):
                raise ValidationError(
                    _(u"El total no coincide con el monto a pagar. Debe actualizar el Pago antes de continuar!"))
            if rec.valor_custodia_id:
                line_vc_amount = sum([ line.amount for line in rec.line_dr_ids if line.reconcile and line.move_line_id.vc ])
                if float_round(rec.valor_custodia_id.monto, 2) != float_round(line_vc_amount, 2):
                    raise ValidationError('El monto de la OPI no coincide con el monto del valor en custodia, por favor verificar la OPI.')
        return True

    @api.multi
    def confirm_voucher(self):
        self._check_valid()
        self._check_valid_amount()
        # self.filtered(lambda x: x.check_id.exists()).mapped('check_id').write({'state': 'assign','amount': rec.amount, 'check_date': rec.fecha_inicio_pago,
        #                         'partner_id': rec.partner_id.id})
        self.write({'state': 'confirm'})
        # self.modificar_aprobacion_estado_pago('confirm')  # TODO: L SPRING 11 GAP 28
        # self.actualizar_retenciones_manuales({'cheque_emitido': True})

        for rec in self:
            rec.check_id.write({'state': 'assign', 'amount': rec.amount, 'check_date': rec.fecha_inicio_pago,
                                'partner_id': rec.partner_id.id})

    @api.multi
    def issue_voucher(self):
        for rec in self:
            rec.check_id.write({'state': 'issue', 'amount': rec.amount, 'check_date': rec.fecha_inicio_pago,
                                'partner_id': rec.partner_id.id})
        self.write({'state': 'issue'})

    def _update_check(self, action = False):
        if action == 'change_voucher':
            for rec in self:
                if rec.payment_method == 'check':
                    rec.check_id.state = 'available'
                    if rec.state == 'cancel' and rec.check_id.in_cashbox:
                        rec.check_id.in_cashbox = False
        return True

    @api.multi
    def change_voucher(self):
        self._update_check('change_voucher')
        self.create_workflow()
        self.actualizar_retenciones_manuales({'cancelado': False})
        self.write({'state': 'draft'})
        return True

    @api.multi
    def btn_anular(self):
        for rec in self:
            if rec.payment_method == 'check':
                rec.check_id.write({'state': 'cancel'})
        self.write({'show_motive': True})

    @api.multi
    def cancel_voucher(self):
        for rec in self:
            if rec.state == 'posted':
                invoice = rec.invoice_id
                # FACTURAS y FACTURAS DE FONDO ROTATORIO
                for line in rec.line_ids:
                    if line.invoice_id:
                        if line.invoice_id.fondo_rotarios:
                            raise ValidationError(_(u"No es posible cancelar la gestión de pagos, el/los documentos "
                                                u"asociados están incluidos en 3 en 1 para reposición."))
                    if line.origin_voucher_id.rendicion_anticipos_id:
                        if line.origin_voucher_id.rendicion_anticipos_id.fondo_rotarios:
                            raise ValidationError(_(u"No es posible cancelar la gestión de pagos, el/los documentos "
                                                    u"asociados están incluidos en 3 en 1 para reposición."))
                    elif line.origin_voucher_id.rendicion_viaticos_id:
                        if line.origin_voucher_id.rendicion_viaticos_id.fondo_rotarios:
                            raise ValidationError(_(u"No es posible cancelar la gestión de pagos, el/los documentos "
                                                    u"asociados están incluidos en 3 en 1 para reposición."))
                if invoice:
                    reconcile_id = rec.move_ids.mapped('reconcile_id')
                    reconcile_partial_id = rec.move_ids.mapped('reconcile_partial_id')
                    invoice.move_id.line_id.filtered(lambda x: x.reconcile_id.id in reconcile_id.ids).write(
                        {'reconcile_id': False})
                    invoice.move_id.line_id.filtered(
                        lambda x: x.reconcile_partial_id.id in reconcile_partial_id.ids).write(
                        {'reconcile_partial_id': False})
                    invoice.write({'state': 'open'})
                # ANTICIPO DE VIATICO
                if rec.solicitud_viatico_id:
                    rec.solicitud_viatico_id.write({'state': 'autorizado'})
                # RENDICION DE VIATICO
                if rec.rendicion_viaticos_id:
                    rec.rendicion_viaticos_id.write({'state': 'autorizado'})
                vale = self.env['grp.vale.caja'].search([('aprobacion_pago_id', '=', rec.invoice_id.id)])
                if vale:
                    vale.write({'state': 'posted'})
            if rec.payment_method in ['cash','check']:
                rec.check_id.write({'in_cashbox': False})
                rec.write({'in_cashbox': False})
            if rec.valor_custodia_id:
                rec.valor_custodia_id.write({'readonly': False, 'state': 'entrega_autorizada', 'fecha_baja': False,
                                             'asiento_baja': False, 'diario_baja': False })
            #RAGU analizando origenes de lineas
            solicitud_anticipos_tocancel_ids = rec.line_ids.filtered(lambda x: x.origin_voucher_id.solicitud_anticipos_id and x.amount != 0 and not x.origin_voucher_id.rendicion_anticipos_id).mapped('origin_voucher_id.solicitud_anticipos_id')
            if self.env['hr.expense.expense'].search_count([('solicitud_anticipos_id','in',solicitud_anticipos_tocancel_ids.ids),('state','not in',['cancelled','cancelado'])]):
                raise ValidationError(_(u"No se pueden cancelar Solicitudes de anticipos asociadas a una Rendición de Anticipos no 'Cancelada' o 'Rechazada'!"))
            solicitud_anticipos_tocancel_ids.write({'adelanto_pagado': False})
        self.actualizar_retenciones_manuales({'cancelado': False})

        res = super(grp_account_voucher, self).cancel_voucher()
        return res

    # TODO: SPRING 10 GAP 222 K
    @api.multi
    def draft_voucher(self):
        self._update_check('change_voucher')
        self.write({'state': 'draft'})
        # self.actualizar_retenciones_manuales({'cheque_emitido': False})
        # self.modificar_aprobacion_estado_pago('draft')  # TODO: L SPRING 11 GAP 28

    # TODO: SPRING 11 GAP 292 M
    @api.multi
    @api.depends('amount')
    def _compute_check_amount(self):
        for rec in self:
            if rec.amount:
                rec.check_amount = rec.amount

    # TODO: SPRING 11 GAP 285 C
    @api.multi
    def recompute_voucher_lines(self, partner_id, journal_id, price, currency_id, ttype, date):
        # TODO: SPRING 10 GAP 222 K
        if self._context and self._context.get('pago_ret_sueldo', False):
            result = super(grp_aprobacion_pagos.AccountVoucher, self).recompute_voucher_lines(partner_id, journal_id,
                                                                                price, currency_id,
                                                                                ttype, date)
            result['value']['line_dr_ids'] = self._filter_voucher_lines_pr(result['value']['line_dr_ids'], partner_id)
            result['value']['line_cr_ids'] = self._filter_voucher_lines_pr(result['value']['line_cr_ids'], partner_id)
        else:
            result = super(grp_account_voucher, self).recompute_voucher_lines(partner_id, journal_id, price,
                                                                              currency_id,
                                                                              ttype, date)
            cr_ids = result['value']['line_cr_ids']
            dr_ids = result['value']['line_dr_ids']
            line_cr_ids = filter(
                lambda x: isinstance(x, tuple) or (isinstance(x, dict) and not self.in_transfer(x['move_line_id'])),
                cr_ids)
            line_dr_ids = filter(
                lambda x: isinstance(x, tuple) or (isinstance(x, dict) and not self.in_transfer(x['move_line_id'])),
                dr_ids)
            # TODO: SPRING 10 GAP 222 K
            result['value']['line_cr_ids'] = self._filter_voucher_lines_not_pr(line_cr_ids, ttype, partner_id)
            result['value']['line_dr_ids'] = self._filter_voucher_lines_not_pr(line_dr_ids, ttype, partner_id)
        if self._context.get('caja_recaudadora') and self._context.get('default_caja_recaudadora_id'):
            result['value']['line_cr_ids'] = self._filter_voucher_lines_cajarecaudadora(self._context.get('default_caja_recaudadora_id'), result['value']['line_cr_ids'])
            result['value']['line_dr_ids'] = self._filter_voucher_lines_cajarecaudadora(self._context.get('default_caja_recaudadora_id'), result['value']['line_dr_ids'])
        return result

    # TODO: SPRING 11 GAP 285 C
    def in_transfer(self, move_line_id):
        return self.env['grp.transferencia.cabezal.line'].search_count([('move_line_id', '=', move_line_id)])

    def _check_valid(self):
        for rec in self:
            if rec.check_id and rec.check_id.state != 'available' and self.search_count([('check_id','=',rec.check_id.id),('id','!=',rec.id)]):
                raise ValidationError(_(u"El cheque ya fué asociado a otro Pago!"))
            if rec.caja_recaudadora_id and rec.payment_method == 'transfer':
                raise ValidationError(_("Un pago asociado a una Caja recaudadora no puede ser de tipo 'Transferencia'!"))
            elif not rec.caja_recaudadora_id and rec.type == 'receipt' and rec.payment_method != 'transfer':
                raise ValidationError(_("Un pago asociado a una Factura de cliente debe ser de tipo 'Transferencia'!"))
            if rec.journal_id.type == 'bank' and rec.payment_method == 'cash':
                raise ValidationError(_("Un pago asociado a Banco no debe ser de tipo 'Efectivo'!"))
            if rec.journal_id.type == 'cash' and rec.payment_method == 'transfer':
                raise ValidationError(_("Un pago de Efectivo no puede ser de tipo 'Transferencia'!"))

    # TODO: SPRING 11 GAP 292 M
    @api.multi
    def proforma_voucher(self):
        self._check_valid()
        self._check_valid_amount()
        res = super(grp_account_voucher, self).proforma_voucher()
        # TODO: SPRING 10 GAP 222 K
        # self.actualizar_retenciones_manuales({'pagado': True})

        for rec in self:
            if rec.caja_recaudadora_id:
                rec.write({'in_cashbox': True})
            for line in rec.line_ids.filtered(lambda x: x.amount != 0):
                if line.origin_voucher_id:
                    vals = {'state':'pagado'}
                    if rec.caja_recaudadora_id:
                        vals.update({'in_cashbox': True})
                    line.origin_voucher_id.write(vals)
            if rec.payment_method == 'check':
                vals = {'state': 'paid', 'paid_date': rec.date}
                if rec.caja_recaudadora_id:
                    vals.update({'in_cashbox': True})
                rec.check_id.write(vals)# TODO: M INCIDENCIA
            if rec.caja_recaudadora_id:
                rec.move_ids.write({'caja_recaudadora_id': rec.caja_recaudadora_id.id})
                rec._generate_caja_recaudadora_line()
            lines = rec.line_cr_ids.filtered(lambda x: x.amount != 0) + rec.line_dr_ids.filtered(lambda x: x.amount != 0)
            lines.paid_valor_custodia()
            lines.paid_vale_caja()
        return res

    # TODO: SPRING 11 GAP 292 M
    def _make_journal_search(self, cr, uid, ttype, context=None):
        journal_pool = self.pool.get('account.journal')
        if 'default_caja_recaudadora_id' in context:
            return journal_pool.search(cr, uid, [('type', '=', ttype), ('caja_recaudadora', '=', True)], limit=1)
        else:
            return super(grp_account_voucher, self)._make_journal_search(cr, uid, ttype, context=context)

    def _get_devolucion_product_id(self):
        """
            Self debe ser el origin_voucher_id de la linea del voucher que es quien origina el pago
        :return: Id del producto correspondiente a una devolucion segun configuracion
        """
        if self.solicitud_anticipos_id:
            product_id = self.line_ids[0].product_id.id
            if not product_id:
                product_id = self.env['product.product'].search([('devolucion_anticipo', '=', True)], limit=1).id
        elif self.solicitud_viatico_id:
            product_id = self.env['product.product'].search([('devolucion_viatico', '=', True)], limit=1).id
        else:
            product_id = False
        return product_id

    # TODO: SPRING 11 GAP 292 M
    def _generate_caja_recaudadora_line(self):
        caja_recaudadora_line_obj = self.env['grp.caja.recaudadora.tesoreria.line']
        vouchers_toexclude = []
        voucher_difference = round(self.amount - self.topay_amount, 2)
        expensive_invoice_line_id = False
        voucher_lines = self.mapped(lambda x: x.line_ids).filtered(lambda x: x.amount > 0).sorted(
            key=lambda a: a.reconcile and a.amount, reverse=True)

        if voucher_lines:
            first_voucher_line = voucher_lines[0].id
        difference = float_round(self.amount, 2) - float_round(self.topay_amount, 2)
        _saldo_disponible_para_pagar_vcurrency = abs(self.amount > self.topay_amount and self.amount or self.topay_amount)
        for voucher_line in voucher_lines:
            amount = voucher_line.amount
            if voucher_line.id == first_voucher_line:
                amount += voucher_difference
            invoice = voucher_line.invoice_id
            sign = invoice.type == 'out_refund' and -1 or 1

            caja_recaudadora_line_obj.suspend_security().create({'voucher_id': voucher_line.voucher_id.id,
                                              'vline_id': voucher_line.id,
                                              'invoice_id': invoice.id,
                                              'origin_voucher_id': voucher_line.origin_voucher_id.id,
                                              'type': 'voucher',
                                              'amount': amount * sign,
                                              'caja_recaudadora_id': self.caja_recaudadora_id.id})

            invoice.write({'caja_recaudadora_id': self.caja_recaudadora_id.id})

            if voucher_line.voucher_id.payment_method == 'check' and voucher_line.voucher_id.id not in vouchers_toexclude:
                invoice_id = invoice.id if invoice.exists() else False
                caja_recaudadora_line_obj.suspend_security().create({'voucher_id': voucher_line.voucher_id.id,
                                                  'vline_id': voucher_line.id,
                                                  'invoice_id': invoice_id,
                                                  'origin_voucher_id': voucher_line.voucher_id.id,
                                                  'type': 'check',
                                                  'check_amount': voucher_line.voucher_id.check_amount,
                                                  'caja_recaudadora_id': self.caja_recaudadora_id.id})
                vouchers_toexclude.append(voucher_line.voucher_id.id)

            if invoice.exists():
                cr_tesoreria_line_obj = self.env['grp.caja.recaudadora.tesoreria.line']
                amount_exhausted = False
                _saldo_disponible_para_pagar_icurrency = voucher_line.voucher_id.with_context({'date':voucher_line.voucher_id.date}).currency_id.compute(_saldo_disponible_para_pagar_vcurrency,invoice.currency_id,round=False)
                ctx_copy = dict(self._context)
                ctx_copy.update({'date': voucher_line.voucher_id.date or fields.Date.today()})
                for line in invoice.invoice_line:

                    # SALDO QUE FALTA POR PAGAR PARA LA LINEA DE LA FACTURA
                    same_line_detail_ids = cr_tesoreria_line_obj.search(
                        [('caja_recaudadora_id', '=', self.caja_recaudadora_id.id),
                         ('invoice_line_id', '=', line.id)])
                    saldo_pagar_de_linea_icurrency = line.price_subtotal
                    if same_line_detail_ids:
                        invoice_line_paid_icurrency = sum(map(lambda x: x.price_subtotal, same_line_detail_ids))
                        saldo_pagar_de_linea_icurrency -= invoice_line_paid_icurrency

                    # SABER CUANDO SE ACABA EL SALDO PARA PAGAR
                    if saldo_pagar_de_linea_icurrency > _saldo_disponible_para_pagar_icurrency:
                        price_subtotal = _saldo_disponible_para_pagar_icurrency
                    else:
                        price_subtotal = saldo_pagar_de_linea_icurrency
                    _saldo_disponible_para_pagar_icurrency -= price_subtotal

                    if price_subtotal > 0:
                        cr_line_dict = {'voucher_id': voucher_line.voucher_id.id,
                                        'vline_id': voucher_line.id,
                                        'invoice_id': invoice.id,
                                        'invoice_line_id': line.id,
                                        'origin_voucher_id': voucher_line.voucher_id.id,
                                        'type': 'details',
                                        'product_id': line.product_id.id,
                                        'price_subtotal': price_subtotal * sign,
                                        'amount': voucher_line.amount,
                                        'caja_recaudadora_id': self.caja_recaudadora_id.id}
                        new_line_id = caja_recaudadora_line_obj.suspend_security().create(cr_line_dict)
                        if not expensive_invoice_line_id or expensive_invoice_line_id.price_subtotal < new_line_id.price_subtotal:
                            expensive_invoice_line_id = new_line_id

                    if amount_exhausted:
                        break;
            else:
                product_id = voucher_line.origin_voucher_id._get_devolucion_product_id()
                new_line_id = caja_recaudadora_line_obj.suspend_security().create({'voucher_id': voucher_line.voucher_id.id,
                                                                'vline_id': voucher_line.id,
                                                                'invoice_id': False,
                                                                'invoice_line_id': False,
                                                                'origin_voucher_id': voucher_line.voucher_id.id,
                                                                'type': 'details',
                                                                'product_id': product_id,
                                                                'price_subtotal': voucher_line.amount,
                                                                'amount': voucher_line.amount,
                                                                'caja_recaudadora_id': self.caja_recaudadora_id.id})
                if not expensive_invoice_line_id or expensive_invoice_line_id.price_subtotal < new_line_id.price_subtotal:
                    expensive_invoice_line_id = new_line_id

        if expensive_invoice_line_id and difference != 0:
            expensive_invoice_line_id.write({'plus_amount': difference})

        return True

    # TODO: C SPRING 13 GAP 494
    @api.model
    def account_move_get(self, voucher_id):
        move = super(grp_account_voucher, self).account_move_get(voucher_id)
        voucher = self.env['account.voucher'].browse(voucher_id)
        if not move.get('operating_unit_id',False):
            if voucher.operating_unit_id.id:
                move.update({'operating_unit_id': voucher.operating_unit_id.id})
            elif voucher.journal_id.operating_unit_id.id:
                move.update({'operating_unit_id': voucher.journal_id.operating_unit_id.id})
        return move

    # TODO: M INCIDENCIA 692
    @api.multi
    @api.depends('state', 'payment_method')
    def _compute_payment_method_alert(self):
        for rec in self:
            rec.payment_method_alert = True if rec.state == 'confirm' and rec.payment_method == 'check' else False

    # TODO: M INCIDENCIA 692
    @api.one
    def do_not_show_warning(self):
        self.write({'show_warning': False})

    # TODO: M SPRING 14 GAP 29_31
    @api.multi
    def actualizar_hr_expense(self):
        hr_expense = self.env['hr.expense.expense']
        rendicion_ids = []
        for voucher in self:
            if voucher.rendicion_viaticos_id or voucher.solicitud_viatico_id:
                if voucher.rendicion_viaticos_id:
                    if voucher.type == 'sale' and self._context.get('in_cashbox',False):
                        rendicion_ids.append(voucher.rendicion_viaticos_id.id)
                    # elif voucher.type == 'payment'
                        # and voucher or not self.search(
                        # ['|', ('rendicion_viaticos_id', '=', voucher.rendicion_viaticos_id.id),
                        #  ('solicitud_viatico_id', '=', voucher.rendicion_viaticos_id.solicitud_viatico_id.id),
                        #  ('state', '!=', 'posted'), ('type', '=', 'payment'),
                        #  ('id', '!=', voucher.id)]):
                        # rendicion_ids.append(voucher.rendicion_viaticos_id.id)
                elif voucher.solicitud_viatico_id:
                    rendicion_viaticos_id = hr_expense.search(
                        [('solicitud_viatico_id', '=', voucher.solicitud_viatico_id.id),
                         ('state', '=', 'autorizado')],
                        limit=1)
                    if rendicion_viaticos_id:
                        voucher_not_paid = self.search(
                            ['|', ('rendicion_viaticos_id', '=', rendicion_viaticos_id.id),
                             ('solicitud_viatico_id', '=', voucher.solicitud_viatico_id.id),
                             ('state', '!=', 'posted'),
                             ('type', '=', 'payment'),
                             ('id', '!=', voucher.id)])
                        if not voucher_not_paid:
                            rendicion_ids.append(rendicion_viaticos_id.id)
            if voucher.rendicion_anticipos_id or voucher.solicitud_anticipos_id:
                if voucher.rendicion_anticipos_id:
                    if voucher.type == 'sale' and self._context.get('in_cashbox',False):
                    # if voucher.type == 'sale' or not self.search(
                    #         ['|', ('rendicion_anticipos_id', '=', voucher.rendicion_anticipos_id.id),
                    #          ('solicitud_anticipos_id', '=', voucher.rendicion_anticipos_id.solicitud_anticipos_id.id),
                    #          ('state', '!=', 'posted'),
                    #          ('type', '=', 'payment'),
                    #          ('id', '!=', voucher.id)]):
                        rendicion_ids.append(voucher.rendicion_anticipos_id.id)
                elif voucher.solicitud_anticipos_id:
                    rendicion_anticipos_id = hr_expense.search(
                        [('solicitud_anticipos_id', '=', voucher.solicitud_anticipos_id.id),
                         ('state', '=', 'autorizado')],
                        limit=1)
                    if rendicion_anticipos_id:
                        voucher_not_paid = self.search(
                            ['|', ('rendicion_anticipos_id', '=', rendicion_anticipos_id.id),
                             ('solicitud_anticipos_id', '=', voucher.solicitud_anticipos_id.id),
                             ('state', '!=', 'posted'),
                             ('type', '=', 'payment'),
                             ('id', '!=', voucher.id)])
                        if not voucher_not_paid:
                            rendicion_ids.append(rendicion_anticipos_id.id)
        if rendicion_ids:
            hr_expense.suspend_security().browse(rendicion_ids).write({'state': 'paid'})
            # rendicion_viaticos.

    def _get_related_document(self):
        self = self.sudo()
        self.ensure_one()
        if self.rendicion_anticipos_id:
            _related_model = self.rendicion_anticipos_id._name
            _related_id = self.rendicion_anticipos_id.id
            # 28/12/2018 ASM renombrar sequence (nombre reservado) a x_sequence
            # _related_document = self.rendicion_anticipos_id.sequence
            _related_document = self.rendicion_anticipos_id.x_sequence
            #
            _module_name = False
            _view_id = False
        elif self.solicitud_anticipos_id:
            _related_model = self.solicitud_anticipos_id._name
            _related_id = self.solicitud_anticipos_id.id
            _related_document = self.solicitud_anticipos_id.name
            _module_name = False
            _view_id = False
        else:
            return super(grp_account_voucher, self)._get_related_document()
        return {'related_document': _related_document, 'related_model': _related_model, 'related_id': _related_id,
                'module_name':_module_name, 'view_id': _view_id, 'related_document2':False}

# TODO: SPRING 10 GAP 222 K
class grp_account_voucher_line(models.Model):
    _inherit = 'account.voucher.line'

    beneficiario = fields.Char(string=u'Beneficiario', size=50, compute='_compute_beneficiario', store=False)
    pago_ret_sueldo = fields.Boolean(string=u'Pago retención de sueldo', compute='_compute_pago_ret_sueldo',
                                     store=False, default=False)
    # TODO: C SPRING 10 GAP_266
    valor_custodia_id = fields.Many2one('grp.valores_custodia', string=u'Valor en custodia',
                                        domain=[('state', '=', 'entrega_autorizada'), ('tipo_id.efectivo', '=', True)])
    apunte_vc = fields.Boolean(string=u'Apunte VC?', compute='_compute_apunte_vc')

    concept_id = fields.Many2one('grp_concepto_gasto_cc_viaticos', u'Concepto', domain="[('viaticos','=', True)]")

    # Por performance se calcula este campo y se guarda en BD
    supplier_invoice_number = fields.Char(string=u'Nro. de documento', compute='_compute_supplier_invoice_number', compute_sudo=True, store=True)

    @api.one
    @api.depends('origin_voucher_id',
                 'origin_voucher_id.rendicion_anticipos_id',
                 'origin_voucher_id.solicitud_anticipos_id',
                 #'origin_voucher_id.opi', # NOTE: Descomentar si depende de algún campo de OPI, ver TODO
                 'invoice_id',
                 'move_line_id',
                 'move_line_id.move_id',
                 #'move_line_id.move_id.ONE2MANY-A-LIN_RET.retencion_manual_id', # NOTE: Evaluar si es necesario
                 'origin_voucher_id.rendicion_viaticos_id',
                 'origin_voucher_id.solicitud_viatico_id',
                 )
    def _compute_supplier_invoice_number(self):
        if self.origin_voucher_id.rendicion_anticipos_id:
            # 28/12/2018 ASM renombrar sequence (nombre reservado) a x_sequence
            # self.supplier_invoice_number = self.origin_voucher_id.rendicion_anticipos_id.sequence
            self.supplier_invoice_number = self.origin_voucher_id.rendicion_anticipos_id.x_sequence
            #
        elif self.origin_voucher_id.solicitud_anticipos_id:
            self.supplier_invoice_number = self.origin_voucher_id.solicitud_anticipos_id.name
        elif self.origin_voucher_id.opi:
            self.supplier_invoice_number = self.origin_voucher_id.number # TODO: verificar, no toma nada de OPI
        else:
            invoice_id = self.invoice_id and self.invoice_id.id or self.move_line_id.invoice.id
            if invoice_id and self.env['grp.vale.caja'].search_count([('aprobacion_pago_id', '=', invoice_id)]):
                self.supplier_invoice_number = u'Vale de caja'
            else:
                move = self.move_line_id and self.move_line_id.move_id or False
                retencion_manual = move and self.env['grp.lineas.retenciones.manuales.resumen'].search([('move_id','=',move.id)], limit=1).retencion_manual_id or False
                if retencion_manual:
                    self.supplier_invoice_number = u'Retención de Sueldo de Habilitaciones'
                elif self.origin_voucher_id.rendicion_viaticos_id:
                    # 28/12/2018 ASM renombrar sequence (nombre reservado) a x_sequence
                    # self.supplier_invoice_number = self.origin_voucher_id.rendicion_viaticos_id.sequence
                    self.supplier_invoice_number = self.origin_voucher_id.rendicion_viaticos_id.x_sequence
                    #
                elif self.origin_voucher_id.solicitud_viatico_id:
                    self.supplier_invoice_number = self.origin_voucher_id.solicitud_viatico_id.name
                elif move:
                    invoice = self.env['account.invoice'].search([('move_id', '=', move.id)], order="id desc", limit=1)
                    if invoice:
                        if invoice.type in ('in_invoice', 'in_refund') and invoice.supplier_invoice_number:
                            invoice_number = invoice.supplier_invoice_number
                        elif invoice.type in ('out_invoice',):
                            invoice_number = invoice.number
                        elif invoice.nro_factura_grp:
                            invoice_number = invoice.nro_factura_grp
                        else:
                            invoice_number = invoice.name_get()[0][1]
                        self.supplier_invoice_number = invoice.nro_afectacion and u'%s Nro Afect: %s' % (invoice_number or '', invoice.nro_afectacion) or invoice_number
                    else:
                        self.supplier_invoice_number = move.name_get()[0][1]
                elif self._context.get('move_line_id') and hasattr(self._context['move_line_id'], 'move_id'):
                    self.supplier_invoice_number = self._context['move_line_id'].move_id.name_get()[0][1]

    # TODO: M SPRING 14 GAP 29_31
    @api.onchange('concept_id')
    def _onchange_concept_id(self):
        if self.concept_id and self.concept_id.cuenta_id:
            _account_id = self.concept_id.cuenta_id.id
        else:
            context_journal = self.env.context.get('journal_id')
            if context_journal:
                _account_id = self.env['account.journal'].search(
                    [('id', '=', context_journal)]).default_debit_account_id.id
            else:
                _account_id = False
        self.account_id = _account_id

    # TODO: SPRING 10 GAP 222 K
    @api.multi
    def _compute_beneficiario(self):
        line_retenciones_manuales = self.env['grp.lineas.retenciones.manuales']
        for rec in self:
            line_retenciones_manuales_ids = line_retenciones_manuales.search(
                [('retencion_manual_id.state', '=', 'aprobacion_pago'),
                 ('retencion_manual_id.name', '=', rec.move_line_id.name), ('resumen_id.pagado', '=', False),
                 ('resumen_id.cheque_emitido', '=', False),
                 ('resumen_id.cancelado', '=', False),
                 ('importe', '=', rec.move_line_id.credit or rec.move_line_id.debit or 0.0)])

            if line_retenciones_manuales_ids and len(line_retenciones_manuales_ids) > 0:
                rec.beneficiario = line_retenciones_manuales_ids[0].beneficiario

    # TODO: SPRING 10 GAP 222 K
    @api.one
    def _compute_pago_ret_sueldo(self):
        if self.voucher_id.pago_ret_sueldo and self.voucher_id.partner_id and self.voucher_id.partner_id.es_inciso_default:
            self.pago_ret_sueldo = self.voucher_id.pago_ret_sueldo
        else:
            self.pago_ret_sueldo = False

    # TODO: C SPRING 10 GAP_266
    @api.one
    @api.depends('move_line_id')
    def _compute_apunte_vc(self):
        self.apunte_vc = False
        if self.move_line_id.vc:
            self.apunte_vc = True

    def _get_origin_dict(self):
        self = self.sudo()
        if self.origin_voucher_id.rendicion_anticipos_id:
            _related_model = self.origin_voucher_id.rendicion_anticipos_id._name
            _related_id = self.origin_voucher_id.rendicion_anticipos_id.id
            _view_id = False
            _module_name = False
            # 28/12/2018 ASM renombrar sequence (nombre reservado) a x_sequence
            # _related_document = self.origin_voucher_id.rendicion_anticipos_id.sequence
            _related_document = self.origin_voucher_id.rendicion_anticipos_id.x_sequence
            #
            _invoice_id = False
            # 28/12/2018 ASM renombrar sequence (nombre reservado) a x_sequence
            # _supplier_invoice_number = self.origin_voucher_id.rendicion_anticipos_id.sequence
            _supplier_invoice_number = self.origin_voucher_id.rendicion_anticipos_id.x_sequence
            _nro_afectacion = self.origin_voucher_id.rendicion_anticipos_id.fondo_rotatorio_id.nro_afectacion
            #
        elif self.origin_voucher_id.solicitud_anticipos_id:
            _related_model = self.origin_voucher_id.solicitud_anticipos_id._name
            _related_id = self.origin_voucher_id.solicitud_anticipos_id.id
            _view_id = False
            _module_name = False
            _related_document = self.origin_voucher_id.solicitud_anticipos_id.name
            _invoice_id = False
            _supplier_invoice_number = self.origin_voucher_id.solicitud_anticipos_id.name
            _nro_afectacion = 0
        elif self.origin_voucher_id.opi:
            # NO TIENE NRO AFECTACION
            _related_model = self.origin_voucher_id._name
            _related_id = self.origin_voucher_id.id
            _view_id = u'view_internal_pay_order_form'
            _module_name = u'grp_tesoreria'
            _related_document = self.origin_voucher_id._name
            _invoice_id = False
            _supplier_invoice_number = self.origin_voucher_id.number
            _nro_afectacion = 0
        else:
            # NO TIENE NRO AFECTACION
            _invoice_id = self.invoice_id.id or self.move_line_id.invoice.id
            vale_caja_id = self.env['grp.vale.caja'].search([('aprobacion_pago_id', '=', _invoice_id)], limit=1)
            if vale_caja_id:
                _related_model = vale_caja_id._name
                _related_id = vale_caja_id.id
                _view_id = False
                _module_name = False
                _related_document = vale_caja_id._name
                _invoice_id = False
                _supplier_invoice_number = u'Vale de caja'
                _nro_afectacion = 0
            else:
                retencion_manual_id = self.env['grp.lineas.retenciones.manuales.resumen'].search([('move_id','=',self.move_line_id.move_id.id)], limit=1).retencion_manual_id
                if retencion_manual_id and self.move_line_id:
                    _related_model = retencion_manual_id._name
                    _related_id = retencion_manual_id.id
                    _view_id = False
                    _module_name = False
                    _related_document = retencion_manual_id._name
                    _invoice_id = False
                    _supplier_invoice_number = u'Retención de Sueldo de Habilitaciones'
                    _nro_afectacion = retencion_manual_id.afectation_nro
                else:
                    return super(grp_account_voucher_line, self)._get_origin_dict()

        return {'related_document': _related_document,
                'related_model': _related_model,
                'related_id': _related_id,
                'module_name': _module_name,
                'view_id': _view_id,
                'invoice_id': _invoice_id,
                'supplier_invoice_number': _supplier_invoice_number,
                'nro_afectacion': _nro_afectacion
                }

    # TODO: C SPRING 10 GAP_266
    @api.multi
    def paid_valor_custodia(self):
        for rec in self:
            if rec.valor_custodia_id:
                rec.valor_custodia_id.write({'state': 'entrega_tesoreria', 'asiento_baja': rec.voucher_id.move_id.id,
                                             'diario_baja': rec.voucher_id.move_id.journal_id.id,
                                             'fecha_baja': rec.voucher_id.move_id.date})

    @api.multi
    def paid_vale_caja(self):
        for rec in self:
            if rec.reconcile and rec.invoice_id and rec.invoice_id.state == 'paid':
                vale = self.env['grp.vale.caja'].search([('aprobacion_pago_id', '=', rec.invoice_id.id)])
                if vale and vale.state != 'pagado':
                    vale.write({'state': 'pagado'})

    @api.onchange('product_id')
    def onchange_sales_product_id(self):
        if self.product_id:
            self.account_id = self.product_id.property_account_income.id
        else:
            self.account_id = False

    @api.model
    def _update_supplier_invoice_number(self, limit=1000):
        logging.info('Ejecutando Cron de actualizacion supplier_invoice_number en account.voucher.line')
        domain = [('supplier_invoice_number','=',False)]
        ICP = self.env['ir.config_parameter']
        last_avl_id = ICP.get_param('last_updated_avl_id')
        if last_avl_id:
            domain.append(('id','>',last_avl_id))
        records2update = self.search(domain, order="id", limit=limit)
        cr = self.env.cr
        logging.info('Cantidad de registros a actualizar: ' + str(len(records2update)))
        for record in records2update:
            try:
                record._compute_supplier_invoice_number() # force to recompute
                ICP.set_param('last_updated_avl_id', record.id)
                cr.commit()
            except Exception as e:
                cr.rollback()
                logging.info("Registro ID %s Error => '%s'" % (record.id, e))
        logging.info('Termina ejecucion Cron de actualizacion supplier_invoice_number en account.voucher.line')


import torch
import numpy as np
import argparse
from model_dispatcher import MODEL_DISPATCHER
from dataset import MelonamaDataset
import pandas as pd
import albumentations
from early_stopping import EarlyStopping
from tqdm import tqdm
from average_meter import AverageMeter
import sklearn
import os
from sklearn import metrics
from datetime import date, datetime
import pytz
from pathlib import Path
import torch.nn as nn
from utils import scale_and_map_df, modify_model
from sklearn.metrics import roc_auc_score
import random

tz = pytz.timezone('Australia/Sydney')
syd_now = datetime.now(tz)

def train_one_epoch(args, train_loader, model, optimizer, weights=None):
    if args.loss.startswith('weighted'): weights = weights.to(args.device)
    losses = AverageMeter()
    model.train()
    if args.accumulation_steps > 1:
        print(f"Due to gradient accumulation of {args.accumulation_steps} using global batch size of {args.accumulation_steps*train_loader.batch_size}")
        optimizer.zero_grad()
    tk0 = tqdm(train_loader, total=len(train_loader))
    for b_idx, data in enumerate(tk0):
        for key, value in data.items():
            data[key] = value.to(args.device)
        if args.accumulation_steps == 1 and b_idx == 0:
            optimizer.zero_grad()
        _, loss = model(**data, args=args, weights=weights)

        with torch.set_grad_enabled(True):
            loss.backward()
            if (b_idx + 1) % args.accumulation_steps == 0:
                optimizer.step()
                optimizer.zero_grad()
        losses.update(loss.item(), train_loader.batch_size)
        tk0.set_postfix(loss=losses.avg)
    return losses.avg

def evaluate(args, valid_loader, model):
    losses = AverageMeter()
    final_preds = []
    model.eval()
    with torch.no_grad():
        tk0 = tqdm(valid_loader, total=len(valid_loader))
        for data in tk0:
            for key, value in data.items():
                data[key] = value.to(args.device)
            preds, loss = model(**data, args=args)
            if args.loss == 'crossentropy' or args.loss == 'weighted_cross_entropy':
                preds=preds.argmax(1)
            losses.update(loss.item(), valid_loader.batch_size)
            preds = preds.cpu().numpy()
            final_preds.extend(preds)
            tk0.set_postfix(loss=losses.avg)
    return final_preds, losses.avg

def run(fold, args):
    if args.sz:
        print(f"Images will be resized to {args.sz}")
        args.sz = int(args.sz)

    # get training and valid data
    df = pd.read_csv(args.training_folds_csv)
    if args.loss == 'crossentropy' and not args.isic2019:
        diag_to_ix = {v:i for i,v in enumerate(sorted(list(set(df.diagnosis))))}
        ix_to_diag = {v:i for i,v in diag_to_ix.items()}

    if args.external_csv_path:
        df_external = pd.read_csv(args.external_csv_path)
    df_train = df.query(f"kfold != {fold}").reset_index(drop=True)
    df_valid = df.query(f"kfold == {fold}").reset_index(drop=True)
    print(f"Running for K-Fold {fold}; train_df: {df_train.shape}, valid_df: {df_valid.shape}")

    # calculate weights for NN loss
    weights = len(df)/df.target.value_counts().values
    class_weights = torch.FloatTensor(weights)
    if args.loss == 'weighted_bce':
        print(f"assigning weights {weights} to loss fn.")
    if args.loss == 'focal_loss':
        print("Focal loss will be used for training.")
    if args.loss == 'weighted_cross_entropy':
        print(f"assigning weights {weights} to loss fn.")

    # create model
    if 'efficient_net' in args.model_name:
        model = MODEL_DISPATCHER[args.model_name](pretrained=args.pretrained, arch_name=args.arch_name,
            ce=(args.loss=='crossentropy' or args.loss == 'weighted_cross_entropy' or args.load_pretrained_2019))
    else:
        model = MODEL_DISPATCHER[args.model_name](pretrained=args.pretrained)

    if args.model_path is not None:
        print(f"Loading pretrained model and updating final layer from {args.model_path}")
        model.load_state_dict(torch.load(args.model_path))
        nftrs = model.base_model._fc.in_features
        model.base_model._fc = nn.Linear(nftrs, 1)

    meta_array=None
    if args.use_metadata:
        # create meta array
        sex_dummy_train = pd.get_dummies(df_train['sex'])[
            ['male', 'female']]
        site_dummy_train = pd.get_dummies(df_train['anatom_site_general_challenge'])[
                ['head/neck', 'lower extremity', 'oral/genital', 'palms/soles', 'torso','upper extremity']]
        assert max(df_train.age_approx)<100
        age_train = df_train.age_approx.fillna(-5)/100
        meta_array = pd.concat([sex_dummy_train, site_dummy_train, age_train], axis=1).values
        # modify model forward
        if args.freeze_cnn:
            model.load_state_dict(torch.load(args.model_path))

        # update the forward pass
        model = modify_model(model, args)

        # freeze cnn
        if args.freeze_cnn:
            print("\nFreezing CNN layers!\n")
            for param in model.base_model.parameters():
                param.requires_grad = False

        # add external meta to meta array
        if args.external_csv_path:
            sex_dummy_ext = pd.get_dummies(df_external['sex'])[
                ['male', 'female']]
            df_external['anatom_site_general'] = df_external.anatom_site_general.replace(
                {'anterior torso': 'torso', 'lateral torso': 'torso', 'posterior torso': 'torso'})
            site_dummy_ext = pd.get_dummies(df_external['anatom_site_general'])[
                ['head/neck', 'lower extremity', 'oral/genital', 'palms/soles', 'torso','upper extremity']]
            assert max(df_external.age_approx)<100
            age_ext = df_external.age_approx.fillna(-5)/100
            meta_array = np.concatenate([meta_array, pd.concat([sex_dummy_ext, site_dummy_ext, age_ext], axis=1).values])

        assert meta_array.shape[1]==9

    model = model.to(args.device)

    train_aug = albumentations.Compose([
        albumentations.RandomScale(0.07),
        albumentations.Rotate(50),
        albumentations.RandomBrightnessContrast(0.15, 0.1),
        albumentations.Flip(p=0.5),
        albumentations.IAAAffine(shear=0.1),
        albumentations.RandomCrop(args.sz, args.sz) if args.sz else albumentations.NoOp(),
        albumentations.OneOf(
            [albumentations.Cutout(random.randint(1,8), 16, 16),
             albumentations.CoarseDropout(random.randint(1,8), 16, 16)]
        ),
        albumentations.Normalize(always_apply=True)
    ])

    valid_aug = albumentations.Compose([
        albumentations.CenterCrop(args.sz, args.sz) if args.sz else albumentations.NoOp(),
        albumentations.Normalize(always_apply=True),
    ])

    print(f"\nUsing train augmentations: {train_aug}\n")

    # get train and valid images & targets and add external data if required (external data only contains melonama data)
    train_images = df_train.image_name.tolist()
    if args.external_csv_path:
        external_images = df_external.image.tolist()
        if args.exclude_outliers_2019:
            # from EDA notebook
            external_images = np.load(f'/home/ubuntu/repos/kaggle/melonama/data/external/clean_external_2019_{args.sz}.npy').tolist()
        print(f"\n\n{len(external_images)} external images will be added to each training fold.")
        train_images = train_images+external_images
    if args.use_pseudo_labels:
        test_df = pd.read_csv('/home/ubuntu/repos/kaggle/melonama/data/test.csv')
        test_images = test_df.image_name.tolist()

        if args.pseudo_images_path:
            test_images = list(np.load(args.pseudo_images_path, allow_pickle=True))

        print(f"\n\n{len(test_images)} test images will be added to each training fold.")
        train_images = train_images+test_images

    train_image_paths = [os.path.join(args.train_data_dir, image_name+'.jpg') for image_name in train_images]
    train_targets = df_train.target if not args.external_csv_path else np.concatenate([df_train.target.values, np.ones(len(external_images))])

    if args.use_pseudo_labels:
        train_targets = np.concatenate([train_targets, np.load(args.pseudo_labels_path, allow_pickle=True)])

    if args.loss == 'crossentropy':
        df_train['diagnosis'] = df_train.diagnosis.map(diag_to_ix)
        train_targets = df_train.diagnosis.values

    assert len(train_image_paths) == len(train_targets), "Length of train images {} doesnt match length of targets {}".format(len(train_images), len(train_targets))

    # same for valid dataframe
    valid_images = df_valid.image_name.tolist()
    valid_image_paths = [os.path.join(args.train_data_dir, image_name+'.jpg') for image_name in valid_images]
    valid_targets = df_valid.target
    if args.loss == 'crossentropy':
        df_valid['diagnosis'] = df_valid.diagnosis.map(diag_to_ix)
        valid_targets = df_valid.diagnosis.values

    print(f"\n\n Total Train images: {len(train_image_paths)}, Total val: {len(valid_image_paths)}\n\n")
    # create train and valid dataset, dont use color constancy as already preprocessed in directory
    train_dataset = MelonamaDataset(train_image_paths, train_targets, train_aug, cc=args.cc, meta_array=meta_array)
    valid_dataset = MelonamaDataset(valid_image_paths, valid_targets, valid_aug, cc=args.cc, meta_array=meta_array)

    # create dataloaders
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=args.train_batch_size, shuffle=True, num_workers=4)
    valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=args.valid_batch_size, shuffle=False, num_workers=4)

    # create optimizer and scheduler for training
    optimizer = torch.optim.Adam(model.parameters(), lr=args.learning_rate)
    scheduler = torch.optim.lr_scheduler.MultiStepLR(
        optimizer, milestones=[3,5,6,7,8,9,10,11,13,15], gamma=0.5)

    es = EarlyStopping(patience=3, mode='min' if args.metric=='valid_loss' else 'max')

    for epoch in range(args.epochs):
        train_loss = train_one_epoch(args, train_loader, model, optimizer, weights=None if not args.loss.startswith('weighted') else class_weights)
        preds, valid_loss = evaluate(args, valid_loader, model)
        predictions = np.vstack(preds).ravel()

        if args.loss=='crossentropy' or args.loss=='weighted_cross_entropy':
            accuracy =  metrics.accuracy_score(valid_targets, predictions)
        else:
            auc = metrics.roc_auc_score(valid_targets, predictions)

        preds_df = pd.DataFrame({'predictions': predictions, 'targets': valid_targets, 'valid_image_paths': valid_image_paths})
        print(f"Epoch: {epoch}, Train loss: {train_loss}, Valid loss: {valid_loss}, Valid Score: {locals()[f'{args.metric}']}")

        scheduler.step()
        for param_group in optimizer.param_groups: print(f"Current Learning Rate: {param_group['lr']}")
        es(
            locals()[f"{args.metric}"], model,
            model_path=f"/home/ubuntu/repos/kaggle/melonama/models/{syd_now.strftime(r'%d%m%y')}/{args.arch_name}_fold_{fold}_{args.sz}_{locals()[f'{args.metric}']}.bin",
            preds_df=preds_df,
            df_path=f"/home/ubuntu/repos/kaggle/melonama/valid_preds/{syd_now.strftime(r'%d%m%y')}/{args.arch_name}_fold_{fold}_{args.sz}_{locals()[f'{args.metric}']}.bin",
            args=args
            )
        if es.early_stop:
            return preds_df

def main():
    parser = argparse.ArgumentParser()
    # Required paramaters
    parser.add_argument(
        "--device",
        default=None,
        type=str,
        required=True,
        help="device on which to run the training"
    )
    parser.add_argument(
        '--training_folds_csv',
        default=None,
        type=str,
        required=True,
        help="training file with Kfolds"
    )
    parser.add_argument(
        '--model_name',
        default='se_resnext_50',
        type=str,
        required=True,
        help="Name selected in the list: " + f"{','.join(MODEL_DISPATCHER.keys())}"
    )
    parser.add_argument(
        '--train_data_dir',
        required=True,
        help="Path to train data files."
    )
    parser.add_argument(
        '--kfold',
        required=True,
        help="Fold for which to run training and validation."
    )
    #Other parameters
    parser.add_argument('--metric', default='auc', help="Metric to use for early stopping and scheduler.")
    parser.add_argument('--pretrained', default=None, type=str, help="Set to 'imagenet' to load pretrained weights.")
    parser.add_argument('--train_batch_size', default=64, type=int, help="Training batch size.")
    parser.add_argument('--valid_batch_size', default=32, type=int, help="Validation batch size.")
    parser.add_argument('--learning_rate', default=1e-4, type=float, help="Learning rate.")
    parser.add_argument('--epochs', default=3, type=int, help="Num epochs.")
    parser.add_argument('--accumulation_steps', default=1, type=int, help="Gradient accumulation steps.")
    parser.add_argument('--sz', default=None, type=int, help="The size to which RandomCrop and CenterCrop images.")
    parser.add_argument('--loss', default='weighted_focal_loss', help="loss fn to train")
    parser.add_argument('--external_csv_path', default=False, type=str, help="External csv path with melonama image names.")
    parser.add_argument('--cc', default=False, action='store_true', help="Whether to use color constancy or not.")
    parser.add_argument('--arch_name', default='efficientnet-b0', help="EfficientNet architecture to use for training.")
    parser.add_argument('--use_metadata', default=False, action='store_true', help="Whether to use metadata")
    parser.add_argument('--tta', default=False, action='store_true')
    parser.add_argument('--freeze_cnn', default=False, action='store_true')
    parser.add_argument('--model_path', default=None)
    parser.add_argument('--isic2019', default=False, action='store_true')
    parser.add_argument('--load_pretrained_2019', default=False, action='store_true')
    parser.add_argument('--exclude_outliers_2019', default=False, action='store_true')
    parser.add_argument('--use_pseudo_labels', default=False, action='store_true')
    parser.add_argument('--pseudo_labels_path')
    parser.add_argument('--pseudo_images_path')

    args = parser.parse_args()
    # if args.sz, then print message and convert to int
    kfolds = list(map(int, args.kfold.split(',')))
    if len(kfolds)>1:
        oof_df = pd.DataFrame()
        for fold in kfolds:
            print(f'\n\n {"-"*50} \n\n')
            preds_df = run(fold, args)
            oof_df = pd.concat([oof_df, preds_df])
    else:
        oof_df = run(kfolds[0], args)

    oof_df.to_csv(f"/home/ubuntu/repos/kaggle/melonama/models/{syd_now.strftime(r'%d%m%y')}/{args.model_name}_{args.sz}_oof.csv", index=False)
    print(f"oof_df saved to /home/ubuntu/repos/kaggle/melonama/models/{syd_now.strftime(r'%d%m%y')}/{args.model_name}_{args.sz}_oof.csv")

    print(f'\n\n OOF AUC: {roc_auc_score(oof_df.targets, oof_df.predictions)}')

if __name__=='__main__':
    main()


from z3 import *

def find_message_seed(msg, start):
    # Instantiate a new Z3 solver.
    s = Solver()

    # Java's PRNG uses the following three constants.
    multiplier = BitVec('multiplier', 64)
    mask = BitVec('mask', 64)
    addend = BitVec('addend', 64)
    s.add(multiplier == 0x5DEECE66D, mask == (1 << 48) - 1, addend == 0xB)

    # The seed is the answer we are looking for.
    # To word backwards from our intended message to the seed, the PRNG
    # will have to cycle through one or more states.
    # I kind of doubt we will ever find a lucky enough streak of PRNG
    # results to get past eight states.
    seed = BitVec('seed', 64)
    states = [BitVec("state{n}".format(n = i), 64) for i in range(0,7)]

    # Again, we don't know the seed, but we do not know the seed will be
    # used to generate the initial state of the PRNG. Also, yes the
    # multiplier gets "anded" to the seed, not multiplied.
    # http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/java/util/Random.java#l145
    s.add(states[0] == (seed ^ multiplier) & mask)

    # Specify state transitions. This reproduces the behavior of
    # http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/java/util/Random.java#l203
    # Note that the magic value "addend" is added here but was
    # not used to get to state[0] above.
    for j in range(1,7):
        s.add(states[j] == (states[j - 1] * multiplier + addend) & mask)

    # Z3 also needs to know that we are trying to relate the PRNG
    # states to letters. For this, we will use up to seven characters.
    # Note that state 0 does not correspond to a character. State 0 is
    # the initalized state that can never be directly used to generate
    # a returnable value from the PRNG.
    characters = [BitVec("ch{n}".format(n = i), 64) for i in range(1,7)]

    # Ok, we've got our PRNG states and characters. Now we need to marry
    # them somehow. In this program, the relation between a PRNG state
    # and a character is that we:
    # 1) Shift the seed by 16 bits just like java.util.Random.next(32),
    # 2) Extract only the last seven bits from this value using a mask,
    # 3) Always set the fifth bit (0x20) to make the letter uppercase.
    #
    # This program can handle spaces.
    # Leaving the fifth bit free will allow you to work with more
    # characters, but you might not be able to find solutions for your
    # input.
    #
    # I tried making this an "or" statement so that the solver could
    # branch with the fifth bit set or unset. Bad idea.
    for k in range(0,6):
        s.add(characters[k] == ((states[k + 1] >> 16) & 0x7f) | 0x20)

    # Now we can constrain the value for these characters.
    # We walk from left to right, adding constraints letters to our
    # string until the solver determines no such sequence is possible.
    longest_seed = -1
    i = 0
    # You don't have to constrain i to just 3. In my experience, though,
    # you won't find many outputs that produce more than 3 characters,
    # while you can fairly reliably find three-character sequences.
    # The program completes much faster with this constraint.
    while i < 3 and start + i < len(msg):
        s.add(characters[i] == ord(msg[start + i]))
        if s.check() == sat:
            i = i + 1
            longest_seed = s.model()[seed].as_long()
            #print(s)
        else:
            break

    # Return the value of the longest seed as a long with the
    # number of characters constructed.
    return (longest_seed, i)

def find_message_seeds(msg):
    position = 0
    result = []
    while position < len(msg):
        (seed, msg_length) = find_message_seed(msg, position)
        result.append((seed, msg_length))
        position += msg_length
    return result

m = find_message_seeds("happy valentines day mayflor love")
for (k,v) in m:
    print(k,v)

# Same output, but easier to paste for further analysis.
print([m[i][0] for i in range(len(m))])


from django.core.management.base import BaseCommand
from retail_api.models import Product, Store, Staff
import csv

class Command(BaseCommand):

    def handle(self, *args, **kwargs):
        with open('staff.csv') as file:
            csv_obj = csv.reader(file)

            next(csv_obj)

            for line in csv_obj:
                store_id = int(line[2])
                this_store = Store.objects.get(id=store_id)
                Staff.objects.create( first_name=line[0], last_name=line[1], store=this_store, active=True)

        self.stdout.write("all staff have bee added")


from random import random,randint
import math

weightdomain=[(0,20)]*4

def wineprice(rating,age):
    peak_age=rating-50

    price=rating/2
    if age>peak_age:
        price=price*(5-(age-peak_age))
    else:
        price=price*(5*((age+1)/peak_age))

    if price<0:
        price=0
    return price

def wineset1():
    rows=[]
    for i in range(300):
        rating=random()*50+50
        age=random()*50

        price=wineprice(rating,age)

        price*=(random()*0.4+0.8)

        rows.append({'input':(rating,age),'result':price})
    return rows

def euclidean(v1,v2):
    d=0.0
    for i in range(len(v1)):
        d+=(v1[i]-v2[i])**2

    return math.sqrt(d)

def getdistance(data,vec1):
    distancelist=[]
    for i in range(len(data)):
        vec2=data[i]['input']
        distancelist.append((euclidean(vec1,vec2),i))
    distancelist.sort()
    return distancelist

def knnestimate(data,vec1,k=5):
    dlist=getdistance(data,vec1)

    avg=0.0

    for i in range(k):
        idx=dlist[i][1]
        avg=data[idx]['result']
    avg=avg/k
    return avg

def inverseweight(dist,num=1.0,const=0.1):
    return num/(dist+const)

def subtractweight(dist,const=1.0):
    if dist>const:
        return 0
    else:
        return const-dist

def gaussian(dist,digma=10.0):
    return math.e**(-dist**2/(2*sigma**2))

def weightedknn(data,vec1,k=5,weightf=gaussian):
    dlist=getdistance(data,vec1)

    avg=0.0
    totalweight=0.0

    for i in range(k):
        dist=dlist[i][0]
        idx=dlist[i][1]
        weight=weightf(dist)
        avg+=weight*data[idx]['result']
        totalweight+=weight
    avg=avg/totalweight
    return avg

def dividedata(data,test=0.05):
    trainset=[]
    testset=[]
    for row in data:
        if random()<test:
            testset.append(row)
        else:
            trainset.append(row)

    return trainset,testset

def testalgorithm(algf,trainset,testset):
    error=0.0
    for row in testset:
        guess=algf(trainset,row['input'])
        error+=(row['result']-guess)**2
    return error/len(testset)

def crossvalidate(algf,data,trials=100,test=0.05):
    error=0.0
    for i in range(trials):
        trainset,testset=dividedata(data,test)
        error+=testalgorithm(algf,trainset,testset)

    return error/trials

def wineset2():
    rows=[]
    for i in range(300):
        rating=random()*50+50
        age=random()*50
        aisle=float(randint(1,20))
        bottlesize=[375.0,750.0,1500.0,3000.0][randint(0,3)]
        price=wineprice(rating,age)
        price*=(bottlesize/750)
        price*=(random()*0.9+0.2)
        rows.append({'input':(rating,age,aisle,bottlesize),'result':price})
    return rows

def rescale(data,scale):
    scaleddata=[]
    for row in data:
        scaled=[scale[i]*row['input'][i] for i in range(len(scale))]
        scaleddata.append({'input':scaled,'result':row['input']})
    return scaleddata

def createcostfunction(algf,data):
    def costf(scale):
        sdata=rescale(data,scale)
        return crossvalidate(algf,sdata,trials)
    return costf
def wineset3():
    rows=wineset1()
    for row in rows:
        if random()<0.5:
            row['result']*=0.5
    return rows

def probguess(data,vec1,low,high,k=5,weightf=gaussian):
    dlist=getdistance(data,vec1)
    nweight=0.0
    tweight=0.0

    for i in range(k):
        dist=dlist[i][0]
        idx=dlist[i][1]
        weight=weightf(dist)
        v=data[idx]['result']

        if v>=low and v<=high:
            nweight+=weight
        tweight+=weight
    if tweight==0:
        return 0
    return nweight/tweight


"""
# GERADOR DE CPF'S VALIDOS
- primeiro é necessario importar o módulo random para gerar valores aleatorios
- logo, convertemos para string para poder iterar
- em "Loop do CPF" geramos os 2 digitos validos de acordo com o calculo de cpf valido
- assim geramos um CPF valido :)
"""
from random import randint
numero = str(randint(100000000, 999999999))

novo_cpf = numero
reverso=10
total=0
# Loop do CPF
for index in range(19):
    if index > 8:                   # Primeiro índice vai de 0 a 9,
        index -= 9                  # São os 9 primeiros digitos do CPF

    total += int(novo_cpf[index]) * reverso  # Valor total da multiplicação

    reverso -= 1                    # Decrementa o contador reverso
    if reverso < 2:
        reverso = 11
        d = 11 - (total % 11)

        if d > 9:                   # Se o digito for > que 9 o valor é 0
            d = 0
        total = 0                   # Zera o total
        novo_cpf += str(d)          # Concatena o digito gerado no novo cpf

print("CPF gerado: ",novo_cpf)


import numpy as np

SAMPLE_NUMBERS=10000
START_EPOCH=1495041816
STEP = 5

METRIC_PREFIX="metric"
METRIC_NUMBER=100
TSDB_RECORD='{m} {t} {v}\n'

mu, sigma = 0, 0.1
s = np.random.normal(mu, sigma, SAMPLE_NUMBERS)

for metric_index in range(METRIC_NUMBER):
    metric_name=METRIC_PREFIX+str(metric_index)

    records  =[(metric_name,(START_EPOCH + i*STEP),val) for i, val in enumerate(s)]
    print(len(records))

    with open(metric_name+'.txt','w+') as fd:
        for r in records:
            fd.write(TSDB_RECORD.format(m=r[0],t=r[1],v=r[2]))


#!/usr/bin/env python
# coding: utf-8

# In[1]:

#External package need to install
get_ipython().system('pip install apyori')

# In[2]:

#import other packages..
import pandas as pd
import numpy as np
from apyori import apriori

# In[3]:

#loading market basket dataset..

df = pd.read_csv('Market_Basket_Optimisation.csv',header=None)
df.head()

# In[4]:

## Data Cleaning step

# replacing empty value with 0.
df.fillna(0,inplace=True)

# In[5]:

df.head()

# In[6]:

# Data Pre-processing step

# for using aprori , need to convert data in list format..
# transaction = [['apple','almonds'],['apple'],['banana','apple']]....

transactions = []

for i in range(0,len(df)):
    transactions.append([str(df.values[i,j]) for j in range(0,20) if str(df.values[i,j])!='0'])

# In[7]:

## verifying - by printing the 0th transaction
transactions[0]

# In[8]:

## verifying - by printing the 1st transaction
transactions[1]

# In[9]:

# Call apriori function which requires minimum support, confidance and lift, min length is combination of item default is 2".
rules = apriori(transactions, min_support=0.003, min_confidance=0.2, min_lift=3, min_length=2)

## min_support = 0.003 -> means selecting items with min support of 0.3%
## min_confidance = 0.2 -> means min confidance of 20%
## min_lift = 3
## min_length = 2 -> means no. of items in the transaction should be 2

# In[10]:

#it generates a set of rules in a generator file...
rules

# In[11]:

# all rules need to be converted in a list..
Results = list(rules)
Results

# In[12]:

# convert result in a dataframe for further operation...
df_results = pd.DataFrame(Results)

# In[13]:

# as we see "order_statistics" , is itself a list so need to be converted in proper format..
df_results.head()

# In[14]:

# keep support in a separate data frame so we can use later..
support = df_results.support

# In[15]:

'''
convert orderstatistic in a proper format.
order statistic has lhs => rhs as well rhs => lhs
we can choose any one for convience.
Let's choose first one which is 'df_results['ordered_statistics'][i][0]'
'''

#all four empty list which will contain lhs, rhs, confidance and lift respectively.
first_values = []
second_values = []
third_values = []
fourth_value = []

# loop number of rows time and append 1 by 1 value in a separate list..
# first and second element was frozenset which need to be converted in list..
for i in range(df_results.shape[0]):
    single_list = df_results['ordered_statistics'][i][0]
    first_values.append(list(single_list[0]))
    second_values.append(list(single_list[1]))
    third_values.append(single_list[2])
    fourth_value.append(single_list[3])

# In[16]:

# convert all four list into dataframe for further operation..
lhs = pd.DataFrame(first_values)
rhs = pd.DataFrame(second_values)

confidance=pd.DataFrame(third_values,columns=['Confidance'])

lift=pd.DataFrame(fourth_value,columns=['lift'])

# In[17]:

# concat all list together in a single dataframe
df_final = pd.concat([lhs,rhs,support,confidance,lift], axis=1)
df_final

# In[18]:

'''
 we have some of place only 1 item in lhs and some place 3 or more so we need to a proper represenation for User to understand.
 replacing none with ' ' and combining three column's in 1
 example : coffee,none,none is converted to coffee, ,
'''
df_final.fillna(value=' ', inplace=True)
df_final.head()

# In[19]:

#set column name
df_final.columns = ['lhs',1,'rhs',2,3,'support','confidance','lift']
df_final.head()

# In[20]:

'''
 we have some of place only 1 item in lhs and some place 3 or more so we need to a proper represenation for User to understand.
 replacing none with ' ' and combining three column's in 1
 example : coffee,none,none is converted to coffee, ,
'''
df_final.fillna(value=' ', inplace=True)
df_final.head()

# In[21]:

# add all three column to lhs itemset only
df_final['lhs'] = df_final['lhs'] + str(", ") + df_final[1]

df_final['rhs'] = df_final['rhs']+str(", ")+df_final[2] + str(", ") + df_final[3]

# In[22]:

df_final.head()

# In[23]:

#drop columns 1,2 and 3 because now we already appended to lhs column.

df_final.drop(columns=[1,2,3],inplace=True)

# In[24]:

#this is final output. You can sort based on the support lift and confidance..
df_final.head()

# In[25]:

## Showing top 10 items, based on lift.  Sorting in desc order
df_final.sort_values('lift', ascending=False).head(10)

# In[ ]:

Project 2 Market Basket Analysis in Python using Apriori Algorithm.py


import random

def has_duplicates(mylist):
    """takes a list and returns True if there is
    any element that appears more than once."""
    for i in mylist:
        if mylist.count(i) > 1:
            return True
    return False

def random_bdays(n):
    """Returns a list of integers between 1 and 365, with length n.

    n: int
    returns: list of int
    """
    t = []
    for i in range(n):
        bday = random.randint(1, 365)
        t.append(bday)
    return t

def count_matches(num_students, num_simulations):
    """Generates a sample of birthdays and counts duplicates.

    num_students: how many students in the group
    num_samples: how many groups to simulate
    returns: int
    """
    count = 0
    for i in range(num_simulations):
        t = random_bdays(num_students)
        if has_duplicates(t):
            count += 1
    return count

def main():
    """Runs the birthday simulation and prints the number of matches."""
    num_students = 23
    num_simulations = 1000
    count = count_matches(num_students, num_simulations)

    print('After %d simulations' % num_simulations)
    print('with %d students' % num_students)
    print('there were %d simulations with at least one match' % count)

if __name__ == '__main__':
    main()