# -*- coding: utf-8 -*-
# @File  : balance_io_account_item.py
# Created by cx on 2020/4/15
# xuan.chen@yunside.com

from odoo import models, fields, api
from odoo.exceptions import ValidationError
from odoo.tools import config

DIRECTION = [('debit', u'借'),
             ('credit', u'贷')]


class BalanceIOAccountItem(models.Model):
    _name = 'jd.init.balance.io.account.item'
    _description = u'凭证科目明细账'
    _seq_prefix = 'BAI'

    number = fields.Char(string=u'流水号', required=True, default='/')
    state = fields.Selection([('draft', u'草稿'),
                              ('confirm', u'确认')
                              ], default='draft', string=u'状态')
    company_id = fields.Many2one('res.company', string=u'组织', required=True)
    date = fields.Date(string=u'业务日期', required=True)
    bukrs = fields.Char(string=u'公司代码')
    prctr = fields.Char(string=u'利润中心')
    account_item_id = fields.Many2one('jd.int.sap.account.item', string=u'凭证科目', required=True)
    direction = fields.Selection(DIRECTION, string=u'方向', required=True)
    currency_id = fields.Many2one('res.currency', string=u'币种', required=True)
    amount_start = fields.Float(string=u'期初金额', required=True, default=0)
    amount = fields.Float(string=u'发生金额', required=True, default=0)
    amount_end = fields.Float(string=u'期末金额', required=True, default=0)
    origin = fields.Reference(string=u'来源单据', selection='_reference_models', readonly=True)
    voucher_code = fields.Char(string=u'凭证编号')
    voucher_revoke_code = fields.Char(string=u'冲销凭证编号')
    number_assist = fields.Char(string=u'辅助对象编码')
    assist_account = fields.Char(string=u'辅助核算')

    @api.model
    def create_default_sequence(self):
        force_company = self._context.get('force_company')
        if not force_company:
            force_company = self.env.user.company_id.id
        seq_code = str(self._name) + ('.%s' % force_company)
        seq_rec = self.env['ir.sequence'].sudo().search(
            ['&', ('code', '=', seq_code), ('company_id', '=', force_company)], limit=1)
        if not seq_rec:
            prefix = config.get('system_prefix', '').strip()
            company_str = ("000" + str(force_company))[-4:]
            if self._seq_prefix:
                prefix += str(self._seq_prefix) + company_str + '%(y)s%(month)s%(day)s'
            else:
                prefix += company_str + '%(y)s%(month)s%(day)s'
            seq_val = {
                'name': seq_code,
                'code': seq_code,
                'prefix': prefix,
                'padding': 4,
                'company_id': force_company
            }
            seq_rec = self.env['ir.sequence'].sudo().create(seq_val)
        return seq_rec

    @api.model
    def create(self, values):
        """
        获取自动单号
        :param values:
        :return:
        """
        if values.get('number', '/') == '/':
            company_id = values.get('company_id', self.env.user.company_id.id)
            seq_rec = self.with_context(force_company=company_id).create_default_sequence()
            values.update({
                'number': seq_rec._next()
            })
        return super(BalanceIOAccountItem, self).create(values)

    @api.model
    def _reference_models(self):
        models = self.env['ir.model'].search([('state', '!=', 'manual')])
        return [(model.model, model.name)
                for model in models
                if not model.model.startswith('ir.')]

    @api.multi
    def unlink(self):
        force_unlink = self.env.context.get('force_unlink')
        if not force_unlink:
            raise ValidationError(u'不能人工删除凭证科目明细账')

    @api.multi
    @api.depends('company_id', 'account_item_id')
    def name_get(self):
        result = []
        for item in self:
            name_direction = dict(self._fields['direction'].selection).get(item.direction)
            name = '%s-%s-%s' % (item.company_id.name, name_direction, item.account_item_id.name)
            result.append((item.id, name))
        return result

    @api.multi
    @api.onchange('amount_start', 'amount', 'direction')
    def onchange_for_amount_end(self):
        for item in self:
            amount_end = 0
            if item.direction:
                if item.direction == 'debit':
                    amount_end = item.amount_start + item.amount
                elif item.direction == 'credit':
                    amount_end = item.amount_start - item.amount
            item.amount_end = amount_end

    @api.model
    def get_last_io_balance(self, company_id, account_item_id, currency_id, record_id, number_assist):
        """
        获取最后一条流水的期末余额
        :param company_id: 
        :param account_item_id: 
        :param currency_id: 
        :param record_id: 
        :param number_assist:  辅助对象编码
        :return: 
        """
        if not isinstance(company_id, int) and hasattr(company_id, 'id'):
            company_id = company_id.id
        if not isinstance(account_item_id, int) and hasattr(account_item_id, 'id'):
            account_item_id = account_item_id.id
        if not isinstance(currency_id, int) and hasattr(currency_id, 'id'):
            currency_id = currency_id.id
        if not isinstance(record_id, int) and hasattr(record_id, 'id'):
            record_id = record_id.id
        if not number_assist:
            number_assist = ''
        info = {
            'number': '无上一条流水',
            'amount_end': 0
        }
        cr = self.env.cr
        sql = '''
        SELECT
          io.number                  AS number,
          coalesce(io.amount_end, 0) AS amount_end
        FROM jd_init_balance_io_account_item io
        WHERE io.company_id = %(company_id)s
              AND io.account_item_id = %(account_item_id)s
              AND io.currency_id = %(currency_id)s
              AND coalesce(io.number_assist, '') = %(number_assist)s
              AND io.id != %(record_id)s
        ORDER BY io.id DESC
        LIMIT 1
        '''
        params = {
            'company_id': company_id,
            'account_item_id': account_item_id,
            'currency_id': currency_id,
            'number_assist': number_assist,
            'record_id': record_id
        }
        cr.execute(sql, params)
        rec = cr.dictfetchone()
        if rec and rec.get('number'):
            info = rec
        return info

    @api.model
    def do_confirm_action(self, record_id, user_id=None):
        """
        :param record_id:
        :param user_id:
        :return:
        """
        if not isinstance(record_id, int) and hasattr(record_id, 'id'):
            record_id = record_id.id
        if not user_id:
            user_id = self.env.uid
        if not isinstance(user_id, int) and hasattr(user_id, 'id'):
            user_id = user_id.id
        cr = self.env.cr
        sql = '''
        DO
        $$
        DECLARE
          rec               RECORD;
          v_balance_id      INT;
          v_number          VARCHAR;
          v_number_assist   VARCHAR;
        BEGIN
          FOR rec IN (
            SELECT
              io.id              AS io_id,
              io.company_id      AS company_id,
              io.account_item_id AS account_item_id,
              io.number_assist   AS number_assist,
              io.assist_account  AS assist_account,
              it.number          AS number_account,
              io.bukrs           AS bukrs,
              io.prctr           AS prctr,
              io.currency_id     AS currency_id,
              CASE WHEN io.direction = 'debit'
                THEN io.amount
              WHEN io.direction = 'credit'
                THEN (io.amount * -1)
              ELSE 0
              END                AS amount_change
            FROM jd_init_balance_io_account_item io
              INNER JOIN jd_int_sap_account_item it ON it.id = io.account_item_id
            WHERE io.state = 'draft'
                AND io.id = %(record_id)s
          ) LOOP
            IF rec.amount_change <> 0
            THEN
              -- 查找是否存在 科目余额记录
              SELECT ai.id
              INTO v_balance_id
              FROM jd_init_balance_account_item ai
              WHERE ai.company_id = rec.company_id
                    AND ai.account_item_id = rec.account_item_id
                    AND ai.currency_id = rec.currency_id
                    AND ai.number_assist = rec.number_assist;
              -- 若不存在科目余额记录，则创建
              IF v_balance_id ISNULL
              THEN
                IF rec.number_assist ISNULL
                THEN
                  v_number_assist := '';
                ELSE
                  v_number_assist := rec.number_assist;
                END IF;
                v_number := 'BA' || rec.company_id :: VARCHAR || rec.number_account || v_number_assist;
                INSERT INTO jd_init_balance_account_item
                (create_date, create_uid, write_date, write_uid,
                 number, company_id, bukrs, prctr, account_item_id,
                 currency_id, number_assist, assist_account, amount)
                VALUES
                  (current_timestamp AT TIME ZONE 'UTC', %(user_id)s, current_timestamp AT TIME ZONE 'UTC', %(user_id)s,
                 v_number, rec.company_id, rec.bukrs, rec.prctr, rec.account_item_id,
                 rec.currency_id, rec.number_assist, rec.assist_account, rec.amount_change);
              ELSE
                UPDATE jd_init_balance_account_item
                SET
                  amount     = amount + rec.amount_change,
                  write_date = current_timestamp AT TIME ZONE 'UTC',
                  write_uid  = %(user_id)s
                WHERE id = v_balance_id;
              END IF;
              
              UPDATE jd_init_balance_io_account_item 
              SET
                state      = 'confirm',
                write_date = current_timestamp AT TIME ZONE 'UTC',
                write_uid  = %(user_id)s
              WHERE id = rec.io_id;
            END IF;
          END LOOP;
        END;
        $$
        '''
        params = {
            'record_id': record_id,
            'user_id': user_id
        }
        cr.execute(sql, params)

    @api.multi
    def do_confirm(self):
        for item in self:
            if item.state == 'confirm':
                raise ValidationError(u'凭证科目明细账【%s】已确定，不能重复确认' % item.number)
            if not item.amount:
                raise ValidationError(u'凭证科目明细账【%s】未填写发生金额，请检查' % item.number)
            # elif item.amount < 0:
            #     raise ValidationError(u'凭证科目明细账【%s】的发生金额【%s】小于0，请检查' % (item.number, item.amount))
            io_info = self.get_last_io_balance(company_id=item.company_id.id,
                                               account_item_id=item.account_item_id.id,
                                               currency_id=item.currency_id.id,
                                               record_id=item.id,
                                               number_assist=item.number_assist)
            amount_end = float(io_info.get('amount_end'))
            if round(amount_end, 3) != round(item.amount_start, 3):
                raise ValidationError(u'【%s-%s-%s】最近一条流水【%s】的期末余额【%s】与当前流水【%s】的期初余额【%s】不一致，'
                                      u'请检查' %
                                      (item.company_id.name, item.account_item_id.name, item.number_assist or '',
                                       io_info.get('number'), io_info.get('amount_end'),
                                       item.number, item.amount_start))
            balance_info = self.env['jd.init.balance.account.item'].get_balance(company_id=item.company_id.id,
                                                                                account_item_id=item.account_item_id.id,
                                                                                currency_id=item.currency_id.id,
                                                                                number_assist=item.number_assist)
            amount = float(balance_info.get('amount'))
            if round(amount, 3) != round(item.amount_start, 3):
                raise ValidationError(u'【%s-%s-%s】的科目余额【%s】与当前流水【%s】的期初余额【%s】不一致，'
                                      u'请检查' %
                                      (item.company_id.name, item.account_item_id.name, item.number_assist or '',
                                       balance_info.get('amount'), item.number, item.amount_start))
            item.do_confirm_action(record_id=item.id)

    @api.model
    def make_a_deal(self, kwargs):
        """
        创建借贷流水记录
        :return:
        """
        if not kwargs.get('company_id'):
            raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                  u'错误详情：\r\n未提供参数：组织ID')
        company_id = kwargs.get('company_id')
        if isinstance(company_id, int):
            company_id = self.env['res.company'].sudo().browse(company_id)
        company_id = company_id.sudo()
        currency_id = company_id.currency_id.id
        if not currency_id:
            raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                  u'错误详情：\r\n组织【%s】未维护 币种' % company_id.name)

        date = kwargs.get('date', fields.Date.context_today(self))
        bukrs = kwargs.get('bukrs')
        prctr = kwargs.get('prctr')
        if not bukrs or not prctr:
            strategy_info = self.env['jd.int.push.strategy'].sudo().get_strategy_info(company_id=company_id,
                                                                                      system_type='sap',
                                                                                      date=date)
            if not strategy_info:
                raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                      u'错误详情：\r\n组织【%s】未配置凭证推送策略' % company_id.name)
        else:
            strategy_info = {}
        
        if not bukrs:
            if not strategy_info.get('bukrs'):
                raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                      u'错误详情：\r\n组织【%s】的凭证推送策略未设置公司代码' % company_id.name)
            bukrs = strategy_info.get('bukrs')

        if not prctr:
            if not strategy_info.get('prctr'):
                raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                      u'错误详情：\r\n组织【%s】的凭证推送策略未设置利润中心' % company_id.name)
            prctr = strategy_info.get('prctr')

        if not kwargs.get('account_item_id'):
            raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                  u'错误详情：\r\n未提供参数：凭证科目ID')
        account_item_id = kwargs.get('account_item_id')
        if not isinstance(account_item_id, int) and hasattr(account_item_id, 'id'):
            account_item_id = account_item_id.id

        if not kwargs.get('direction'):
            raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                  u'错误详情：\r\n未提供参数：借贷方向')
        direction = kwargs.get('direction')
        if direction not in ('debit', 'credit'):
            raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
                                  u'错误详情：\r\n提供的借贷方向参数值【%s】错误' % direction)
        origin = kwargs.get('origin', None)
        amount = float(kwargs.get('amount', 0))
        # if float(amount) <= 0:
        #     raise ValidationError(u'创建凭证科目明细账 make_a_deal 错误，请联系管理员\r\n'
        #                           u'错误详情：\r\n提供的 发生金额【%s】需大于0' % amount)
        number_assist = kwargs.get('number_assist', None)
        assist_account = kwargs.get('assist_account', None)
        balance_info = self.env['jd.init.balance.account.item'].get_balance(company_id=company_id.id,
                                                                            account_item_id=account_item_id,
                                                                            currency_id=currency_id,
                                                                            number_assist=number_assist)
        amount_start = float(balance_info.get('amount'))
        if direction == 'debit':
            amount_end = amount_start + amount
        else:
            amount_end = amount_start - amount
        voucher_code = kwargs.get('voucher_code', None)
        voucher_revoke_code = kwargs.get('voucher_revoke_code', None)

        # 获取编号
        seq_rec = self.with_context(force_company=company_id.id).create_default_sequence()
        number = seq_rec._next()
        cr = self.env.cr
        sql = '''
        INSERT INTO jd_init_balance_io_account_item
        (create_date, create_uid, write_date, write_uid,
         number, company_id, bukrs, prctr, account_item_id,
         currency_id, direction, date, origin, voucher_code, voucher_revoke_code,
         number_assist, assist_account, amount_start, amount, amount_end, state)
        VALUES
          (current_timestamp AT TIME ZONE 'UTC', %(user_id)s, current_timestamp AT TIME ZONE 'UTC', %(user_id)s,
           %(number)s, %(company_id)s, %(bukrs)s, %(prctr)s, %(account_item_id)s,
           %(currency_id)s, %(direction)s, %(date)s, %(origin)s, %(voucher_code)s, %(voucher_revoke_code)s,
           %(number_assist)s, %(assist_account)s, %(amount_start)s, %(amount)s, %(amount_end)s, 'draft')
        RETURNING id;
        '''
        params = {
            'number': number,
            'company_id': company_id.id,
            'bukrs': bukrs,
            'prctr': prctr,
            'account_item_id': account_item_id,
            'currency_id': currency_id,
            'direction': direction,
            'date': date,
            'origin': origin,
            'amount_start': amount_start,
            'amount': amount,
            'amount_end': amount_end,
            'voucher_code': voucher_code,
            'voucher_revoke_code': voucher_revoke_code,
            'number_assist': number_assist,
            'assist_account': assist_account,
            'user_id': self.env.uid
        }
        cr.execute(sql, params)
        io_id = cr.fetchone()[0]
        io_rec = self.browse(io_id)
        io_rec.do_confirm()
