# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2005-2009 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
##
""" Stock Decrease wizard definition """

from decimal import Decimal

import gtk
from kiwi.currency import currency
from kiwi.ui.objectlist import Column
from storm.expr import And, Eq

from stoqlib.api import api
from stoqlib.domain.costcenter import CostCenter
from stoqlib.domain.fiscal import CfopData
from stoqlib.domain.payment.group import PaymentGroup
from stoqlib.domain.payment.method import PaymentMethod
from stoqlib.domain.person import Branch, Employee, Person
from stoqlib.domain.product import Product
from stoqlib.domain.sellable import Sellable
from stoqlib.domain.stockdecrease import StockDecrease, StockDecreaseItem
from stoqlib.domain.views import ProductWithStockBranchView
from stoqlib.lib.formatters import format_quantity, format_sellable_description
from stoqlib.lib.message import yesno
from stoqlib.lib.parameters import sysparam
from stoqlib.lib.pluginmanager import get_plugin_manager
from stoqlib.lib.translation import stoqlib_gettext
from stoqlib.gui.base.dialogs import run_dialog
from stoqlib.gui.base.wizards import WizardEditorStep, BaseWizard
from stoqlib.gui.dialogs.batchselectiondialog import BatchDecreaseSelectionDialog
from stoqlib.gui.dialogs.missingitemsdialog import (get_missing_items,
                                                    MissingItemsDialog)
from stoqlib.gui.editors.stockdecreaseeditor import StockDecreaseItemEditor
from stoqlib.gui.events import (StockDecreaseWizardFinishEvent, InvoiceSetupEvent,
                                WizardAddSellableEvent,
                                StockOperationPersonValidationEvent)
from stoqlib.gui.utils.printing import print_report
from stoqlib.gui.wizards.abstractwizard import SellableItemStep
from stoqlib.gui.wizards.salewizard import PaymentMethodStep
from stoqlib.reporting.stockdecrease import StockDecreaseReceipt

_ = stoqlib_gettext


#
# Wizard Steps
#


class StartStockDecreaseStep(WizardEditorStep):
    gladefile = 'StartStockDecreaseStep'
    model_type = StockDecrease
    proxy_widgets = [
        'confirm_date',
        'branch',
        'reason',
        'removed_by',
        'cfop',
        'cost_center',
        'person']

    def _fill_employee_combo(self):
        employess = self.store.find(Employee)
        self.removed_by.prefill(api.for_person_combo(employess))

    def _fill_branch_combo(self):
        branches = Branch.get_active_branches(self.store)
        self.branch.prefill(api.for_person_combo(branches))
        sync_mode = api.sysparam.get_bool('SYNCHRONIZED_MODE')
        self.branch.set_sensitive(not sync_mode)

    def _fill_cfop_combo(self):
        cfops = CfopData.get_for_sale(self.store)
        self.cfop.prefill(api.for_combo(cfops))

    def _fill_cost_center_combo(self):
        cost_centers = CostCenter.get_active(self.store)

        # we keep this value because each call to is_empty() is a new sql query
        # to the database
        cost_centers_exists = not cost_centers.is_empty()

        if cost_centers_exists:
            self.cost_center.prefill(api.for_combo(cost_centers, attr='name',
                                                   empty=_('No cost center.')))
        self.cost_center.set_visible(cost_centers_exists)
        self.cost_center_lbl.set_visible(cost_centers_exists)

    def _fill_person_combo(self):
        items = Person.get_items(self.store,
                                 Person.branch != api.get_current_branch(self.store))
        self.person.prefill(items)

    def _setup_widgets(self):
        self.confirm_date.set_sensitive(False)
        self._fill_employee_combo()
        self._fill_branch_combo()
        self._fill_cfop_combo()
        self._fill_cost_center_combo()
        self._fill_person_combo()

        manager = get_plugin_manager()
        nfe_is_active = manager.is_active('nfe')
        self.person.set_property('mandatory', nfe_is_active)

        if not sysparam.get_bool('CREATE_PAYMENTS_ON_STOCK_DECREASE'):
            self.create_payments.hide()

    #
    # WizardStep hooks
    #

    def post_init(self):
        self.confirm_date.grab_focus()
        self.table1.set_focus_chain([self.confirm_date, self.branch,
                                     self.removed_by, self.reason, self.cfop,
                                     self.person])
        self.register_validate_function(self.wizard.refresh_next)
        self.force_validation()

    def next_step(self):
        self.wizard.create_payments = self.create_payments.read()
        return DecreaseItemStep(self.wizard, self, self.store, self.model)

    def has_previous_step(self):
        return False

    def setup_proxies(self):
        self._setup_widgets()
        self.proxy = self.add_proxy(self.model, self.proxy_widgets)

    #
    # Callbacks
    #

    def on_branch__validate(self, widget, branch):
        return StockOperationPersonValidationEvent.emit(branch.person, type(branch))

    def on_person__validate(self, widget, person_id):
        person = self.store.get(Person, person_id)
        return StockOperationPersonValidationEvent.emit(person, type(person))


class DecreaseItemStep(SellableItemStep):
    """ Wizard step for purchase order's items selection """
    model_type = StockDecrease
    item_table = StockDecreaseItem
    sellable_view = ProductWithStockBranchView
    summary_label_text = "<b>%s</b>" % api.escape(_('Total quantity:'))
    summary_label_column = 'quantity'
    sellable_editable = False
    validate_stock = True
    batch_selection_dialog = BatchDecreaseSelectionDialog
    item_editor = StockDecreaseItemEditor

    #
    # SellableItemStep
    #

    def post_init(self):
        self.hide_add_button()
        manager = get_plugin_manager()
        nfe_is_active = manager.is_active('nfe')
        if not self.wizard.create_payments and not nfe_is_active:
            self.cost_label.hide()
            self.cost.hide()
            self.cost.update(0)

        self.slave.klist.connect('cell-editing-started',
                                 self._on_klist__cell_editing_started)

        super(DecreaseItemStep, self).post_init()

    def get_sellable_view_query(self):
        # The stock quantity of consigned products can not be
        # decreased manually. See bug 5212.
        query = And(Eq(Product.consignment, False),
                    self.sellable_view.branch_id == self.model.branch_id,
                    Sellable.get_available_sellables_query(self.store))
        return self.sellable_view, query

    def get_order_item(self, sellable, cost, quantity, batch=None, parent=None):
        item = self.model.add_sellable(sellable, cost, quantity, batch=batch)
        WizardAddSellableEvent.emit(self.wizard, item)
        return item

    def get_saved_items(self):
        return self.model.get_items()

    def get_columns(self):
        columns = [
            Column('sellable.code', title=_('Code'), data_type=str),
            Column('sellable.description', title=_('Description'),
                   data_type=str, expand=True, searchable=True,
                   format_func=self._format_description, format_func_data=True),
            Column('sellable.category_description', title=_('Category'),
                   data_type=str, expand=True, searchable=True),
            Column('quantity', title=_('Quantity'), data_type=Decimal,
                   format_func=format_quantity),
            Column('sellable.unit_description', title=_('Unit'), data_type=str,
                   width=70),
        ]

        if self.wizard.create_payments:
            columns.extend([
                Column('cost', title=_('Cost'), data_type=currency),
                Column('total', title=_('Total'), data_type=currency),
            ])

        return columns

    def has_next_step(self):
        return self.wizard.create_payments

    def next_step(self):
        if not self.wizard.create_payments:
            return

        group = PaymentGroup(store=self.store)
        self.model.group = group
        return PaymentMethodStep(self.wizard, self, self.store, self.model,
                                 PaymentMethod.get_by_name(self.store, u'multiple'),
                                 finish_on_total=False)

    def validate(self, value):
        for item in self.model.get_items():
            if item.quantity == 0:
                value = False
                break

        super(DecreaseItemStep, self).validate(value)

    #
    # Private
    #

    def _format_description(self, item, data):
        return format_sellable_description(item.sellable, item.batch)

    #
    # Callbacks
    #

    def _on_klist__cell_editing_started(self, klist, obj, attr,
                                        renderer, editable):
        if attr == 'quantity':
            adjustment = editable.get_adjustment()
            remaining_quantity = self.get_remaining_quantity(obj.sellable,
                                                             obj.batch)
            adjustment.set_upper(obj.quantity + remaining_quantity)


class StockDecreaseWizard(BaseWizard):
    size = (775, 400)
    title = _('Manual Stock Decrease')
    need_cancel_confirmation = True

    def __init__(self, store):
        model = self._create_model(store)

        first_step = StartStockDecreaseStep(store, self, model)
        BaseWizard.__init__(self, store, first_step, model)
        self.create_payments = False

    def _create_model(self, store):
        branch = api.get_current_branch(store)
        user = api.get_current_user(store)
        employee = user.person.employee
        cfop_id = sysparam.get_object_id('DEFAULT_STOCK_DECREASE_CFOP')
        return StockDecrease(responsible=user,
                             removed_by=employee,
                             branch=branch,
                             status=StockDecrease.STATUS_INITIAL,
                             cfop_id=cfop_id,
                             store=store)

    def _receipt_dialog(self):
        msg = _('Would you like to print a receipt?')
        if yesno(msg, gtk.RESPONSE_YES,
                 _("Print receipt"), _("Don't print")):
            print_report(StockDecreaseReceipt, self.model)

    #
    # WizardStep hooks
    #

    def finish(self):
        missing = get_missing_items(self.model, self.store)
        if missing:
            run_dialog(MissingItemsDialog, self, self.model, missing)
            return False

        invoice_ok = InvoiceSetupEvent.emit()
        if invoice_ok is False:
            # If there is any problem with the invoice, the event will display an error
            # message and the dialog is kept open so the user can fix whatever is wrong.
            return

        self.retval = self.model
        self.model.confirm()
        self.close()

        StockDecreaseWizardFinishEvent.emit(self.model)
        # Commit before printing to avoid losing data if something breaks
        self.store.confirm(self.retval)
        self._receipt_dialog()
