# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

from decimal import Decimal

from trytond.exceptions import UserError
from trytond.model import ModelSQL, ModelView, Workflow, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import Button, StateAction, StateView, Wizard

__all__ = [
    'TradeFinance',
    'TradeFinancePresentation',
    'TradeFinancePresentationLine',
    'TradeFinanceCreateStart',
    'TradeFinanceCreate',
]


_DRAFT_STATES = {
    'readonly': Eval('state') != 'draft',
}
_DRAFT_DEPENDS = ['state']


class TradeFinance(Workflow, ModelSQL, ModelView):
    'Trade Finance'
    __name__ = 'trade.finance'
    _rec_name = 'number'

    number = fields.Char(
        'Trade Finance Reference', required=True, readonly=True)
    bank_reference = fields.Char('Bank Reference')
    bank = fields.Many2One(
        'bank', 'Trade Finance Entity', required=True, ondelete='RESTRICT',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    party = fields.Many2One(
        'party.party', 'Counterparty', ondelete='RESTRICT',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    company = fields.Many2One(
        'company.company', 'Company', required=True, ondelete='RESTRICT',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    currency = fields.Many2One(
        'currency.currency', 'Currency', required=True, ondelete='RESTRICT',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    start_date = fields.Date(
        'Start Date', required=True,
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('active', 'Active'),
        ('closed', 'Closed'),
        ('cancelled', 'Cancelled'),
    ], 'State', required=True, readonly=True)
    description = fields.Text(
        'Description', states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    presentations = fields.One2Many(
        'trade.finance.presentation', 'trade_finance', 'Presentations',
        states={'readonly': Eval('state').in_(['closed', 'cancelled'])},
        depends=['state'])
    exposure_amount = fields.Function(fields.Numeric(
        'Exposure Amount', digits=(16, 2)), 'get_exposure_amount')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._order.insert(0, ('number', 'DESC'))
        cls._transitions |= set((
            ('draft', 'active'),
            ('active', 'closed'),
            ('draft', 'cancelled'),
            ('active', 'cancelled'),
            ('cancelled', 'draft'),
        ))
        cls._buttons.update({
            'activate': {
                'invisible': Eval('state') != 'draft',
                'depends': ['state'],
            },
            'close': {
                'invisible': Eval('state') != 'active',
                'depends': ['state'],
            },
            'cancel': {
                'invisible': ~Eval('state').in_(['draft', 'active']),
                'depends': ['state'],
            },
            'draft': {
                'invisible': Eval('state') != 'cancelled',
                'depends': ['state'],
            },
        })

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_start_date():
        Date = Pool().get('ir.date')
        return Date.today()

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @classmethod
    def _new_number(cls):
        pool = Pool()
        ModelData = pool.get('ir.model.data')
        Sequence = pool.get('ir.sequence')
        try:
            sequence_id = ModelData.get_id(
                'trade_finance', 'sequence_trade_finance')
        except KeyError:
            return '/'
        return Sequence(sequence_id).get()

    @classmethod
    def create(cls, vlist):
        vlist = [v.copy() for v in vlist]
        for values in vlist:
            values.setdefault('number', cls._new_number())
        return super().create(vlist)

    @classmethod
    def write(cls, *args):
        actions = iter(args)
        for records, values in zip(actions, actions):
            if set(values) - {'state'}:
                for record in records:
                    if record.state in {'closed', 'cancelled'}:
                        raise UserError(
                            'Closed or cancelled trade finance files cannot '
                            'be modified.')
        return super().write(*args)

    def get_rec_name(self, name):
        parts = [self.number]
        if self.bank_reference:
            parts.append('[%s]' % self.bank_reference)
        return ' '.join(parts)

    @classmethod
    def search_rec_name(cls, name, clause):
        _, operator, value = clause
        if operator.startswith('!') or operator.startswith('not '):
            bool_op = 'AND'
        else:
            bool_op = 'OR'
        return [bool_op,
            ('number', operator, value),
            ('bank_reference', operator, value),
        ]

    @classmethod
    def get_exposure_amount(cls, records, name):
        amounts = {r.id: Decimal('0.00') for r in records}
        for record in records:
            for presentation in record.presentations:
                if presentation.state == 'draft':
                    continue
                for line in presentation.lines:
                    amounts[record.id] += line.signed_financed_amount or 0
        return amounts

    @classmethod
    @ModelView.button
    @Workflow.transition('active')
    def activate(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('closed')
    def close(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('cancelled')
    def cancel(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, records):
        pass


class TradeFinancePresentation(Workflow, ModelSQL, ModelView):
    'Trade Finance Presentation'
    __name__ = 'trade.finance.presentation'

    trade_finance = fields.Many2One(
        'trade.finance', 'Trade Finance', required=True, ondelete='CASCADE',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    sequence = fields.Integer(
        'Sequence', states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    presentation_date = fields.Date(
        'Presentation Date', required=True,
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    presentation_type = fields.Selection([
        ('prefi', 'Prefinancing'),
        ('transfer', 'Transfer'),
        ('shipment', 'Shipment Presentation'),
        ('matching', 'Matched Sale'),
        ('invoice', 'Invoice Financing'),
        ('release', 'Release'),
        ('cancellation', 'Cancellation'),
    ], 'Presentation Type', required=True,
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    finance_stage = fields.Selection([
        ('prefi', 'Prefi'),
        ('produced', 'Produced'),
        ('inland_transit', 'Inland Transit'),
        ('port_stock', 'Port Stock'),
        ('on_board', 'On Board'),
        ('ocean_transit', 'Ocean Transit'),
        ('delivered', 'Delivered'),
        ('matched', 'Matched'),
        ('invoiced', 'Invoiced'),
        ('repaid', 'Repaid'),
        ('cancelled', 'Cancelled'),
    ], 'Finance Stage', required=True,
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    financing_type = fields.Many2One(
        'trade_finance.financing_type', 'Financing Type',
        ondelete='RESTRICT', states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    origin = fields.Reference(
        'Origin', selection='get_origin',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    previous_presentation = fields.Many2One(
        'trade.finance.presentation', 'Previous Presentation',
        ondelete='RESTRICT', states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    description = fields.Text(
        'Description', states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    lines = fields.One2Many(
        'trade.finance.presentation.line', 'presentation', 'Movement Lines',
        states=_DRAFT_STATES, depends=_DRAFT_DEPENDS)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('posted', 'Posted'),
        ('cancelled', 'Cancelled'),
    ], 'State', required=True, readonly=True)
    currency = fields.Function(fields.Many2One(
        'currency.currency', 'Currency'), 'on_change_with_currency')
    bank = fields.Function(fields.Many2One(
        'bank', 'Trade Finance Entity'), 'on_change_with_bank')
    exposure_amount = fields.Function(fields.Numeric(
        'Exposure Amount', digits=(16, 2)), 'get_exposure_amount')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._order.insert(0, ('presentation_date', 'DESC'))
        cls._order.insert(1, ('sequence', 'DESC'))
        cls._transitions |= set((
            ('draft', 'posted'),
            ('posted', 'cancelled'),
        ))
        cls._buttons.update({
            'post': {
                'invisible': Eval('state') != 'draft',
                'depends': ['state'],
            },
            'cancel': {
                'invisible': Eval('state') != 'posted',
                'depends': ['state'],
            },
        })

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_presentation_date():
        Date = Pool().get('ir.date')
        return Date.today()

    @staticmethod
    def default_presentation_type():
        return 'prefi'

    @staticmethod
    def default_finance_stage():
        return 'prefi'

    @classmethod
    def _get_origin(cls):
        'Return list of Model names for origin Reference.'
        return ['stock.shipment.in', 'stock.shipment.out', 'stock.move']

    @classmethod
    def get_origin(cls):
        Model = Pool().get('ir.model')
        get_name = Model.get_name
        return [(None, '')] + [
            (model, get_name(model)) for model in cls._get_origin()]

    @classmethod
    def create(cls, vlist):
        vlist = [v.copy() for v in vlist]
        for values in vlist:
            if values.get('trade_finance') and not values.get('sequence'):
                values['sequence'] = cls._next_sequence(
                    values['trade_finance'])
        return super().create(vlist)

    @classmethod
    def write(cls, *args):
        actions = iter(args)
        for records, values in zip(actions, actions):
            if set(values) - {'state'}:
                for record in records:
                    if record.state != 'draft':
                        raise UserError(
                            'Posted or cancelled presentations cannot be '
                            'modified.')
        return super().write(*args)

    @classmethod
    def delete(cls, records):
        for record in records:
            if record.state != 'draft':
                raise UserError(
                    'Posted or cancelled presentations cannot be deleted.')
        return super().delete(records)

    @classmethod
    def _next_sequence(cls, trade_finance):
        presentations = cls.search([
            ('trade_finance', '=', trade_finance),
        ], order=[('sequence', 'DESC')], limit=1)
        if presentations and presentations[0].sequence:
            return presentations[0].sequence + 1
        return 1

    @fields.depends('trade_finance', '_parent_trade_finance.currency')
    def on_change_with_currency(self, name=None):
        return self.trade_finance.currency if self.trade_finance else None

    @fields.depends('trade_finance', '_parent_trade_finance.bank')
    def on_change_with_bank(self, name=None):
        return self.trade_finance.bank if self.trade_finance else None

    def get_rec_name(self, name):
        items = []
        if self.trade_finance:
            items.append(self.trade_finance.number)
        if self.sequence:
            items.append('#%s' % self.sequence)
        items.append(self.presentation_type_string)
        return ' '.join(items)

    @classmethod
    def get_exposure_amount(cls, records, name):
        amounts = {r.id: Decimal('0.00') for r in records}
        for record in records:
            for line in record.lines:
                amounts[record.id] += line.signed_financed_amount or 0
        return amounts

    @classmethod
    @ModelView.button
    @Workflow.transition('posted')
    def post(cls, presentations):
        Line = Pool().get('trade.finance.presentation.line')
        lines = []
        for presentation in presentations:
            presentation.check_for_posting()
            for line in presentation.lines:
                line.apply_limit_values()
                line.check_for_posting()
                lines.append(line)
        if lines:
            Line.save(lines)

    @classmethod
    @ModelView.button
    @Workflow.transition('cancelled')
    def cancel(cls, presentations):
        for presentation in presentations:
            presentation.create_reversal()

    def check_for_posting(self):
        if not self.lines:
            raise UserError(
                'A presentation must contain at least one movement line before '
                'posting.')
        if self.presentation_type == 'transfer' and not self.previous_presentation:
            raise UserError(
                'Transfer presentations must reference a previous '
                'presentation.')

    def create_reversal(self):
        Date = Pool().get('ir.date')
        reversal_lines = []
        for line in self.lines:
            reversal_lines.append(('create', [line.get_reverse_values()]))
        self.__class__.create([{
            'trade_finance': self.trade_finance.id,
            'sequence': self.__class__._next_sequence(self.trade_finance.id),
            'presentation_date': Date.today(),
            'presentation_type': 'cancellation',
            'finance_stage': 'cancelled',
            'financing_type': (
                self.financing_type.id if self.financing_type else None),
            'previous_presentation': self.id,
            'description': 'Reversal of %s' % self.rec_name,
            'state': 'posted',
            'lines': reversal_lines,
        }])


class TradeFinancePresentationLine(ModelSQL, ModelView):
    'Trade Finance Presentation Line'
    __name__ = 'trade.finance.presentation.line'

    presentation = fields.Many2One(
        'trade.finance.presentation', 'Presentation',
        required=True, ondelete='CASCADE')
    facility_limit = fields.Many2One(
        'trade_finance.facility_limit', 'Facility Limit', required=True,
        ondelete='RESTRICT')
    movement_date = fields.Date('Movement Date', required=True)
    finance_stage = fields.Selection([
        ('prefi', 'Prefi'),
        ('produced', 'Produced'),
        ('inland_transit', 'Inland Transit'),
        ('port_stock', 'Port Stock'),
        ('on_board', 'On Board'),
        ('ocean_transit', 'Ocean Transit'),
        ('delivered', 'Delivered'),
        ('matched', 'Matched'),
        ('invoiced', 'Invoiced'),
        ('repaid', 'Repaid'),
        ('cancelled', 'Cancelled'),
    ], 'Finance Stage', required=True)
    signed_quantity = fields.Numeric('Signed Quantity', digits=(16, 4))
    uom = fields.Many2One('product.uom', 'UoM', ondelete='RESTRICT')
    commodity_price = fields.Numeric('Commodity Price', digits=(16, 6))
    gross_collateral_value = fields.Numeric(
        'Gross Collateral Value', digits=(16, 2))
    haircut_percent_applied = fields.Numeric(
        'Haircut % Applied', digits=(16, 2))
    financed_amount = fields.Numeric('Financed Amount', digits=(16, 2))
    signed_financed_amount = fields.Numeric(
        'Signed Financed Amount', digits=(16, 2), required=True)
    interest_rate_applied = fields.Numeric(
        'Interest Rate % Applied', digits=(16, 4))
    day_count_basis_applied = fields.Selection([
        ('360', '360'),
        ('365', '365'),
        ('act_360', 'ACT/360'),
        ('act_365', 'ACT/365'),
    ], 'Day Count Basis')
    currency = fields.Many2One(
        'currency.currency', 'Currency', required=True, ondelete='RESTRICT')
    value_date = fields.Date('Value Date')
    maturity_date = fields.Date('Maturity Date')
    description = fields.Text('Description')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._order.insert(0, ('movement_date', 'DESC'))

    @classmethod
    def create(cls, vlist):
        vlist = [v.copy() for v in vlist]
        for values in vlist:
            cls._set_defaults_from_presentation(values)
            cls._set_amounts(values)
        return super().create(vlist)

    @classmethod
    def write(cls, *args):
        actions = iter(args)
        for lines, values in zip(actions, actions):
            if values:
                for line in lines:
                    if line.presentation.state != 'draft':
                        raise UserError(
                            'Posted or cancelled movement lines cannot be '
                            'modified.')
        args = list(args)
        actions = iter(args)
        new_args = []
        for lines, values in zip(actions, actions):
            values = values.copy()
            cls._set_defaults_from_presentation(values)
            cls._set_amounts(values)
            new_args.extend((lines, values))
        return super().write(*new_args)

    @classmethod
    def delete(cls, lines):
        for line in lines:
            if line.presentation.state != 'draft':
                raise UserError(
                    'Posted or cancelled movement lines cannot be deleted.')
        return super().delete(lines)

    @classmethod
    def _set_defaults_from_presentation(cls, values):
        if not values.get('presentation'):
            return
        presentation = Pool().get('trade.finance.presentation')(
            values['presentation'])
        values.setdefault('movement_date', presentation.presentation_date)
        values.setdefault('finance_stage', presentation.finance_stage)
        if presentation.trade_finance:
            values.setdefault('currency', presentation.trade_finance.currency.id)

    @classmethod
    def _set_amounts(cls, values):
        quantity = values.get('signed_quantity')
        price = values.get('commodity_price')
        haircut = values.get('haircut_percent_applied')
        gross = values.get('gross_collateral_value')
        financed = values.get('financed_amount')

        if gross is None and quantity is not None and price is not None:
            gross = abs(quantity) * price
            values['gross_collateral_value'] = gross
        if financed is None and gross is not None:
            haircut = haircut or Decimal('0')
            financed = gross * (Decimal('1') - haircut / Decimal('100'))
            values['financed_amount'] = financed
        if (values.get('signed_financed_amount') is None
                and financed is not None):
            sign = Decimal('1')
            if quantity is not None and quantity < 0:
                sign = Decimal('-1')
            values['signed_financed_amount'] = financed * sign

    @fields.depends('presentation', '_parent_presentation.presentation_date',
        '_parent_presentation.finance_stage',
        '_parent_presentation.trade_finance',
        '_parent_presentation.trade_finance.currency')
    def on_change_presentation(self):
        if not self.presentation:
            return
        self.movement_date = self.presentation.presentation_date
        self.finance_stage = self.presentation.finance_stage
        if self.presentation.trade_finance:
            self.currency = self.presentation.trade_finance.currency

    @fields.depends('signed_quantity', 'commodity_price')
    def on_change_with_gross_collateral_value(self, name=None):
        if self.signed_quantity is None or self.commodity_price is None:
            return None
        return abs(self.signed_quantity) * self.commodity_price

    @fields.depends('gross_collateral_value', 'haircut_percent_applied')
    def on_change_with_financed_amount(self, name=None):
        if self.gross_collateral_value is None:
            return None
        haircut = self.haircut_percent_applied or Decimal('0')
        return self.gross_collateral_value * (
            Decimal('1') - haircut / Decimal('100'))

    @fields.depends('financed_amount', 'signed_quantity')
    def on_change_with_signed_financed_amount(self, name=None):
        if self.financed_amount is None:
            return None
        sign = Decimal('1')
        if self.signed_quantity is not None and self.signed_quantity < 0:
            sign = Decimal('-1')
        return self.financed_amount * sign

    def apply_limit_values(self):
        haircut_copied = False
        if self.haircut_percent_applied is None:
            self.haircut_percent_applied = (
                self._get_applicable_haircut() or Decimal('0'))
            haircut_copied = True
        if self.interest_rate_applied is None:
            self.interest_rate_applied = (
                self._get_applicable_interest_rate() or Decimal('0'))
        if not self.day_count_basis_applied:
            self.day_count_basis_applied = '360'
        if self.gross_collateral_value is None:
            self.gross_collateral_value = (
                self.on_change_with_gross_collateral_value())
        if self.financed_amount is None or haircut_copied:
            self.financed_amount = self.on_change_with_financed_amount()
        if self.signed_financed_amount is None or (
                haircut_copied and self.signed_quantity is not None):
            self.signed_financed_amount = (
                self.on_change_with_signed_financed_amount())

    def check_for_posting(self):
        presentation = self.presentation
        trade_finance = presentation.trade_finance
        limit = self.facility_limit
        movement_date = self.movement_date or presentation.presentation_date

        if self.signed_financed_amount == 0:
            raise UserError('Signed financed amount cannot be zero.')
        if limit.facility.tfe != trade_finance.bank:
            raise UserError(
                "The movement limit must belong to the presentation's Trade "
                'Finance Entity.')
        if limit.currency != self.currency:
            raise UserError(
                'The movement currency must match the facility limit currency.')
        if movement_date < limit.date_from or movement_date > limit.date_to:
            raise UserError(
                'The movement date must be within the facility limit validity '
                'period.')
        if (presentation.financing_type and limit.financing_type
                and presentation.financing_type != limit.financing_type):
            raise UserError(
                'The movement financing type must match the facility limit '
                'financing type.')

    def _get_applicable_haircut(self):
        for haircut in self.facility_limit.haircuts:
            if self._date_matches(haircut.date_from, haircut.date_to):
                return haircut.haircut_pct
        return None

    def _get_applicable_interest_rate(self):
        for cost in self.facility_limit.costs:
            if (cost.cost_type == 'interest'
                    and self._date_matches(cost.date_from, cost.date_to)):
                return cost.spread
        return None

    def _date_matches(self, date_from, date_to):
        date = self.movement_date or self.presentation.presentation_date
        if date_from and date < date_from:
            return False
        if date_to and date > date_to:
            return False
        return True

    def get_reverse_values(self):
        return {
            'facility_limit': self.facility_limit.id,
            'movement_date': Pool().get('ir.date').today(),
            'finance_stage': 'cancelled',
            'signed_quantity': (
                -self.signed_quantity if self.signed_quantity is not None
                else None),
            'uom': self.uom.id if self.uom else None,
            'commodity_price': self.commodity_price,
            'gross_collateral_value': self.gross_collateral_value,
            'haircut_percent_applied': self.haircut_percent_applied,
            'financed_amount': self.financed_amount,
            'signed_financed_amount': -self.signed_financed_amount,
            'interest_rate_applied': self.interest_rate_applied,
            'day_count_basis_applied': self.day_count_basis_applied,
            'currency': self.currency.id,
            'value_date': self.value_date,
            'maturity_date': self.maturity_date,
            'description': 'Reversal of %s' % (self.description or self.id),
        }


class TradeFinanceCreateStart(ModelView):
    'Create Trade Finance Start'
    __name__ = 'trade.finance.create.start'

    facility_limit = fields.Many2One(
        'trade_finance.facility_limit', 'Facility Limit', required=True,
        ondelete='RESTRICT')
    bank = fields.Function(fields.Many2One(
        'bank', 'Trade Finance Entity'), 'on_change_with_bank')
    company = fields.Many2One(
        'company.company', 'Company', required=True, ondelete='RESTRICT')
    party = fields.Many2One(
        'party.party', 'Counterparty', ondelete='RESTRICT')
    currency = fields.Function(fields.Many2One(
        'currency.currency', 'Currency'), 'on_change_with_currency')
    bank_reference = fields.Char('Bank Reference')
    start_date = fields.Date('Start Date', required=True)
    presentation_date = fields.Date('Presentation Date', required=True)
    presentation_type = fields.Selection([
        ('prefi', 'Prefinancing'),
        ('transfer', 'Transfer'),
        ('shipment', 'Shipment Presentation'),
        ('matching', 'Matched Sale'),
        ('invoice', 'Invoice Financing'),
        ('release', 'Release'),
        ('cancellation', 'Cancellation'),
    ], 'Presentation Type', required=True)
    finance_stage = fields.Selection([
        ('prefi', 'Prefi'),
        ('produced', 'Produced'),
        ('inland_transit', 'Inland Transit'),
        ('port_stock', 'Port Stock'),
        ('on_board', 'On Board'),
        ('ocean_transit', 'Ocean Transit'),
        ('delivered', 'Delivered'),
        ('matched', 'Matched'),
        ('invoiced', 'Invoiced'),
        ('repaid', 'Repaid'),
        ('cancelled', 'Cancelled'),
    ], 'Finance Stage', required=True)
    financing_type = fields.Function(fields.Many2One(
        'trade_finance.financing_type', 'Financing Type'),
        'on_change_with_financing_type')
    signed_quantity = fields.Numeric('Signed Quantity', digits=(16, 4))
    uom = fields.Many2One('product.uom', 'UoM', ondelete='RESTRICT')
    commodity_price = fields.Numeric('Commodity Price', digits=(16, 6))
    gross_collateral_value = fields.Numeric(
        'Gross Collateral Value', digits=(16, 2))
    haircut_percent_applied = fields.Numeric(
        'Haircut % Applied', digits=(16, 2))
    financed_amount = fields.Numeric('Financed Amount', digits=(16, 2))
    signed_financed_amount = fields.Numeric(
        'Signed Financed Amount', digits=(16, 2), required=True)
    value_date = fields.Date('Value Date')
    maturity_date = fields.Date('Maturity Date')
    description = fields.Text('Description')
    post = fields.Boolean('Post Presentation')

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @staticmethod
    def default_start_date():
        Date = Pool().get('ir.date')
        return Date.today()

    @staticmethod
    def default_presentation_date():
        Date = Pool().get('ir.date')
        return Date.today()

    @staticmethod
    def default_presentation_type():
        return 'prefi'

    @staticmethod
    def default_finance_stage():
        return 'prefi'

    @staticmethod
    def default_post():
        return False

    @fields.depends('facility_limit', '_parent_facility_limit.facility')
    def on_change_with_bank(self, name=None):
        if self.facility_limit and self.facility_limit.facility:
            return self.facility_limit.facility.tfe

    @fields.depends('facility_limit', '_parent_facility_limit.currency')
    def on_change_with_currency(self, name=None):
        if self.facility_limit:
            return self.facility_limit.currency

    @fields.depends('facility_limit', '_parent_facility_limit.financing_type')
    def on_change_with_financing_type(self, name=None):
        if self.facility_limit:
            return self.facility_limit.financing_type

    @fields.depends('signed_quantity', 'commodity_price')
    def on_change_with_gross_collateral_value(self, name=None):
        if self.signed_quantity is None or self.commodity_price is None:
            return None
        return abs(self.signed_quantity) * self.commodity_price

    @fields.depends('gross_collateral_value', 'haircut_percent_applied')
    def on_change_with_financed_amount(self, name=None):
        if self.gross_collateral_value is None:
            return None
        haircut = self.haircut_percent_applied or Decimal('0')
        return self.gross_collateral_value * (
            Decimal('1') - haircut / Decimal('100'))

    @fields.depends('financed_amount', 'signed_quantity')
    def on_change_with_signed_financed_amount(self, name=None):
        if self.financed_amount is None:
            return None
        sign = Decimal('1')
        if self.signed_quantity is not None and self.signed_quantity < 0:
            sign = Decimal('-1')
        return self.financed_amount * sign


class TradeFinanceCreate(Wizard):
    'Create Trade Finance'
    __name__ = 'trade.finance.create'

    start = StateView('trade.finance.create.start',
        'trade_finance.trade_finance_create_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Create', 'create_', 'tryton-ok', default=True),
            ])
    create_ = StateAction('trade_finance.act_trade_finance')

    def do_create_(self, action):
        pool = Pool()
        TradeFinance = pool.get('trade.finance')
        Presentation = pool.get('trade.finance.presentation')
        Line = pool.get('trade.finance.presentation.line')

        start = self.start
        facility_limit = start.facility_limit
        bank = facility_limit.facility.tfe
        currency = facility_limit.currency
        financing_type = facility_limit.financing_type
        trade_finance, = TradeFinance.create([{
            'bank_reference': start.bank_reference,
            'bank': bank.id,
            'party': start.party.id if start.party else None,
            'company': start.company.id,
            'currency': currency.id,
            'start_date': start.start_date,
            'description': start.description,
        }])
        presentation, = Presentation.create([{
            'trade_finance': trade_finance.id,
            'presentation_date': start.presentation_date,
            'presentation_type': start.presentation_type,
            'finance_stage': start.finance_stage,
            'financing_type': financing_type.id if financing_type else None,
            'description': start.description,
        }])
        line, = Line.create([{
            'presentation': presentation.id,
            'facility_limit': facility_limit.id,
            'movement_date': start.presentation_date,
            'finance_stage': start.finance_stage,
            'signed_quantity': start.signed_quantity,
            'uom': start.uom.id if start.uom else None,
            'commodity_price': start.commodity_price,
            'gross_collateral_value': start.gross_collateral_value,
            'haircut_percent_applied': start.haircut_percent_applied,
            'financed_amount': start.financed_amount,
            'signed_financed_amount': start.signed_financed_amount,
            'currency': currency.id,
            'value_date': start.value_date,
            'maturity_date': start.maturity_date,
            'description': start.description,
        }])
        if start.post:
            Presentation.post([presentation])
        else:
            line.apply_limit_values()
            Line.save([line])

        action['res_id'] = [trade_finance.id]
        action['views'].reverse()
        return action, {}
