[POS-commit] r1127 - in stoq/trunk/stoq: . domain examples gui/editors gui/editors/glade gui/pos gui/till gui/till/glade lib

Henrique Romano henrique at async.com.br
Mon Sep 5 10:21:50 BRT 2005


Author: henrique
Date: Mon Sep  5 10:21:49 2005
New Revision: 1127

Added:
   stoq/trunk/stoq/domain/payment.py
   stoq/trunk/stoq/examples/sale.py
   stoq/trunk/stoq/gui/editors/glade/TillClosing.glade
   stoq/trunk/stoq/gui/editors/glade/TillOpening.glade
   stoq/trunk/stoq/gui/editors/till.py
   stoq/trunk/stoq/gui/till/
   stoq/trunk/stoq/gui/till/__init__.py
   stoq/trunk/stoq/gui/till/app.py
   stoq/trunk/stoq/gui/till/glade/
   stoq/trunk/stoq/gui/till/glade/till.glade
   stoq/trunk/stoq/gui/till/till.py
Modified:
   stoq/trunk/stoq/domain/interfaces.py
   stoq/trunk/stoq/domain/sale.py
   stoq/trunk/stoq/domain/tables.py
   stoq/trunk/stoq/examples/createall.py
   stoq/trunk/stoq/gui/pos/pos.py
   stoq/trunk/stoq/lib/parameters.py
   stoq/trunk/stoq/main.py
Log:

Fix for bug #2081: Initial implementation of Till application.

r=evandro



Modified: stoq/trunk/stoq/domain/interfaces.py
==============================================================================
--- stoq/trunk/stoq/domain/interfaces.py	(original)
+++ stoq/trunk/stoq/domain/interfaces.py	Mon Sep  5 10:21:49 2005
@@ -351,6 +351,44 @@
                                'comission. This is a reference to another '
                                'object')
 
+
+class IInPayment(ConnInterface):
+    """ Interface specification for InPayments. """
+
+    def receive(value=None, paid_date=None):
+        """ Confirm the payment. """
+
+
+class IOutPayment(ConnInterface):
+    """ Interface specification for OutPayments. """
+
+    def pay(value=None, paid_date=None):
+        """ Confirm the payment."""
+
+
+class IPaymentGroup(ConnInterface):
+    """ Interface specification for PaymentGroups. """
+
+    status = Attribute('status',
+                       'int',
+                       'The status of the payment group. ')
+    open_date = Attribute('open_date',
+                          'datetime',
+                          'The open date of the payment group.')
+    close_date = Attribute('close_date',
+                           'datetime',
+                           'The close date of the payment group.')
+    notes = Attribute('notes',
+                      'str',
+                      'Extra notes for the payment group.')
+    payments = Attribute('payments',
+                         'list',
+                         'A list of payments associated to this payment '
+                         'group') 
+    thirdparty = Attribute('thirdparty',
+                           'Person',
+                           'The thirdparty associated to this payment group.')
+
 class IDelivery(ConnInterface):
     """ Specification of a Delivery interface for a sellable. """
 

Added: stoq/trunk/stoq/domain/payment.py
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/domain/payment.py	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,304 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2005 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 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):    Henrique Romano <henrique at async.com.br>
+"""
+stoq/domain/payment.py:
+
+   Implementation of classes related to Payment management.
+"""
+
+from datetime import datetime
+import operator
+
+from sqlobject import (IntCol, DateTimeCol, FloatCol, StringCol, 
+                       ForeignKey)
+from sqlobject.sqlbuilder import AND
+from stoqlib.exceptions import PaymentError, TillError
+
+from stoq.domain.base import (Domain, ModelAdapter,
+                              InheritableModelAdapter)
+from stoq.domain.interfaces import IInPayment, IOutPayment, IPaymentGroup
+from stoq.domain.interfaces import IBranch, IContainer
+from stoq.domain.person import Person
+from stoq.lib.parameters import sysparam
+
+
+#
+# Domain Classes
+# 
+
+class Payment(Domain):
+    (STATUS_PREVIEW, STATUS_TO_PAY, STATUS_PAID, STATUS_REVIEWING,
+     STATUS_CONFIRMED, STATUS_CANCELLED) = range(6)
+
+    status = IntCol(default=STATUS_PREVIEW)
+    due_date = DateTimeCol()
+    paid_date = DateTimeCol(default=None)
+    paid_value = FloatCol(default=None)
+    value = FloatCol()
+    description = StringCol(default=None)
+
+    method = ForeignKey('PaymentMethod')
+    group = ForeignKey('AbstractPaymentGroup')
+    # XXX: It will not be implemented for now.
+#    destination = ForeignKey('Account')
+    
+    def is_to_pay(self):
+        return self.status == self.STATUS_TO_PAY
+
+    def pay(self, value=None, paid_date=None):
+        if self.group.get_thirdparty() is None:
+            raise PaymentError("You must have a thirdparty to quit "
+                               "the payment")
+
+        self.paid_value = value and value or self.value
+        self.paid_date = paid_date and paid_date or datetime.now()
+        self.status = self.STATUS_PAID
+
+    def cancel(self):
+        if self.status == self.STATUS_CANCELLED:
+            raise PaymentError("This payment is already cancelled")
+
+        self.status = self.STATUS_CANCELLED
+
+
+class PaymentMethod(Domain):
+    description = StringCol()
+
+
+class Till(Domain):
+    STATUS_PENDING, STATUS_OPEN, STATUS_CLOSED = range(3)
+
+    status = IntCol(default=STATUS_PENDING)
+    balance_sent = FloatCol(default=None)
+    initial_cash_amount = FloatCol(default=0.0)
+    final_cash_amount = FloatCol(default=None)
+    opening_date = DateTimeCol(default=datetime.now())
+    closing_date = DateTimeCol(default=None)
+
+    branch = ForeignKey(Person.getAdapterClass(IBranch).__name__)
+
+    def get_balance(self):
+        """ Return the total of all "extra" payments (like cash
+        advance, till complement, ...) associated to this till
+        movimentation *plus* all the payments, which payment method is
+        money, of all the sales associated with this movimentation 
+        *plus* the initial cash amount. """
+
+        from stoq.domain.sale import Sale
+
+        conn = self.get_connection()
+
+        query = AND(Sale.q.status == Sale.STATUS_CONFIRMED,
+                    Sale.q.tillID == self.id)
+        result = Sale.select(query, connection=conn)
+        payments = []
+        money_payment_method = sysparam(conn).MONEY_PAYMENT_METHOD
+        for sale in result:
+            sale_pg_facet = IPaymentGroup(sale)
+            assert sale_pg_facet, ("The sale associated to this "
+                                   "till movimentation don't have "
+                                   "a PaymentGroup facet.")
+            payments.extend([p for p in sale_pg_facet.get_items() 
+                                 if p.method == money_payment_method])
+        pg_facet = IPaymentGroup(self, connection=conn)
+        if pg_facet:
+            payments.extend(pg_facet.get_items())
+
+        total = reduce(operator.add, [p.value for p in payments], 0.0)
+        return total + self.initial_cash_amount
+    
+    def open_till(self, opening_date=datetime.now(), initial_cash_amount=0.0):
+        if not initial_cash_amount:
+            last_till = get_last_till_movimentation(self.get_connection())
+            if last_till:
+                self.initial_cash_amount = last_till.final_cash_amount
+        self.opening_date = opening_date
+        self.status = self.STATUS_OPEN
+
+    def close_till(self, balance_to_send=0.0, closing_date=datetime.now()):
+        if self.status != Till.STATUS_OPEN:
+            raise ValueError("This till is already closed. Open a new till "
+                             "before close it.")
+
+        from stoq.domain.sale import Sale
+
+        conn = self.get_connection()
+        sales = Sale.selectBy(till=self, connection=conn)
+
+        money_payment_method = sysparam(conn).MONEY_PAYMENT_METHOD
+        for sale in sales:
+            for payment in IPaymentGroup(sale).get_items():
+                if payment.method is money_payment_method:
+                    payment.status = Payment.STATUS_REVIEWING
+                else:
+                    payment.status = Payment.STATUS_TO_PAY
+
+        current_balance = self.get_balance()
+        if balance_to_send and balance_to_send > current_balance:
+            raise ValueError("The cash amount that you want to send is "
+                             "greater than the current balance.")
+        self.status = self.STATUS_CLOSED
+        self.closing_date = closing_date
+        self.final_cash_amount = current_balance - balance_to_send
+        self.balance_sent = balance_to_send
+
+
+#
+# Adapters
+#
+
+class PaymentAdaptToInPayment(ModelAdapter):
+
+    __implements__ = IInPayment
+
+    def receive(self):
+        payment = self.get_adapted()
+        if not payment.is_to_pay():
+            raise ValueError("This payment is already received.")
+        payment.pay()
+
+Payment.registerFacet(PaymentAdaptToInPayment)
+
+
+class PaymentAdaptToOutPayment(ModelAdapter):
+
+    __implements__ = IOutPayment
+
+    def pay(self):
+        payment = self.get_adapted()
+        if not payment.is_to_pay():
+            raise ValueError("This payment is already paid.")
+        payment.pay()
+
+Payment.registerFacet(PaymentAdaptToOutPayment)
+
+
+class AbstractPaymentGroup(InheritableModelAdapter):
+    STATUS_PREVIEW, STATUS_OPEN, STATUS_CLOSED, STATUS_CANCELLED = range(4)
+
+    __implements__ = IPaymentGroup, IContainer
+
+    status = IntCol(default=STATUS_OPEN)
+    open_date = DateTimeCol(default=datetime.now())
+    close_date = DateTimeCol(default=None)
+    notes = StringCol(default='')
+    thirdparty = ForeignKey('Person')
+
+
+    def set_thirdparty(self, person):
+        if not isinstance(person, Person):
+            raise TypeError("A Person object is required for set_thirdparty, "
+                            "got %s instead." % type(person))
+        self.thirdparty = person
+
+    def get_thirdparty(self):
+        return self.thirdparty
+
+    def get_balance(self):
+        values = [s.value for s in self.get_items()]
+        return reduce(operator.add, values, 0.0)
+
+    def add_debit(self, value, reason, category, date=None):
+        payment = self.create_payment(value, reason, category, date)
+
+        return payment.addFacet(IOutPayment)
+
+    def add_credit(self, value, reason, category, date=None):
+        payment = self.create_payment(value, reason, category, date)
+        
+        return payment.addFacet(IInPayment)
+
+    #
+    # Helper methods
+    #
+
+    def create_payment(self, value, reason, category, date=None):
+        date = date or datetime.now()
+        payment = Payment(due_date=date, value=value, description=reason,
+                          category=category, group=self)
+        self.add_item(payment)
+
+        return payment
+
+    #
+    # IPaymentGroup implementation
+    #
+
+    def add_item(self, payment):
+        payment.group = self
+
+    def remove_item(self, payment):
+        if not isinstance(payment, Payment):
+            raise TypeError("A Payment object is required for remove_item, "
+                            "got %s instead." % type(payment))
+
+        Payment.delete(payment.id, connection=self.get_connection())
+
+    def get_items(self):
+        result = Payment.selectBy(group=self, 
+                                   connection=self.get_connection())
+        return list(result)
+
+
+class TillAdaptToPaymentGroup(AbstractPaymentGroup):
+    __implements__ = IPaymentGroup
+
+    def add_complement(self, value, reason, category, date=None):
+        # TODO: implement this method
+        pass
+
+    def get_cash_advance(self, value, reason, category, employee, date=None):
+        # TODO: implement this method
+        pass
+
+    def get_cancel_payment(self, payment):
+        # TODO: implement this method
+        pass
+
+Till.registerFacet(TillAdaptToPaymentGroup)
+
+#
+# Functions
+#
+
+def get_current_till_movimentation(conn):
+    result = Till.select(Till.q.status == Till.STATUS_OPEN, connection=conn)
+    if result.count() > 1:
+        raise TillError("You should have only one Till opened. Got %d "
+                        "instead." % result.count())
+    elif result.count() == 0:
+        return None
+
+    return result[0]
+
+
+def get_last_till_movimentation(conn):
+    """  The last till movimentation is used to get a initial cash amount
+    to a new till movimentation that will be created, this value is based
+    on the final_cash_amount attribute of the last till movimentation """
+
+    query = AND(Till.q.status == Till.STATUS_CLOSED, 
+                Till.q.branchID == sysparam(conn).CURRENT_BRANCH.id)
+    result = Till.select(query, connection=conn)
+    return result.count() and result[-1] or None

Modified: stoq/trunk/stoq/domain/sale.py
==============================================================================
--- stoq/trunk/stoq/domain/sale.py	(original)
+++ stoq/trunk/stoq/domain/sale.py	Mon Sep  5 10:21:49 2005
@@ -21,6 +21,7 @@
 ## USA.
 ##
 ## Author(s):   Evandro Vale Miquelito      <evandro at async.com.br>
+##              Henrique Romano             <henrique at async.com.br>
 ##
 """
 stoq/domain/sale.py:
@@ -28,18 +29,24 @@
     Sale object and related objects implementation.
 """
 
-from sqlobject import ForeignKey
+import gettext
+
+from datetime import datetime
+
 from twisted.python.components import implements
+from sqlobject import StringCol, DateTimeCol, ForeignKey, IntCol, FloatCol
 
 from stoq.domain.base import Domain
 from stoq.domain.interfaces import IContainer, ISellable, IClient
 from stoq.domain.person import Person
 from stoq.lib.runtime import get_connection
 from stoq.domain.sellable import AbstractSellableItem
+from stoq.domain.payment import AbstractPaymentGroup
 
 __connection__ = get_connection()
 
 
+_ = gettext.gettext
 
 #
 # Base Domain Classes
@@ -64,7 +71,29 @@
         if not client:
             raise TypeError("%s cannot be adapted to IClient." % person)
         self.client = client
-    
+    (STATUS_OPENED, 
+     STATUS_CONFIRMED, 
+     STATUS_CLOSED, 
+     STATUS_CANCELLED,
+     STATUS_REVIEWING) = range(5)
+
+    statuses_name = {STATUS_OPENED: _("Opened"),
+                     STATUS_CONFIRMED: _("Confirmed"),
+                     STATUS_CLOSED: _("Closed"),
+                     STATUS_CANCELLED: _("Cancelled"),
+                     STATUS_REVIEWING: _("Reviewing")}
+
+    code = StringCol(default=None)
+    open_date = DateTimeCol(default=datetime.today())
+    close_date = DateTimeCol(default=None)
+    client = ForeignKey('PersonAdaptToClient')
+    status = IntCol(default=STATUS_OPENED)
+    total = FloatCol(default=0.0)
+    till = ForeignKey('Till')
+
+    def get_status_name(self):
+        return self.statuses_name[self.status]
+
     #
     # IContainer methods
     #
@@ -84,3 +113,15 @@
         conn = self.get_connection()
         table = type(item)
         table.delete(item.id, connection=conn)
+
+#
+# Adapters
+#
+
+
+class SaleAdaptToPaymentGroup(AbstractPaymentGroup):
+    pass
+
+Sale.registerFacet(SaleAdaptToPaymentGroup)
+
+

Modified: stoq/trunk/stoq/domain/tables.py
==============================================================================
--- stoq/trunk/stoq/domain/tables.py	(original)
+++ stoq/trunk/stoq/domain/tables.py	Mon Sep  5 10:21:49 2005
@@ -69,8 +69,16 @@
                                   "PersonAdaptToBranch",
                                   "PersonAdaptToSalesPerson",
                                   )),
+     ('stoq.domain.payment',    ("AbstractPaymentGroup",
+                                 "PaymentMethod",
+                                 "Payment",
+                                 "Till",
+                                 "PaymentAdaptToInPayment",
+                                 "PaymentAdaptToOutPayment",
+                                 "TillAdaptToPaymentGroup",
+                                 )),
      ('stoq.domain.sale',       ("Sale",
-                                  )),
+                                 "SaleAdaptToPaymentGroup")),
      ('stoq.domain.sellable',    ("AbstractSellableCategory",
                                   "BaseSellableCategory",
                                   "SellableCategory", 

Modified: stoq/trunk/stoq/examples/createall.py
==============================================================================
--- stoq/trunk/stoq/examples/createall.py	(original)
+++ stoq/trunk/stoq/examples/createall.py	Mon Sep  5 10:21:49 2005
@@ -30,13 +30,14 @@
 from stoq.examples.person import create_persons
 from stoq.examples.product import create_products
 from stoq.examples.service import create_services
-
+from stoq.examples.sale import create_sales
 
 if __name__ == "__main__":
     print 'Creating example database...'
     create_persons()
     create_products()
     create_services()
+    create_sales()
     print '-'*40
     print 'done.'
 

Added: stoq/trunk/stoq/examples/sale.py
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/examples/sale.py	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,134 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2005 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 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):        Henrique Romano <henrique at async.com.br>
+##
+"""
+stoq/examples/sale.py
+
+    Create a simple sale to an example database.
+"""
+
+import gettext
+import sys
+from datetime import datetime, timedelta
+
+from stoqlib.exceptions import SellError
+
+from stoq.lib.runtime import new_transaction
+from stoq.domain.payment import get_current_till_movimentation
+from stoq.domain.payment import Payment, Till
+from stoq.domain.sale import Sale
+from stoq.domain.product import Product
+from stoq.domain.interfaces import ISellable, IClient, IPaymentGroup
+from stoq.domain.person import Person
+from stoq.lib.parameters import sysparam
+
+_ = gettext.gettext
+
+# Number of installments for the sale
+DEFAULT_PAYMENTS_NUMBER = 4
+
+# Interval between payments (in days)
+DEFAULT_PAYMENTS_INTERVAL = 30
+
+# Number of sales to be created
+DEFAULT_SALE_NUMBER = 4
+
+def get_till(conn):
+    till = get_current_till_movimentation(conn)
+    if till is None:
+        till = Till(connection=conn, 
+                    branch=sysparam(conn).CURRENT_BRANCH)
+        till.open_till()
+
+    return till
+
+def get_clients(conn):
+    client_table = Person.getAdapterClass(IClient)
+    result = client_table.select(connection=conn)
+    if result.count() <= 0:
+        raise SellError("You must have clients to create a sale!")
+    return list(result)
+
+def get_all_products(conn):
+    result = Product.select(connection=conn)
+    if result.count() <= 0:
+        raise SellError("You have nothing to sale!")
+        sys.exit()
+    return list(result)
+
+#
+# Main
+#
+
+def create_sales():
+    conn = new_transaction()
+    print "Creating Sale... ",
+
+    till = get_till(conn)
+
+    clients = get_clients(conn)
+    if not len(clients) >= DEFAULT_SALE_NUMBER:
+        raise SellError("You don't have clients to create all the sales.")
+
+    product_list = get_all_products(conn)
+    if not len(product_list) >= DEFAULT_SALE_NUMBER:
+        raise SellError("You don't have products to create all the sales.")
+
+    payment_method = sysparam(conn).MONEY_PAYMENT_METHOD
+
+    for i in range(DEFAULT_SALE_NUMBER):
+        #
+        # Setting up the items
+        #
+        sale = Sale(connection=conn, till=till, client=clients[i], 
+                    code='#%03d' % (i + 1))
+        sellable_facet = ISellable(product_list[i], connection=conn)
+        sellable_facet.add_sellable_item(sale=sale, quantity=1,
+                                         base_price=sellable_facet.price,
+                                         price=sellable_facet.price)
+
+        sale.total = sellable_facet.price
+
+        #
+        # Setting up the payments
+        #
+        pg_facet = sale.addFacet(IPaymentGroup, connection=conn,
+                                 thirdparty=clients[i].get_adapted())
+        each_payment = sale.total / DEFAULT_PAYMENTS_NUMBER
+        due_date = datetime.now()
+        for i in range(DEFAULT_PAYMENTS_NUMBER):
+            payment = Payment(due_date=due_date, value=each_payment,
+                              connection=conn,method=payment_method,
+                              group=pg_facet)
+            pg_facet.add_item(payment)
+            due_date += timedelta(days=DEFAULT_PAYMENTS_INTERVAL)
+
+    conn.commit()
+
+    print "done."
+
+
+if __name__ == '__main__':
+    create_sales()
+

Added: stoq/trunk/stoq/gui/editors/glade/TillClosing.glade
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/editors/glade/TillClosing.glade	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,116 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface>
+    <widget class="GtkWindow" id="TillClosing">
+        <property name="default_height">250</property>
+        <property name="default_width">440</property>
+        <property name="title" context="yes" translatable="yes">Till Closing</property>
+        <child>
+            <widget class="GtkTable" id="table1">
+                <property name="column_spacing">5</property>
+                <property name="row_spacing">5</property>
+                <property name="visible">True</property>
+                <child>
+                    <placeholder/>
+                    <packing>
+                        <property name="left_attach">2</property>
+                        <property name="right_attach">3</property>
+                    </packing>
+                </child>
+                <child>
+                    <placeholder/>
+                    <packing>
+                        <property name="left_attach">3</property>
+                        <property name="right_attach">4</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="GtkLabel" id="label1">
+                        <property name="label" context="yes" translatable="yes">Closing Date:</property>
+                        <property name="visible">True</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="GtkLabel" id="label2">
+                        <property name="label" context="yes" translatable="yes">Balance To Send:</property>
+                        <property name="visible">True</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="bottom_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="kiwi+ui+widgets+entry+Entry" id="balance_to_send">
+                        <property name="data_type">float</property>
+                        <property name="model_attribute">balance_sent</property>
+                        <property name="width_chars">11</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="bottom_attach">2</property>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="GtkLabel" id="label3">
+                        <property name="label" context="yes" translatable="yes">Final Balance:</property>
+                        <property name="visible">True</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="bottom_attach">2</property>
+                        <property name="left_attach">2</property>
+                        <property name="right_attach">3</property>
+                        <property name="top_attach">1</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="kiwi+ui+widgets+entry+Entry" id="final_cash_amount">
+                        <property name="data_type">float</property>
+                        <property name="model_attribute">final_cash_amount</property>
+                        <property name="width_chars">11</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="bottom_attach">2</property>
+                        <property name="left_attach">3</property>
+                        <property name="right_attach">4</property>
+                        <property name="top_attach">1</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="kiwi+ui+widgets+entry+Entry" id="closing_date">
+                        <property name="data_type">date</property>
+                        <property name="is_focus">True</property>
+                        <property name="model_attribute">closing_date</property>
+                        <property name="width_chars">10</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+            </widget>
+        </child>
+    </widget>
+</glade-interface>

Added: stoq/trunk/stoq/gui/editors/glade/TillOpening.glade
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/editors/glade/TillOpening.glade	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,71 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface>
+    <widget class="GtkWindow" id="TillOpening">
+        <property name="default_height">250</property>
+        <property name="default_width">440</property>
+        <property name="title" context="yes" translatable="yes">Till Opening</property>
+        <child>
+            <widget class="GtkTable" id="table1">
+                <property name="column_spacing">5</property>
+                <property name="visible">True</property>
+                <child>
+                    <widget class="GtkLabel" id="label1">
+                        <property name="label" context="yes" translatable="yes"> Date:</property>
+                        <property name="visible">True</property>
+                    </widget>
+                    <packing>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="GtkLabel" id="label2">
+                        <property name="justify">right</property>
+                        <property name="label" context="yes" translatable="yes"> Initial Cash Amount:</property>
+                        <property name="visible">True</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="left_attach">2</property>
+                        <property name="right_attach">3</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="kiwi+ui+widgets+entry+Entry" id="open_date">
+                        <property name="data_type">date</property>
+                        <property name="has_focus">True</property>
+                        <property name="is_focus">True</property>
+                        <property name="mandatory">True</property>
+                        <property name="model_attribute">opening_date</property>
+                        <property name="width_chars">10</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="kiwi+ui+widgets+entry+Entry" id="initial_cash_amount">
+                        <property name="data_type">float</property>
+                        <property name="mandatory">True</property>
+                        <property name="model_attribute">initial_cash_amount</property>
+                        <property name="width_chars">12</property>
+                        <property name="xalign">1.0</property>
+                    </widget>
+                    <packing>
+                        <property name="left_attach">3</property>
+                        <property name="right_attach">4</property>
+                        <property name="x_options">fill</property>
+                        <property name="y_options">fill</property>
+                    </packing>
+                </child>
+            </widget>
+        </child>
+    </widget>
+</glade-interface>

Added: stoq/trunk/stoq/gui/editors/till.py
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/editors/till.py	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,120 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2005 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 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):        Henrique Romano <henrique at async.com.br>
+##
+"""
+stoq/gui/editors:
+
+    Editors implementation for open/close operation on till movimentation.
+"""
+
+import gettext
+
+from stoqlib.gui.editors import BaseEditor
+from kiwi.datatypes import ValidationError
+
+from stoq.domain.payment import Till
+
+_ = gettext.gettext
+
+class TillOpeningEditor(BaseEditor):
+    gladefile = 'TillOpening'
+    widgets = ('open_date', 
+               'initial_cash_amount')
+    model_type = Till
+    title = _('Till Opening')
+
+    def __init__(self, conn, model):
+        BaseEditor.__init__(self, conn, model)
+
+    def _setup_widgets(self):
+        self.initial_cash_amount.set_data_format('%.2f')
+
+    #
+    # BaseEditor hooks
+    # 
+
+    def setup_proxies(self):
+        self.model.open_till()
+        self._setup_widgets()
+        self.add_proxy(self.model, self.widgets)
+
+
+class TillClosingEditor(BaseEditor):
+    gladefile = 'TillClosing'
+    widgets = ('closing_date',
+               'final_cash_amount',
+               'balance_to_send')
+    model_type = Till
+    title = _('Till Closing')
+
+    def __init__(self, conn, model):
+        BaseEditor.__init__(self, conn, model)
+        self.total_balance = model.get_balance()
+
+    def _setup_widgets(self):
+        for widget in (self.balance_to_send, self.final_cash_amount):
+            widget.set_data_format('%.2f')
+
+    def update_final_cash_amount(self):
+        balance_to_send = self.model.balance_sent or 0.0
+        self.model.final_cash_amount = self.total_balance - balance_to_send
+        self.proxy.update('final_cash_amount')
+
+    def update_balance_to_send(self):
+        final_cash_amount = self.model.final_cash_amount or 0.0
+        self.model.balance_sent = self.total_balance - final_cash_amount 
+        self.proxy.update('balance_sent')
+
+    #
+    # BaseEditor hooks
+    # 
+
+    def setup_proxies(self):
+        self.model.close_till()
+        self.final_cash = self.model.final_cash_amount
+        self._setup_widgets()
+        self.proxy = self.add_proxy(self.model, self.widgets)
+
+    #
+    # Kiwi handlers
+    #
+
+    def after_final_cash_amount__validate(self, widget, value):
+        if value <= self.final_cash:
+            return
+        return ValidationError(_("You can not specifiy a final"
+                                 " cash amount greater than the "
+                                 "calculated value."))
+
+    def after_balance_to_send__changed(self, *args):
+        self.handler_block(self.final_cash_amount, 'changed')
+        self.update_final_cash_amount()
+        self.handler_unblock(self.final_cash_amount, 'changed')
+
+    def after_final_cash_amount__changed(self, *args):
+        self.handler_block(self.balance_to_send, 'changed')
+        self.update_balance_to_send()
+        self.handler_unblock(self.balance_to_send, 'changed')
+
+

Modified: stoq/trunk/stoq/gui/pos/pos.py
==============================================================================
--- stoq/trunk/stoq/gui/pos/pos.py	(original)
+++ stoq/trunk/stoq/gui/pos/pos.py	Mon Sep  5 10:21:49 2005
@@ -40,14 +40,15 @@
 from stoqlib.gui.dialogs import notify_dialog
 
 from stoq.gui.application import AppWindow
-from stoq.lib.runtime import get_current_user, new_transaction
+from stoq.lib.runtime import new_transaction
 from stoq.lib.validators import format_quantity
 from stoq.lib.parameters import sysparam
 from stoq.domain.sellable import AbstractSellable, get_formatted_price
 from stoq.domain.sale import Sale
-from stoq.domain.product import ProductSellableItem
-from stoq.domain.interfaces import ISellable, IClient
 from stoq.domain.service import ServiceSellableItem
+from stoq.domain.product import ProductSellableItem
+from stoq.domain.payment import get_current_till_movimentation
+from stoq.domain.interfaces import ISellable
 from stoq.gui.editors.product import ProductEditor, ProductItemEditor
 from stoq.gui.editors.delivery import DeliveryEditor
 from stoq.gui.editors.service import ServiceEditor
@@ -84,6 +85,12 @@
     def __init__(self, app):
         AppWindow.__init__(self, app)
         self.conn = new_transaction()
+        if not get_current_till_movimentation(self.conn):
+            notify_dialog(_("You need to open the till before start doing "
+			    "sales."),
+                          _("Error"))
+            self.app.shutdown()
+
         self._setup_slaves()
         self._setup_signals()
         self._setup_proxies()
@@ -181,7 +188,9 @@
             self.order_list.select_instance(self.order_list[0])
 
     def reset_order(self):
-        self.sale = Sale(connection=self.conn, client=None)
+        self.sale = Sale(connection=self.conn, 
+			 till=get_current_till_movimentation(self.conn),
+			 client=None)
         self.person_proxy.new_model(None, relax_type=True)
         self.product_proxy.new_model(None, relax_type=True)
         items = self.order_list[:]

Added: stoq/trunk/stoq/gui/till/__init__.py
==============================================================================

Added: stoq/trunk/stoq/gui/till/app.py
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/till/app.py	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,37 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2005 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 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):       Henrique Romano     <henrique at async.com.br>
+##
+"""
+stoq/gui/till/app.py:
+
+   Main callsite for Till application.
+"""
+
+from stoq.gui.till.till import TillApp
+from stoq.gui.application import App
+
+def main(config):
+    app = App(TillApp, config)
+    app.run()
+

Added: stoq/trunk/stoq/gui/till/glade/till.glade
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/till/glade/till.glade	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,284 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface>
+    <object class="GtkUIManager" id="uimanager">
+        <child>
+            <object class="GtkActionGroup" id="DefaultActions">
+                <child>
+                    <object class="GtkAction" id="ChangeUser">
+                        <property name="name">ChangeUser</property>
+                        <property name="label" translatable="yes">Change User</property>
+                        <property name="stock_id">gtk-refresh</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="ClearCookie">
+                        <property name="name">ClearCookie</property>
+                        <property name="label" translatable="yes">Clear Cookie</property>
+                        <property name="stock_id">gtk-clear</property>
+                        <signal handler="_clear_cookie" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Copy">
+                        <property name="name">Copy</property>
+                        <property name="tooltip">Copy selected object into the clipboard</property>
+                        <property name="stock_id">gtk-copy</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="CurrentTill">
+                        <property name="name">CurrentTill</property>
+                        <property name="label" translatable="yes">Current Till</property>
+                        <property name="stock_id">gtk-justify-fill</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Cut">
+                        <property name="name">Cut</property>
+                        <property name="tooltip">Cut selected object into the clipboard</property>
+                        <property name="stock_id">gtk-cut</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="EditMenu">
+                        <property name="name">EditMenu</property>
+                        <property name="label" translatable="yes">_Edit</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="FileMenu">
+                        <property name="name">FileMenu</property>
+                        <property name="label" translatable="yes">_File</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="HelpAbout">
+                        <property name="name">HelpAbout</property>
+                        <property name="label" translatable="yes">About</property>
+                        <property name="stock_id">gtk-about</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="HelpMenu">
+                        <property name="name">HelpMenu</property>
+                        <property name="label" translatable="yes">Help</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="New">
+                        <property name="name">New</property>
+                        <property name="tooltip">Create a new file</property>
+                        <property name="stock_id">gtk-new</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Open">
+                        <property name="name">Open</property>
+                        <property name="tooltip">Open a file</property>
+                        <property name="stock_id">gtk-open</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Paste">
+                        <property name="name">Paste</property>
+                        <property name="tooltip">Paste object from the Clipboard</property>
+                        <property name="stock_id">gtk-paste</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Quit">
+                        <property name="name">Quit</property>
+                        <property name="tooltip">Quit the program</property>
+                        <property name="stock_id">gtk-quit</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Save">
+                        <property name="name">Save</property>
+                        <property name="tooltip">Save a file</property>
+                        <property name="stock_id">gtk-save</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="SaveAs">
+                        <property name="name">SaveAs</property>
+                        <property name="tooltip">Save with a different name</property>
+                        <property name="stock_id">gtk-save-as</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="StoreCookie">
+                        <property name="name">StoreCookie</property>
+                        <property name="label" translatable="yes">Store Cookie</property>
+                        <property name="stock_id">gtk-save</property>
+                        <signal handler="_store_cookie" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="TillClose">
+                        <property name="name">TillClose</property>
+                        <property name="label" translatable="yes">Close Till</property>
+                        <property name="stock_id">gtk-close</property>
+                        <signal handler="close_till" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="TillMenu">
+                        <property name="name">TillMenu</property>
+                        <property name="label" translatable="yes">Till</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="TillOpen">
+                        <property name="name">TillOpen</property>
+                        <property name="label" translatable="yes">Open Till</property>
+                        <property name="stock_id">gtk-open</property>
+                        <signal handler="open_till" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="quit_action">
+                        <property name="name">quit_action</property>
+                        <property name="tooltip">Quit the program</property>
+                        <property name="stock_id">gtk-quit</property>
+                        <signal handler="_on_quit_action__clicked" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="users_menu">
+                        <property name="name">users_menu</property>
+                        <property name="label" translatable="yes">User</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+        <ui id="initial-state"><![CDATA[<ui><menubar action="menubar" name="menubar">
+  
+  
+<menu action="TillMenu" name="TillMenu"><menuitem action="TillOpen" name="TillOpen"/><menuitem action="TillClose" name="TillClose"/><separator/><menuitem action="quit_action" name="quit_action"/></menu><menu action="users_menu" name="users_menu"><menuitem action="StoreCookie" name="StoreCookie"/><menuitem action="ClearCookie" name="ClearCookie"/><separator/><menuitem action="ChangeUser" name="ChangeUser"/></menu><menu action="HelpMenu" name="HelpMenu"><menuitem action="HelpAbout" name="HelpAbout"/></menu></menubar><toolbar action="toolbar1" name="toolbar1">
+  
+  
+  
+  
+  
+  
+  
+<toolitem action="TillOpen" name="TillOpen"/><toolitem action="TillClose" name="TillClose"/><separator/><toolitem action="CurrentTill" name="CurrentTill"/></toolbar></ui>]]></ui>
+    </object>
+    <widget class="GtkWindow" id="till">
+        <property name="height_request">570</property>
+        <property name="title" context="yes" translatable="yes">STOQ - Till Application</property>
+        <property name="width_request">800</property>
+        <child>
+            <widget class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <child>
+                    <widget constructor="initial-state" class="GtkMenuBar" id="menubar">
+                        <property name="visible">True</property>
+                    </widget>
+                    <packing>
+                        <property name="expand">False</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget constructor="initial-state" class="GtkToolbar" id="toolbar1">
+                        <property name="visible">True</property>
+                    </widget>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                    </packing>
+                </child>
+                <child>
+                    <widget class="GtkVBox" id="vbox4">
+                        <property name="visible">True</property>
+                        <child>
+                            <widget class="GtkEventBox" id="searchbar_holder">
+                                <property name="visible">True</property>
+                                <child>
+                                    <placeholder/>
+                                </child>
+                            </widget>
+                            <packing>
+                                <property name="expand">False</property>
+                            </packing>
+                        </child>
+                        <child>
+                            <widget class="GtkEventBox" id="klist_holder">
+                                <property name="visible">True</property>
+                                <child>
+                                    <placeholder/>
+                                </child>
+                            </widget>
+                            <packing>
+                                <property name="position">1</property>
+                            </packing>
+                        </child>
+                        <child>
+                            <widget class="GtkTable" id="table1">
+                                <property name="border_width">5</property>
+                                <property name="column_spacing">5</property>
+                                <property name="row_spacing">5</property>
+                                <property name="visible">True</property>
+                                <child>
+                                    <widget class="GtkButton" id="confirm_order_button">
+                                        <property name="label">Confirm Order</property>
+                                        <property name="use_stock">True</property>
+                                        <property name="visible">True</property>
+                                    </widget>
+                                    <packing>
+                                        <property name="x_options">fill</property>
+                                        <property name="y_options">fill</property>
+                                    </packing>
+                                </child>
+                                <child>
+                                    <placeholder/>
+                                    <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="x_options">&lt;flags 0 of type GtkAttachOptions&gt;</property>
+                                        <property name="y_options">&lt;flags 0 of type GtkAttachOptions&gt;</property>
+                                    </packing>
+                                </child>
+                                <child>
+                                    <widget class="GtkLabel" id="label2">
+                                        <property name="label" context="yes" translatable="yes">&lt;span size='x-large'&gt;&lt;b&gt;Total: &lt;/b&gt;&lt;/span&gt;</property>
+                                        <property name="use_markup">True</property>
+                                        <property name="visible">True</property>
+                                        <property name="xalign">1.0</property>
+                                    </widget>
+                                    <packing>
+                                        <property name="left_attach">2</property>
+                                        <property name="right_attach">3</property>
+                                        <property name="y_options">fill</property>
+                                    </packing>
+                                </child>
+                                <child>
+                                    <widget class="kiwi+ui+widgets+label+Label" id="total_label">
+                                        <property name="data_type">str</property>
+                                        <property name="label" context="yes" translatable="yes">0,00</property>
+                                        <property name="xalign">1.0</property>
+                                    </widget>
+                                    <packing>
+                                        <property name="left_attach">3</property>
+                                        <property name="right_attach">4</property>
+                                        <property name="x_options">fill</property>
+                                        <property name="y_options">fill</property>
+                                    </packing>
+                                </child>
+                            </widget>
+                            <packing>
+                                <property name="expand">False</property>
+                                <property name="position">2</property>
+                            </packing>
+                        </child>
+                    </widget>
+                    <packing>
+                        <property name="position">2</property>
+                    </packing>
+                </child>
+            </widget>
+        </child>
+    </widget>
+</glade-interface>

Added: stoq/trunk/stoq/gui/till/till.py
==============================================================================
--- (empty file)
+++ stoq/trunk/stoq/gui/till/till.py	Mon Sep  5 10:21:49 2005
@@ -0,0 +1,187 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2005 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 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):       Henrique Romano     <henrique at async.com.br>
+##
+"""
+stoq/gui/till/till.py:
+    Implementation of till application.
+"""
+
+import gettext
+from datetime import date
+import gtk
+
+from sqlobject.sqlbuilder import AND
+from kiwi.ui.widgets.list import Column
+from stoqlib.gui.search import BaseListSlave, SearchBar
+from stoqlib.gui.columns import ForeignKeyColumn
+from stoqlib.exceptions import TillError
+from stoqlib.database import rollback_and_begin
+
+from stoq.gui.application import AppWindow
+from stoq.domain.sale import Sale
+from stoq.domain.person import Person, PersonAdaptToClient
+from stoq.domain.payment import get_current_till_movimentation, Till
+from stoq.domain.sellable import get_formatted_price
+from stoq.lib.runtime import new_transaction
+from stoq.lib.parameters import sysparam
+from stoq.gui.editors.till import TillOpeningEditor, TillClosingEditor
+
+_ = gettext.gettext
+
+class TillApp(AppWindow):
+    gladefile = 'till'
+    widgets = ('searchbar_holder',
+               'klist_holder',
+               'total_label',
+               'confirm_order_button',
+               'TillMenu',
+               'TillOpen',
+               'TillClose',
+               'CurrentTill',
+               'quit_action')
+
+    def __init__(self, app):
+        AppWindow.__init__(self, app)
+        self.conn = new_transaction()
+        self._setup_widgets()
+        self._setup_slaves()
+
+    def _setup_widgets(self):
+        self._update_widgets()
+        self.total_label.set_size('x-large')
+        self.total_label.set_bold(True)
+        self.total_label.set_text(get_formatted_price(0.00))
+        # TODO: Waiting for bug #1862
+        self.confirm_order_button.set_sensitive(False)
+        # TODO: Implement Current Till movimentation dialog
+        self.CurrentTill.set_sensitive(False)
+
+    def _update_widgets(self):
+        has_till = get_current_till_movimentation(self.conn) is not None
+        self.TillClose.set_sensitive(has_till)
+        self.TillOpen.set_sensitive(not has_till)
+
+    def _setup_slaves(self):
+        list_slave = BaseListSlave(columns=self.get_columns())
+        self.attach_slave('klist_holder', list_slave)
+        self.sale_list = list_slave.klist
+
+        self.searchbar = SearchBar(self, Sale, self.get_columns(),
+                                   search_lbl_text=_('Find Sales'))
+        self.searchbar.search_items()
+        self.attach_slave('searchbar_holder', self.searchbar)
+
+    #
+    # BaseListSlave hooks
+    #
+
+    def get_columns(self):
+        return [Column('code', title=_('Code'), width=100, data_type=int,
+                       sorted=True),
+                Column('open_date', title=_('Date'), width=120, 
+                       data_type=date, justify=gtk.JUSTIFY_RIGHT),
+                ForeignKeyColumn(Person, 'name', title=_('Client'), expand=True,
+                                 data_type=str, obj_field='client._original'),
+                Column('status_name', title=_('Status'), width=120, 
+                       data_type=str, justify=gtk.JUSTIFY_CENTER),
+                Column('total', title=_('Total'), width=150, data_type=float,
+                       justify=gtk.JUSTIFY_RIGHT, format='%.2f')]
+
+    def get_extra_query(self):
+        q1 = Sale.q.clientID == PersonAdaptToClient.q.id
+        q2 = PersonAdaptToClient.q._originalID == Person.q.id
+        q3 = Sale.q.status == Sale.STATUS_OPENED
+        return AND(q1, q2, q3)
+
+    def update_klist(self, items=[]):
+        self.sale_list.clear()
+        total_value = 0.00
+
+        for item in items:
+            self.sale_list.add_instance(item)
+            total_value += item.total
+
+        self.total_label.set_text(get_formatted_price(total_value))
+
+    #
+    # Kiwi callbacks
+    #
+
+    def open_till(self, *args):
+        rollback_and_begin(self.conn)
+
+        if get_current_till_movimentation(self.conn) is not None:
+            raise TillError("You already have a till movimentation opened. "
+                            "Close the current Till and open another one.")
+        
+        # Trying get the movimentation created by the last till
+        # movimentation closed.  This movimentation has all the sales
+        # not confirmed in the last movimentation.
+        result = Till.select(Till.q.status == Till.STATUS_PENDING,
+                             connection=self.conn)
+        if result.count() == 0:
+            till = Till(connection=self.conn, 
+                        branch=sysparam(self.conn).CURRENT_BRANCH)
+        elif result.count() == 1:
+            till = result[0]
+        else:
+            raise TillError("You have more than one till operation "
+                            "pending.")
+
+        if self.run_dialog(TillOpeningEditor, self.conn, till):
+            self.conn.commit()
+            self._update_widgets()
+            return
+        rollback_and_begin(self.conn)
+
+    def close_till(self, *args):
+        till = get_current_till_movimentation(self.conn)
+        if till is None:
+            raise ValueError("You should have a till operation opened at "
+                             "this point")
+
+        if not self.run_dialog(TillClosingEditor, self.conn, till):
+            rollback_and_begin(self.conn)
+            return
+
+        self.conn.commit()
+        self._update_widgets()
+
+        opened_sales = Sale.select(Sale.q.status == Sale.STATUS_OPENED,
+                                   connection=self.conn)
+        if opened_sales.count() == 0:
+            return
+
+        # A new till object to "store" the sales that weren't
+        # confirmed. Note that this new till operation isn't
+        # opened yet, but it will be considered when opening a
+        # new operation
+        new_till = Till(connection=self.conn, 
+                        branch=sysparam(self.conn).CURRENT_BRANCH)
+        for sale in opened_sales:
+            sale.till = new_till
+
+        self.conn.commit()
+
+

Modified: stoq/trunk/stoq/lib/parameters.py
==============================================================================
--- stoq/trunk/stoq/lib/parameters.py	(original)
+++ stoq/trunk/stoq/lib/parameters.py	Mon Sep  5 10:21:49 2005
@@ -54,9 +54,12 @@
                                                    adding a new
                                                    SellableCategory object.
 
+    * MONEY_PAYMENT_METHOD(PaymentMethod): Definition of the money payment 
+                                           method.
+
     * DELIVERY_SERVICE(ServiceAdaptToSellable): The default delivery service
                                                 to the system.
-                                               
+
 >> System constants:                                               
                                                
 
@@ -114,7 +117,6 @@
 
 from stoq.domain.base import Domain
 from stoq.domain.interfaces import ISupplier, IBranch, ICompany, ISellable
-from stoq.domain.interfaces import IDelivery
 from stoq.lib.runtime import get_connection, new_transaction
 
 
@@ -254,6 +256,14 @@
         return branch
 
     @property
+    def MONEY_PAYMENT_METHOD(self):
+        from stoq.domain.payment import PaymentMethod
+        parameter = get_foreign_key_parameter('MONEY_PAYMENT_METHOD',
+                                              self.conn)
+        return PaymentMethod.get(parameter.foreign_key, 
+				 connection=self.conn)
+
+    @property
     def DEFAULT_BASE_CATEGORY(self):
         from stoq.domain.sellable import BaseSellableCategory
         parameter = get_foreign_key_parameter('DEFAULT_BASE_CATEGORY', 
@@ -458,6 +468,12 @@
     for key, data in values:
         set_schema(conn, key, data)
 
+def ensure_default_payment_method(conn):
+    from stoq.domain.payment import PaymentMethod
+
+    pm = PaymentMethod(description=_('Money'), connection=conn)
+    set_schema(conn, 'MONEY_PAYMENT_METHOD', 'get_default_payment_method',
+               foreign_key=pm.id)
 
 
 #
@@ -476,6 +492,7 @@
     ensure_current_branch(trans)
     ensure_current_warehouse(trans)
     ensure_city_location(trans)
+    ensure_default_payment_method(trans)
     ensure_delivery_service(trans)
 
     trans.commit()

Modified: stoq/trunk/stoq/main.py
==============================================================================
--- stoq/trunk/stoq/main.py	(original)
+++ stoq/trunk/stoq/main.py	Mon Sep  5 10:21:49 2005
@@ -41,7 +41,7 @@
 
 # A list of subdirectories in stoq/gui/
 for dir in ['editors', 'components', 'pos', 'search',
-            'slaves', 'templates']:
+            'slaves', 'templates', 'till']:
     path = os.path.join(module.basedir, "stoq", "gui", dir, "glade")
     if os.path.exists(path) and os.path.isdir(path):
         environ.add_resource("glade", path)


More information about the POS-commit mailing list