commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue-contrib] r219 - / gnue-invoice/scripts gnue-pointofsale gnue-point


From: reinhard
Subject: [gnue-contrib] r219 - / gnue-invoice/scripts gnue-pointofsale gnue-pointofsale/doc gnue-pointofsale/doc/technotes gnue-pointofsale/samples gnue-pointofsale/samples/oldtownemarket gnue-pointofsale/scripts gnue-pointofsale/src gnue-pointofsale/src/backends gnue-pointofsale/src/backends/Base gnue-pointofsale/src/backends/gnue_sb gnue-pointofsale/src/frontends gnue-pointofsale/src/frontends/Base gnue-pointofsale/src/frontends/wx gnue-pointofsale/src/hardware gnue-pointofsale/src/hardware/barcodereader gnue-pointofsale/src/hardware/cashdrawer gnue-pointofsale/src/hardware/logprinter gnue-pointofsale/src/hardware/poledisplay gnue-pointofsale/src/hardware/receiptprinter gnue-pointofsale/src/payments hotline/forms
Date: Sat, 23 Apr 2005 07:05:42 -0500 (CDT)

Author: reinhard
Date: 2005-04-23 07:05:40 -0500 (Sat, 23 Apr 2005)
New Revision: 219

Added:
   gnue-pointofsale/
   gnue-pointofsale/AUTHORS
   gnue-pointofsale/COPYING
   gnue-pointofsale/ChangeLog
   gnue-pointofsale/FAQ
   gnue-pointofsale/INSTALL
   gnue-pointofsale/NEWS
   gnue-pointofsale/README
   gnue-pointofsale/ROADMAP.in
   gnue-pointofsale/TODO
   gnue-pointofsale/doc/
   gnue-pointofsale/doc/technotes/
   gnue-pointofsale/doc/technotes/00000.txt
   gnue-pointofsale/doc/technotes/00001.txt
   gnue-pointofsale/doc/technotes/index.html
   gnue-pointofsale/doc/technotes/index.txt
   gnue-pointofsale/samples/
   gnue-pointofsale/samples/oldtownemarket/
   gnue-pointofsale/samples/oldtownemarket/pos_backend.py
   gnue-pointofsale/scripts/
   gnue-pointofsale/scripts/gnue-pos.in
   gnue-pointofsale/setup-cvs.py
   gnue-pointofsale/setup.cfg.in
   gnue-pointofsale/setup.cvs
   gnue-pointofsale/setup.py
   gnue-pointofsale/src/
   gnue-pointofsale/src/Config.py
   gnue-pointofsale/src/Handler.py
   gnue-pointofsale/src/IOProxy.py
   gnue-pointofsale/src/POSClient.py
   gnue-pointofsale/src/__init__.py
   gnue-pointofsale/src/backends/
   gnue-pointofsale/src/backends/Base/
   gnue-pointofsale/src/backends/Base/Driver.py
   gnue-pointofsale/src/backends/Base/__init__.py
   gnue-pointofsale/src/backends/__init__.py
   gnue-pointofsale/src/backends/gnue_sb/
   gnue-pointofsale/src/backends/gnue_sb/Driver.py
   gnue-pointofsale/src/backends/gnue_sb/__init__.py
   gnue-pointofsale/src/frontends/
   gnue-pointofsale/src/frontends/Base/
   gnue-pointofsale/src/frontends/Base/Driver.py
   gnue-pointofsale/src/frontends/Base/__init__.py
   gnue-pointofsale/src/frontends/__init__.py
   gnue-pointofsale/src/frontends/wx/
   gnue-pointofsale/src/frontends/wx/Driver.py
   gnue-pointofsale/src/frontends/wx/NumberDisplay.py
   gnue-pointofsale/src/frontends/wx/__init__.py
   gnue-pointofsale/src/hardware/
   gnue-pointofsale/src/hardware/__init__.py
   gnue-pointofsale/src/hardware/barcodereader/
   gnue-pointofsale/src/hardware/barcodereader/__init__.py
   gnue-pointofsale/src/hardware/cashdrawer/
   gnue-pointofsale/src/hardware/cashdrawer/__init__.py
   gnue-pointofsale/src/hardware/cashdrawer/bell.py
   gnue-pointofsale/src/hardware/cashdrawer/escpos.py
   gnue-pointofsale/src/hardware/logprinter/
   gnue-pointofsale/src/hardware/logprinter/plain.py
   gnue-pointofsale/src/hardware/poledisplay/
   gnue-pointofsale/src/hardware/poledisplay/__init__.py
   gnue-pointofsale/src/hardware/receiptprinter/
   gnue-pointofsale/src/hardware/receiptprinter/__init__.py
   gnue-pointofsale/src/hardware/receiptprinter/escpos.py
   gnue-pointofsale/src/hardware/receiptprinter/plain.py
   gnue-pointofsale/src/payments/
   gnue-pointofsale/src/payments/__init__.py
Modified:
   gnue-invoice/scripts/setup-db.sh
   hotline/forms/hotline-en.gfd
   hotline/forms/kunden.gfd
Log:
Moved gnue-pointofsale from gnue to gnue-contrib repository.


Modified: gnue-invoice/scripts/setup-db.sh
===================================================================
--- gnue-invoice/scripts/setup-db.sh    2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-invoice/scripts/setup-db.sh    2005-04-23 12:05:40 UTC (rev 219)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-GNUEDIR="/home/gabor/SVN/gnue"
+GNUEDIR="/home/reinhard/prj/gnue"
 PACKAGEDIR="$GNUEDIR/gnue-packages"
 
 DATADIR=`echo -e "import gnue.paths\nprint gnue.paths.data" | gcvs`

Added: gnue-pointofsale/AUTHORS
===================================================================
--- gnue-pointofsale/AUTHORS    2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/AUTHORS    2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,4 @@
+Jason Cater <address@hidden>:
+  Primary Author
+
+If we've missed anyone, please let us know.

Added: gnue-pointofsale/COPYING
===================================================================
--- gnue-pointofsale/COPYING    2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/COPYING    2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: gnue-pointofsale/ChangeLog
===================================================================

Added: gnue-pointofsale/FAQ
===================================================================

Added: gnue-pointofsale/INSTALL
===================================================================

Added: gnue-pointofsale/NEWS
===================================================================

Added: gnue-pointofsale/README
===================================================================
--- gnue-pointofsale/README     2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/README     2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,79 @@
+GNUe Point of Sale: A tool of the GNUe Project (www.gnue.org)
+
+Introduction
+------------
+GNUe-PoS is the Point of Sale register program for the GNUe system.
+It is designed to work with your existing schema.
+
+Requirements
+------------
+  * Python 2.1+
+  * wxPython 2.4+
+  * GNUe Common
+  * GNUe Forms (w/Wx Drivers)
+  * GNUe Reports
+  * GNUe Small Business (Or a custom backend)
+  * Appropriate Hardware as needed (Receipt printers, pole displays, etc)
+
+It currently requires wxPython 2.4 or greater, so it will work on
+Win32, GTK (Linux/*BSD), and MacOSX.
+
+Installation
+------------
+Please see INSTALL for installation instructions.
+
+Features
+--------
+Short-term Goals:
+
+  Commission tracking
+  Discounts
+   * Per-item or Per-ticket
+   * Flat Reduction or Percentage
+   * Before-tax or after-tax
+  Customer support
+   * Buying on Account
+   * Automatic trade/store credit
+  Product Options
+  Non-inventoried transactions
+    * add description/department/price at time of sale
+  Mouse-less operation
+  Multiple printer support
+  Display Pole support
+  Manually keyed items
+  Tax exempt purchases
+
+Long-term Goals:
+
+  Credit Card backend support
+  Touch-screen support
+
+Hardware Support:
+
+  Input
+   * POS Keyboards
+   * Barcode Readers
+   * MICR/Check readers
+   * Toucscreen (transparent to us?)
+  Output
+   * Receipt Printers
+   * Cash Drawer
+   * Pole Display
+
+Backend Accounting:
+
+  * LanePOS
+  * GNUe Small Business
+  * GNUe Financials
+
+Payment Processing:
+
+  * Debit
+  * Credit
+  * On Account
+  * Check Guarantee
+
+License
+-------
+GNU Enterprise is free software.  See the file `COPYING' for the terms and
+conditions for copying, distribution and modification.

Added: gnue-pointofsale/ROADMAP.in
===================================================================

Added: gnue-pointofsale/TODO
===================================================================

Added: gnue-pointofsale/doc/technotes/00000.txt
===================================================================
--- gnue-pointofsale/doc/technotes/00000.txt    2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/doc/technotes/00000.txt    2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,19 @@
+Title:   GNUe Point-of-Sale Tech Note Specification
+Status:  Current
+Created: 2002-07-17
+Revised: 2002-07-17
+
+This technote describes the format of technotes.  A technote should be in
+plain text (ASCII) format. They should be numbered sequentially.
+
+The file consists of two sections, the header section and the text section.
+The header consists of line-delimited "field: value" pairs. Valid headers:
+
+  Title:  <The title of this technote>  (Required) 
+  Status: <Current|Deprecated|Obsolete> (Required)
+  Created: <Creation date in YYYY-MM-DD format>
+  Revised: <Last revision date in YYYY-MM-DD format>
+
+The header section is terminated with the first non-blank line. 
+
+

Added: gnue-pointofsale/doc/technotes/00001.txt
===================================================================
--- gnue-pointofsale/doc/technotes/00001.txt    2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/doc/technotes/00001.txt    2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,54 @@
+Title:   Internal Events
+Status:  Current
+Created: 2003-06-18
+
+User-Initiated Events
+=====================
+RequestBeginTicket
+RequestEndTicket
+RequestSuspendTicket
+RequestResumeTicket
+RequestCancelTicket
+RequestPriceLookup
+RequestTender
+RequestAddItem
+RequestRemoveItem
+RequestCancelLastItem
+RequestCancelItem
+RequestDuplicateLastItem
+RequestDiscount
+RequestExit
+RequestLogOut
+RequestQuantity
+RequestTaxExempt
+
+
+Data-Related Events
+===================
+UpdateTotals
+UpdateQuantity
+
+DiscountItem
+DiscountTicket
+
+BeginTicket
+EndTicket
+SuspendTicket
+ResumeTicket
+CancelTicket
+
+TenderTicket
+
+AddTicketItem
+CancelTicketItem
+
+PriceLookup
+
+BeginManualItem
+EndManualItem
+
+RequestManualDepartment
+RequestManualDescription
+RequestManualBasePrice
+RequestManualPrice
+

Added: gnue-pointofsale/doc/technotes/index.html
===================================================================
--- gnue-pointofsale/doc/technotes/index.html   2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/doc/technotes/index.html   2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,26 @@
+
+<html>
+<title>
+Tech Notes Index
+</title>
+<body>
+<h1>GNUe Point-of-Sale Tech Notes Index</h1>
+
+
+<h3>Current Tech Notes</h3>
+
+<table border="1" width="100%">
+<tr><th>File</th><th>Title</th></tr>
+
+<tr><td><a href="00000.txt">00000.txt</a></td><td>GNUe Point-of-Sale Tech Note 
Specification</td></tr>
+<tr><td><a href="00001.txt">00001.txt</a></td><td>Internal Events</td></tr>
+</table>
+
+
+
+
+<hr>
+<p>This file automatically created by
+common/utils/create-technote-index.py</p>
+</body>
+</html>

Added: gnue-pointofsale/doc/technotes/index.txt
===================================================================
--- gnue-pointofsale/doc/technotes/index.txt    2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/doc/technotes/index.txt    2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,19 @@
+
+GNUe Point-of-Sale Tech Notes Index
+
+
+==============================================================================
+Current Tech Notes
+==============================================================================
+
+File        Title
+----------  ------------------------------------------------------------------
+00000.txt   GNUe Point-of-Sale Tech Note Specification
+00001.txt   Internal Events
+----------  ------------------------------------------------------------------
+
+
+
+--------
+This file automatically created by
+common/utils/create-technote-index.py

Added: gnue-pointofsale/samples/oldtownemarket/pos_backend.py
===================================================================
--- gnue-pointofsale/samples/oldtownemarket/pos_backend.py      2005-04-19 
12:09:26 UTC (rev 218)
+++ gnue-pointofsale/samples/oldtownemarket/pos_backend.py      2005-04-23 
12:05:40 UTC (rev 219)
@@ -0,0 +1,241 @@
+#
+# Copyright 2003 Jason Cater
+#
+# FILE:
+# cater/pos_backend.py
+#
+# DESCRIPTION:
+# Custom backend for my schema
+#
+# NOTES:
+#
+
+from gnue.pointofsale.backends.Base.Driver import Driver as BaseDriver
+from gnue.common.datasources import GConditions
+from gnue.common.datasources.GDataSource import DataSourceWrapper
+from random import randint
+
+CONN = gConfig('BackendConnection')
+MANUAL_SKU = gConfig('ManualItemSKU')
+
+class Driver(BaseDriver):
+  def init(self):
+    self.__totals = {}
+    self.registerEventListeners({
+          'ProcessSKU': self._ProcessSKU,
+          'EmployeeLogin': self._EmployeeLogin,
+#          'EndManualItem': self._EndManualItem,
+          'RequestBeginTicket': self._RequestBeginTicket,
+          'BeginLayaway': self._BeginLayaway,
+          'BeginTicket': self._BeginTicket,
+          'EndTicket': self._EndTicket,
+          'CancelTicket': self._CancelTicket,
+          'VerifyLogin': self._VerifyLogin,
+          'AddDiscount': self._AddDiscount,
+          'AddTender': self._AddTender,
+          'AddTicketItem': self._AddTicketItem,
+          'UpdateTotal': self._UpdateTotal,
+          'CancelTicketItem': self._CancelTicketItem,
+          'TaxExemptionEntered': self._TaxExemptionEntered,
+          'Receipt:GetNotice': self._GetFooter
+})
+    self.__dealermap = {}
+
+  def _EmployeeLogin(self, event):
+    self._login = event.id
+
+  def _VerifyLogin(self, event):
+
+    resultset = self._getResultSet('employees', ('id', 
'first_name','last_name'),
+       conditions={'login': event.login, 'password': event.password, 
'active':'Y'})
+
+    rec = resultset.firstRecord()
+    if not rec:
+      self.dispatchEvent('Warning',message='Invalid Login')
+      event.setResult(1)
+      return
+
+    self.dispatchEvent('EmployeeLogin',
+                       name='%s' % (rec['first_name']),
+                       id=rec['id'],
+                       login=event.login)
+
+
+  def _ProcessSKU(self, event):
+    sku = event.sku
+    origEvent= event.origEvent
+    event.setResult(1)
+    if not hasattr(event,'finalEvent'):
+      event.finalEvent = 'AddTicketItem'
+
+    if sku and sku == MANUAL_SKU:  # Manually Keyed Item
+      # Special item...
+      origEvent.dispatchAfter('BeginManualItem', sku=sku, 
quantity=event.quantity)
+      event.setResult(0)
+
+    elif 1 <= len(sku) <= 3:
+      # Booth number...
+      rs = self._getResultSet('booths', ('dealer',), {'booth': sku})
+      rec = rs.firstRecord()
+      if not rec:
+        self.dispatchEvent('Warning',message='Invalid Dealer Code')
+        return
+
+      self.__dealermap[sku] = rec['dealer']
+
+      origEvent.dispatchAfter('BeginManualItem', sku=sku, 
quantity=event.quantity)
+      event.setResult(0)
+
+    else:
+      # Regular item...
+      price = 10
+      description = 'Foobar'
+##      self.dispatchEvent(event.finalEvent, sku=sku, description=description, 
quantity=event.quantity, price=price, extended=round(event.quantity*price,2), 
taxable=1)
+
+    if 0: # requiresQuantity
+      origEvent.dispatchAfter('RequestQuantity')
+
+
+
+  def _RequestBeginTicket(self, event):
+    datasource = DataSourceWrapper(self.instance.connections,
+                   attributes={'connection': CONN,
+                               'name': 'dtsTicket',
+                               'table': 'tickets' } )
+
+    self.__ticket = serial = 
datasource.extensions.getSequence('pos_ticket_id_seq')
+
+    self.dispatchEvent('BeginTicket', serial=serial)
+
+
+  def _EndTicket(self, event):
+
+    # Insert the ticket
+    ts = self._createResultSet('pos_tickets', ('id','clerk','station',
+                                                'tax_exemption','subtotal',
+                                                'tax','total'))
+    tr = ts.insertRecord()
+    tr['id'] = self.__ticket
+    tr['clerk'] = self._login
+    tr['station'] = self.instance.registername
+    tr['tax_exemption'] = self.__taxExempt
+    tr['subtotal'] = self.__totals['subtotal']
+    tr['tax'] = self.__totals['tax']
+    tr['total'] = self.__totals['total']
+
+
+    # Then the items...
+    irs = self._createResultSet('pos_ticket_items', ('ticket_id','quantity',
+                                                    'manual_item','department',
+                                                    'sku','description',
+                                                    
'base_amount','unit_amount',
+                                                    'amount','taxable'))
+    for item in self.__items.values():
+      ir = irs.insertRecord()
+      ir['ticket_id'] = self.__ticket
+      ir['quantity'] = item.quantity
+      ir['manual_item'] = len(item.sku) <= 3 and 'Y' or 'N'
+      ir['department'] = item.department
+      ir['sku'] = item.sku
+      ir['description'] = item.description
+      ir['base_amount'] = item.base
+      ir['unit_amount'] = item.each
+      ir['amount'] = item.extended
+      ir['taxable'] = item.taxable and 'Y' or 'N'
+      try:
+        ir['dealer'] = self.__dealermap[item.sku]
+      except KeyError:
+        pass
+
+    # and the payments...
+    trs = self._createResultSet('pos_ticket_payments', 
('ticket_id','payment_type',
+                                                    
'payment_amount','approval_code'))
+    for tender in self.__tenders:
+      tr = trs.insertRecord()
+      tr['ticket_id'] = self.__ticket
+      tr['payment_type'] = tender.type
+      tr['payment_amount'] = tender.amount
+      try:
+        tr['approval_code'] = tender.extra
+      except:
+        pass
+
+    ts.post()
+    irs.post()
+    trs.post()
+    self.instance.connections.commitAll()
+
+    self.__ticket = 0
+
+
+  def _UpdateTotal(self, event):
+    self.__totals[event.field] = event.amount
+
+
+  def _CancelTicket(self, event):
+    self.__ticket = 0
+
+
+  def _BeginTicket(self, event):
+    self.__layaway = 0
+    self.__items = {}
+    self.__totals = {}
+    self.__tenders = []
+    self.__taxExempt = None
+
+  def _BeginLayaway(self, event):
+    self.__layaway = 1
+
+  def _TaxExemptionEntered(self, event):
+    if event.value:
+      self.__taxExempt = event.value
+    else:
+      self.__taxExempt = None
+
+  def _AddDiscount(self, event):
+    pass
+
+  def _AddTender(self, event):
+    self.__tenders.append(event)
+
+  def _AddTicketItem(self, event):
+    self.__items[event.lineitem] = event
+
+  def _CancelTicketItem(self, event):
+    del self.items[event.lineitem]
+
+
+  def _GetFooter(self, event):
+    i = randint(0, len(_FooterChoices)-1)
+    print "Quote #%s" % i
+    event.setResult(_FooterChoices[i])
+
+
+_FooterChoices = (
+"""\
+Did you know?  Old Towne Market has
+a professional portrait studio. Let
+us take your next family portrait,
+senior picture, or holiday photo.""",
+"""\
+Old Towne Market has something for
+everyone! From antiques, artwork,
+reproductions, and pottery, to
+children's clothing and glassware.""",
+"""\
+Add Old Towne Market to your
+shopping list this coming holiday
+season. We offer gift certificates
+for your convenience.""",
+"""\
+Old Towne Market now carries
+Peter's Pottery. Ask about our
+custom orders.""",
+"""\
+Know a special little girl with
+an upcoming birthday? Old Towne
+Market now hosts tea parties!"""
+)
+
+
+

Added: gnue-pointofsale/scripts/gnue-pos.in
===================================================================
--- gnue-pointofsale/scripts/gnue-pos.in        2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/scripts/gnue-pos.in        2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003 Free Software Foundation
+#
+
+import os
+import sys
+
+__GNUEENV__
+
+if not os.environ.has_key('GNUE_CONNECTIONS'):
+    if os.path.isfile(INST_GNUE_CONNECTIONS):
+        os.environ['GNUE_CONNECTIONS'] = INST_GNUE_CONNECTIONS
+
+from gnue.pointofsale.POSClient import POSClient
+
+if __name__ == '__main__':
+  POSClient().run()
+


Property changes on: gnue-pointofsale/scripts/gnue-pos.in
___________________________________________________________________
Name: svn:executable
   + *

Added: gnue-pointofsale/setup-cvs.py
===================================================================
--- gnue-pointofsale/setup-cvs.py       2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/setup-cvs.py       2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,301 @@
+#!/usr/bin/env python
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+
+import sys, string, os, glob
+
+
+BASEDIR = os.path.abspath(os.path.join(os.getcwd(), '..'))
+
+rootCommands = []
+
+def createCVS (scriptdir,script,py):
+  script = "%s%s" % (script, SUFFIX)
+  print "Creating %s" % os.path.join(scriptdir, script)
+  file = open(os.path.join(scriptdir, script),'w')
+  file.write(cvsbase % py)
+  file.close()
+  os.system ('chmod 700 %s/%s/%s' % (CVSDIR,scriptdir, script))
+
+  rootCommands.append ('rm -f %s/%s' % (BINDIR, script))
+  rootCommands.append ('ln -s %s/%s/%s %s/%s' % \
+        (CVSDIR, scriptdir, script, BINDIR, script))
+  rootCommands.append ('chmod o+x %s/%s/%s' % (CVSDIR,scriptdir, script))
+
+
+def createDocs(sgmlfile, destdir):
+  sgmlfile = os.path.join(CVSDIR, sgmlfile)
+  destdir  = os.path.join(DOCDIR, destdir)
+  rootCommands.append("if [ ! -d %s ]; then mkdir %s; fi" % (destdir, destdir))
+  rootCommands.append("%s --output %s %s" %
+                                                  (DB2HTML, destdir, sgmlfile))
+
+
+def getInput(message, default, responseSet=None):
+  print ""
+  while 1:
+    if default != None:
+      print "%s [%s] " % (message, default),
+    else:
+      print "%s " % (message),
+
+    val = string.strip(sys.stdin.readline())
+    print ""
+
+    if not len(val) and default != None:
+      return default
+    elif responseSet and string.upper(val) in (responseSet):
+      return string.upper(val)
+    elif not responseSet:
+      return val
+
+
+def getYesNo(message, default=None):
+  return getInput(message,default,('Y','N')) == 'Y'
+
+
+def getBinPaths():
+  paths = string.split(os.environ['PATH'],os.pathsep)
+  if not ('/usr/local/bin' in paths or '/usr/local/bin/' in paths) and \
+     os.path.isdir('/usr/local/bin'):
+    paths.append('/usr/local/bin')
+  return(paths)
+
+
+# Returns a list containing all Python executables in the user's path.
+# List element 0 will always be the currently running executable
+def findAllPythonExes():
+  results = [sys.executable]
+
+  for path in getBinPaths():
+    for file in glob.glob('%s/python?*' % os.path.abspath(string.strip(path))):
+      base = os.path.basename(file)
+      version = base[6:]
+
+      # Do not include python 1.x files
+      if version < '2':
+        break
+
+      if os.path.samefile(file, sys.executable):
+        # If this file is the one being executed, just replace w/full name
+        if results[0] == sys.executable:
+          results[0] = file
+      else:
+        results.append(file)
+
+  return results
+
+
+def finddb2html():
+  for path in getBinPaths():
+    for file in glob.glob('%s/db2html' % os.path.abspath(string.strip(path))):
+      return(file)
+
+  # Nothing found
+  return "";
+
+
+def restartWithExecutable(exe):
+  os.system('%s %s --withNewExecutable "%s"' \
+      % (exe, sys.argv[0], string.join(sys.argv[1:],'" "')))
+  sys.exit()
+
+
+restarting = ('--withNewExecutable' in sys.argv)
+
+if not restarting:
+  print """
+GNU Enterprise Developer Setup
+
+This script will install the GNU Enterprise tools to run
+from a CVS copy.  This allows GNUe to be run without a
+full installation.  THIS IS PRIMARILY USEFUL TO DEVELOPERS.
+  """
+
+# First of all, make sure this is a current enough Python release.
+try:
+  if sys.hexversion < 0x02000000:
+    raise AttributeError
+except AttributeError:
+
+  print "-" * 70
+  print """
+  You are running Python %s.
+
+  GNU Enterprise requires at least Python 2.0 (recommended: 2.1+).
+  If you have a later version installed, you should run setup.py
+  against that version. For example, if you have Python 2.1
+  installed, you may need to run:
+
+       python2.1 %s
+""" % (string.split(sys.version)[0], sys.argv[0])
+  print "-" * 70
+
+  exes = findAllPythonExes()
+
+  if len(exes) == 1:
+    sys.exit()
+  else:
+
+    # There are better versions of Python installed...
+
+    exes.pop(0)
+    print "\n"
+    print "I have found the following Python executables. "
+    print "Choose another version of Python to run."
+    print ""
+
+    validOptions = []
+    i = 0
+
+    for file in exes:
+      i = i + 1   # Do not change this to i += 1 as that's >= py2.x
+      validOptions.append(str(i))
+      print "  [%s]  %s" % (i, file)
+
+    validOptions.append ('Q')
+    selection = getInput(
+       "Please enter the number of the Python executable to\n"
+       'use [1-%s] or enter "q" to quit.' % i, '1', validOptions)
+
+    if selection == 'Q':
+      sys.exit()
+
+    restartWithExecutable(exes[int(selection)-1])
+
+
+
+
+try:
+  import posixpath
+except ImportError:
+  print "This CVS setup script will currently only install on a POSIX-based"
+  print "system.  To install on non-POSIX systems, you will have to install"
+  print "each individual tool using it's 'setup.py install' script."
+  sys.exit()
+
+while 1:
+  BASEDIR = os.path.abspath(
+         getInput ("Where is your cvs install of the GNUe tools located?", 
BASEDIR))
+  _BASEDIR = BASEDIR
+
+  try:
+    f = open(os.path.join(BASEDIR,'.cvs-config'))
+    break
+  except:
+    print """\
+I am unable to find the .cvs-config file created by the GNUe Tools'
+setup-cvs.py script. Have you run this script (recently)?
+"""
+text = f.read()
+f.close()
+exec(text)
+
+# Reset CVSDIR
+CVSDIR = os.path.abspath(os.getcwd())
+
+newdirs = [ ]
+for dir in newdirs:
+  rootCommands.append("if [ ! -d %s ]; then mkdir %s; fi" % (dir,dir))
+
+
+cvsbase = """#!/bin/sh
+     if [ "z$GNUE_CONNECTIONS" = "z" ]
+      then
+       if [ -f %(CONFDIR)s/etc/connections.conf ]
+       then
+         GNUE_CONNECTIONS=%(CONFDIR)s/etc/connections.conf
+         export GNUE_CONNECTIONS
+       fi
+     fi
+     if [ "z$INSTALL_PREFIX" = "z" ]
+     then
+       INSTALL_PREFIX=%(CONFDIR)s
+       export INSTALL_PREFIX
+     fi
+     PYTHONPATH=%(BASEDIR)s:$PYTHONPATH; export PYTHONPATH
+     SCRIPT="%%s"
+     if [ "$SCRIPT" != "" ]
+     then
+       %(PYTHONBIN)s %(GNUEDIR)s/${SCRIPT} "$@"
+     else
+       %(PYTHONBIN)s "$@"
+     fi
+  """ % globals()
+
+
+
+os.system ("""
+    cd %(GNUEDIR)s
+    rm -f pointofsale
+    ln -s %(CVSDIR)s/src pointofsale
+""" % globals() )
+
+
+rootCommands.append("""
+
+#rm -f %(CONFDIR)s/shared/images/pointofsale
+#ln -s %(CVSDIR)s/pointofsale/images %(CONFDIR)s/shared/images/pointofsale
+""" % globals() )
+
+
+createCVS ('scripts','gnue-pos-cvs','pointofsale/POSClient.py')
+
+
+if ROOTINSTALL:
+
+  out = open("setup-root.sh",'w')
+  out.write("#!/bin/sh\n")
+  out.write(string.join(rootCommands,"\n"))
+  out.write("\n")
+  out.close()
+
+  print ""
+  print "All the commands that need to be run as root have been written"
+  print "to setup-root.sh .  In order for your setup to be complete, this"
+  print "file must be run.  This setup script can run setup-root.sh for"
+  print "you. If you choose to run the script now and are not currently"
+  print "logged in as root, you will be prompted for the root password."
+  if getYesNo('Run "/bin/sh setup-root.sh" as root?','Y'):
+    os.system('su root -c "/bin/sh setup-root.sh"')
+  else:
+    print ""
+    print 'Do not forget to run "/bin/sh setup-root.sh" as root'
+    print ""
+
+else:
+
+  out = open("setup.log",'w')
+  out.write(string.join(rootCommands,"\n"))
+  out.write("\n")
+  out.close()
+
+  os.system(string.join(rootCommands,'\n'))
+
+
+#
+# Save our config settings
+#
+from pprint import pformat
+c = open (os.path.join(CVSDIR,'.cvs-config'),'w')
+for key, val in globals().items():
+  if key[0].isupper():
+    c.write('%s=%s\n\n' % (key, pformat(val)))


Property changes on: gnue-pointofsale/setup-cvs.py
___________________________________________________________________
Name: svn:executable
   + *

Added: gnue-pointofsale/setup.cfg.in
===================================================================
--- gnue-pointofsale/setup.cfg.in       2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/setup.cfg.in       2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,46 @@
+#
+# Copyright 2001-2003 Free Software Foundation
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+[install]
+
+
+# Where to install GNUe's application files... 
+prefix = /usr/local/gnue 
+
+# Where to install GNUe executable symlinks... 
+exec_prefix = /usr/local/bin
+
+
+
+###############################################
+#
+# We recommend you do not change anything below 
+# this line unless you know what you are doing 
+#
+###############################################
+
+install_lib=%(prefix)s/lib/python
+install_data=%(prefix)s
+install_scripts=%(exec_prefix)s
+
+[bdist_rpm]
+doc_files = README, README.curses, samples
+

Added: gnue-pointofsale/setup.cvs
===================================================================
--- gnue-pointofsale/setup.cvs  2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/setup.cvs  2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,30 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2001-2003 Free Software Foundation
+#
+# ********************
+# NOTE:
+# This file is parsed/run by gnue-common's setup-cvs.py script.
+# If you are attempting to set up a CVS install environment, please
+# run that file.  This file is not a standalone script.
+# ********************
+#
+
+linkModule('src/','pointofsale')
+

Added: gnue-pointofsale/setup.py
===================================================================
--- gnue-pointofsale/setup.py   2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/setup.py   2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+
+import sys
+import string
+import os
+import os.path
+
+from src import VERSION
+
+try:
+  import py2exe
+except:
+  pass
+
+
+#
+# hack to deal w/ fact distutils won't
+# allow us to set absolute path prefix
+# on windows
+#
+
+if not 'sdist' in sys.argv:
+  if sys.platform != 'win32' and '--prefix' not in sys.argv and '--root' not 
in sys.argv:
+    os.system ("""cp setup.cfg.in setup.cfg""")
+  else:
+    try:
+      os.remove('setup.cfg')
+    except:
+      pass
+
+
+
+
+
+try:
+  from distutils.core import setup
+  from distutils.command.build_scripts import build_scripts
+except ImportError:
+  print """
+Setup requires that python's distutils be
+installed.  You can get a copy of it from
+http://www.python.org/sigs/distutils-sig/
+    """
+  sys.exit()
+
+
+if 'sdist' in sys.argv:
+
+  print "Now, we are going to create the man pages for our tools."
+  os.system( """
+if [ ! -e man ]
+then
+mkdir man
+fi
+cd man
+../scripts/gnue-pos-cvs --generate-man-page
+""")
+
+
+# Site config stuff
+try:
+  index = sys.argv.index('--cfg-file')
+  site_config = os.path.join(os.path.abspath(sys.argv[index+1]))
+  sys.argv.pop(index)
+  sys.argv.pop(index)
+  config_line = "os.environ['GNUE_INSTALLED_SITE_CFG']='%s'\n" % site_config
+
+except ValueError:
+  config_line = ""
+
+#if ('build' in sys.argv) or ('install' in sys.argv):
+#  # Verify that gnue-common is installed
+#  try:
+#    print "Checking for GNUe-Common....",
+#    from gnue import common
+#    if common.__dict__.has_key('VERSION'):
+#      print "OK (version %s)" % common.VERSION
+#    else:
+#      print "OK"
+#  except ImportError:
+#    print """
+#You will need to install gnue-common
+#before gnuef will function.  You can
+#obtain a copy from
+#
+#http://www.gnuenterprise.org/download.php
+#"""
+#    sys.exit()
+
+
+
+class my_build_scripts(build_scripts):
+  def finalize_options(self):
+
+    build_scripts.finalize_options(self)
+
+    if self.finalized:
+      return
+
+
+    if sys.platform != 'win32':
+      dict = self.distribution.get_option_dict("install")
+      install_prefix = dict['prefix'][1]
+
+      gnue_env = \
+           "#######\n" \
+         + "# The following variable were set when GNUe was installed\n" \
+         + "import os, sys\n" \
+         + "sys.path.append('%s')\n" % dict['install_lib'][1] \
+         + "INST_GNUE_CONNECTIONS='%s/etc/connections.conf'\n" % 
dict['prefix'][1] \
+         + "os.environ['INSTALL_LIB']='%s'\n" % dict['install_lib'][1] \
+         + "os.environ['INSTALL_PREFIX']='%s'\n" % dict['prefix'][1] \
+         + config_line \
+         + "#######\n"
+    else:
+      gnue_env = \
+               "#######\n" \
+             + "# The following variables were set when GNUe was installed\n"  
  \
+             + "# (Generated for a Win32 system)\n" \
+             + "import os, sys\n" \
+             + "if __name__ == '__main__': \n" \
+             + "  _BASE = 
os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),'..'))\n" \
+             + "  try:\n" \
+             + "    os.environ['PATH']= 
os.path.abspath(os.path.dirname(sys.argv[0])) + ';' + os.environ['PATH']\n" \
+             + "  except KeyError:\n" \
+             + "    os.environ['PATH']= 
os.path.abspath(os.path.dirname(sys.argv[0]))\n" \
+             + "else: \n" \
+             + "  _BASE = 
os.path.abspath(os.path.join(os.path.dirname(__file__),'..'))\n" \
+             + 
"INST_GNUE_CONNECTIONS=os.path.join(_BASE,'etc','connections.conf')\n" \
+             + "os.environ['INSTALL_PREFIX']=_BASE\n"                \
+             + "os.environ['INSTALL_LIB']=_BASE\n"                   \
+             + "os.putenv('PYTHONCASEOK','1')\n" \
+             + config_line \
+             + "if os.path.isdir(os.path.join(_BASE,'extras')):\n" \
+             + "  sys.path.append(os.path.join(_BASE,'extras'))\n" \
+             + "#######\n\n"
+
+    for script in self.scripts:
+      print "Building %s..."% (script),
+
+
+      fin = open(script+".in", "r")
+      fout = open(script, "w")
+
+      print "Generating script %s from %s.in" % (script,script)
+
+      lines = fin.readlines()
+
+      for i in range(0, len(lines)):
+        if string.strip(lines[i]) == "__GNUEENV__":
+          lines[i] = gnue_env
+
+      fout.writelines(lines)
+      fin.close()
+      fout.close()
+      print "OK"
+
+
+docfiles = []
+##for file in ('doc/ReportingConcepts.txt',
+##             'doc/ReportingConcepts.pdf'):
+##  if os.path.isfile(file):
+##    docfiles.append(file)
+##  else:
+##    print "WARNING: File %s does not exist... not installing!" % file
+
+manfiles = []
+for file in ('man/gnue-pos.1',):
+  if os.path.isfile(file):
+    manfiles.append(file)
+  else:
+    print "WARNING: File %s does not exist... not installing!" % file
+
+
+#
+# You can run:
+#   $ GNUE_VERSION_SUFFIX=-pre1 ./setup.py sdist
+# and the packages will be created as GNUe-App-0.x.x-pre1.tar.gz
+#
+try:
+  suffix = os.environ['GNUE_VERSION_SUFFIX']
+except KeyError:
+  suffix = ""
+
+
+setup (name = "GNUe Point-of-Sale",
+       version = VERSION+suffix,
+       description = "GNUe Point-of-Sale",
+       long_description = "",
+       author = "GNUe Team",
+       author_email = "address@hidden",
+       url = "http://www.gnue.org";,
+
+       # Override certain command classes with our own ones
+       cmdclass = {'build_scripts':my_build_scripts},
+
+       data_files = [ ('doc/pointofsale', docfiles),
+                      ('man/man1', manfiles) ],
+
+       packages = ["gnue.pointofsale",
+                   "gnue.pointofsale.backends",
+                   "gnue.pointofsale.backends.Base",
+                   "gnue.pointofsale.backends.gnue_sb",
+                   "gnue.pointofsale.frontends",
+                   "gnue.pointofsale.frontends.Base", 
+                   "gnue.pointofsale.frontends.wx",
+                   "gnue.pointofsale.hardware",
+                   "gnue.pointofsale.hardware.cashdrawer",
+                   "gnue.pointofsale.hardware.barcodereader",
+                   "gnue.pointofsale.hardware.receiptprinter",
+                   "gnue.pointofsale.hardware.poledisplay",
+                   "gnue.pointofsale.payments",
+                   ],
+
+       package_dir = {"gnue.pointofsale" : "src"},
+
+       scripts = ["scripts/gnue-pos"]
+       )
+
+if 'install' in sys.argv:
+    print """
+    Note:
+    Warnings about the files not being installed in pythons search path can be 
ignored.
+
+    Make sure you have a default gnue.conf file (typically in 
/usr/local/gnue/etc.
+    A sample is provided in that directory that you can typically just copy 
over.
+    """
+
+if not 'sdist' in sys.argv:
+  if sys.platform != 'win32':
+    os.system (""" /bin/rm -rf setup.cfg""")


Property changes on: gnue-pointofsale/setup.py
___________________________________________________________________
Name: svn:executable
   + *

Added: gnue-pointofsale/src/Config.py
===================================================================
--- gnue-pointofsale/src/Config.py      2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/Config.py      2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,137 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# GRConfig.py
+#
+# DESCRIPTION:
+#
+# NOTES:
+#
+from gnue.common.formatting import GTypecast
+
+ConfigOptions = (
+
+  { 'Name'       : 'BackendConnection',
+    'Type'       : 'Setting',
+    'Comment'    : 'The connection name to use for sales data.',
+    'Description': 'The connection name to use for sales data.',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'gnuesb' },
+
+  { 'Name'       : 'BackendProvider',
+    'Type'       : 'Setting',
+    'Comment'    : 'The backend provider to use for data.',
+    'Description': 'The backend provider to use for data.',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'gnue_sb' },
+
+  { 'Name'       : 'FrontendUI',
+    'Type'       : 'Setting',
+    'Comment'    : 'The frontend user interface.',
+    'Description': 'The frontend user interface.',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'wx' },
+
+  { 'Name'       : 'ManualItemSKU',
+    'Type'       : 'Setting',
+    'Comment'    : 'The SKU to use for manually keyed items.',
+    'Description': 'The SKU to use for manually keyed items. (Can be empty to 
disable)',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : '9999' },
+
+  { 'Name'       : 'ManualItemSKUisDepartment',
+    'Type'       : 'Setting',
+    'Comment'    : 'The SKU to use for manually keyed items.',
+    'Description': 'The SKU to use for manually keyed items. (Can be empty to 
disabled)',
+    'Typecast'   :  GTypecast.boolean,
+    'Default'    : 1 },
+
+  { 'Name'       : 'ManualItemRequestBasePrice',
+    'Type'       : 'Setting',
+    'Comment'    : 'The SKU to use for manually keyed items.',
+    'Description': 'The SKU to use for manually keyed items. (Can be empty to 
disabled)',
+    'Typecast'   :  GTypecast.boolean,
+    'Default'    : 1 },
+
+  { 'Name'       : 'TaxRate',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.number,
+    'Default'    : 7.00 },
+
+  { 'Name'       : 'TaxState',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'MS' },
+
+  { 'Name'       : 'SKULabel',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'Item Code' },
+
+  { 'Name'       : 'CompanyName',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'GNU Enterprise' },
+
+  { 'Name'       : 'CompanyPhone',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : '' },
+
+  { 'Name'       : 'CompanyLogo',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : '' },
+
+  { 'Name'       : 'ReceiptHeader',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'My Sample Store\\n123 N Main St\\nMy Town, MS 
38654\\n800-555-1234' },
+
+  { 'Name'       : 'ReceiptFooter',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : 'Thank you for your purchase.\\nPlease Come Again!' },
+
+  { 'Name'       : 'ReceiptLogo',
+    'Type'       : 'Setting',
+    'Comment'    : '',
+    'Description': '',
+    'Typecast'   :  GTypecast.text,
+    'Default'    : '' },
+)
+

Added: gnue-pointofsale/src/Handler.py
===================================================================
--- gnue-pointofsale/src/Handler.py     2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/Handler.py     2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,515 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# Handler.py
+#
+# DESCRIPTION:
+#
+# NOTES:
+#
+
+import os, sys, types
+
+from gnue.common.apps import GDebug
+from gnue.common.apps import GConfig
+from gnue.common import events
+
+
+class Handler(events.EventAware):
+
+  def __init__(self, client):
+    events.EventAware.__init__(self, client)
+
+    self._ManualItemSKUisDepartment = gConfig('ManualItemSKUisDepartment')
+    self._ManualItemRequestBasePrice = gConfig('ManualItemRequestBasePrice')
+    self._TaxRate = float(gConfig('TaxRate'))/100
+
+    self._loggedin = 0
+    self._quantityRequestedFlag = 0
+    self._ticket = 0
+    self._quantity = 1
+
+    self.registerEventListeners( {
+            'BeginTicket': self._BeginTicket,
+            'EndTicket': self._EndTicket,
+            'CancelTicket': self._CancelTicket,
+            'ClearTicket': self._ClearTicket,
+
+            'BeginLayaway': self._BeginLayaway,
+            'EndLayaway': self._EndLayaway,
+            'RequestBeginLayaway': self._RequestBeginLayaway,
+            'RequestEndLayaway': self._RequestEndLayaway,
+
+            'AddDiscount': self._AddDiscount,
+            'AddTender': self._AddTender,
+            'AddTicketItem': self._AddTicketItem,
+            'CancelTicketItem': self._CancelTicketItem,
+
+            'LoginEntered': self._LoginEntered,
+            'PasswordEntered': self._PasswordEntered,
+            'SKUEntered': self._SKUEntered,
+            'QuantityEntered': self._QuantityEntered,
+            'CancelItemEntered': self._CancelItemEntered,
+
+            'RequestLogin': self._RequestLogin,
+            'RequestPassword': self._RequestPassword,
+
+            'RequestSKU': self._RequestSKU,
+            'RequestQuantity': self._RequestQuantity,
+            'RequestSuspendTicket': self._RequestSuspendTicket,
+            'RequestResumeTicket': self._RequestResumeTicket,
+            'RequestCancelTicket': self._RequestCancelTicket,
+            'RequestPriceLookup': self._RequestPriceLookup,
+            'RequestRemoveItem': self._RequestRemoveItem,
+            'RequestCancelItem': self._RequestCancelItem,
+            'RequestDuplicateLastItem': self._RequestDuplicateLastItem,
+            'RequestDiscount': self._RequestDiscount,
+            'RequestLogOut': self._RequestLogOut,
+            'RequestExit': self._RequestExit,
+
+            'RequestTender:Cash': self._RequestTenderCash,
+            'RequestTender:Check': self._RequestTenderCheck,
+            'RequestTender:CreditCard': self._RequestTenderCreditCard,
+            'RequestTender:Gift': self._RequestTenderGift,
+            'TenderEntered': self._TenderEntered,
+            'TenderExtraEntered': self._TenderExtraEntered,
+
+            'BeginManualItem': self._BeginManualItem,
+            'RequestEndManualItem': self._RequestEndManualItem,
+            'ManualDepartmentEntered': self._ManualDepartmentEntered,
+            'ManualDescriptionEntered': self._ManualDescriptionEntered,
+            'ManualBasePriceEntered': self._ManualBasePriceEntered,
+            'ManualPriceEntered': self._ManualPriceEntered,
+            'RequestManualDepartment': self._RequestManualDepartment,
+            'RequestManualDescription': self._RequestManualDescription,
+            'RequestManualBasePrice': self._RequestManualBasePrice,
+            'RequestManualPrice': self._RequestManualPrice,
+
+            'RequestTaxExemption': self._RequestTaxExemption,
+            'TaxExemptionEntered': self._TaxExemptionEntered,
+    })
+
+  def run(self):
+    self._RequestLogin(None)
+
+
+  def _LoginEntered(self, event):
+    self._login = event.value
+    self.dispatchEvent('RequestPassword')
+
+  def _RequestPassword(self, event):
+    self.dispatchEvent('Prompt',
+                       label='Enter Password:',
+                       field='Password',
+                       response="PasswordEntered")
+
+  def _PasswordEntered(self, event):
+    rs = self.dispatchEvent('VerifyLogin', login=self._login, 
password=event.value)
+    if rs:
+      self.dispatchEvent('RequestLogin')
+      return
+    self._loggedin = 1
+    self.dispatchEvent('RequestSKU')
+
+
+  def _SKUEntered(self, event):
+    if not hasattr(event,'next'):
+      event.next = 'RequestSKU'
+
+    if event.next == 'RequestQuantity':
+      rs = 0
+      if not self._ticket:
+        self.dispatchEvent('RequestBeginTicket')
+      self.dispatchEvent('RequestQuantity')
+
+    elif event.value:
+      if not self._ticket:
+        self.dispatchEvent('RequestBeginTicket')
+      rs = self.dispatchEvent('ProcessSKU', sku=event.value, origEvent=event, 
quantity=self._quantity)
+    else:
+      if not self._ticket and event.next in ('RequestTaxExemption',):
+        self.dispatchEvent('RequestBeginTicket')
+
+      rs = 1
+
+    if rs:
+      event.dispatchAfter(event.next)
+
+
+  def _QuantityEntered(self, event):
+    if not event.value or event.value == '0':
+      self.dispatchEvent('RequestSKU')
+
+    try:
+      self._quantity = float(event.value)
+      if self._quantity < 0:
+        raise ValueError
+      self.dispatchEvent('RequestSKU')
+    except ValueError:
+      self.dispatchEvent('Warning',message='Quantity must be entered')
+      self.dispatchEvent('RequestQuantity')
+      return
+
+  def _CancelItemEntered(self, event):
+    if not event.value:
+      self.dispatchEvent('RequestSKU')
+      return
+    v = int(event.value) - 1
+    if v >= len(self._items):
+      self.dispatchEvent('RequestCancelItem')
+    elif not hasattr(self._items[v],'CANCELED'):
+      attrs = {}
+      for attr, value in event.__dict__.items():
+        if attr[0] != '_' and type(value) != types.MethodType:
+          attrs[attr] = value
+      attrs['origlineitem'] = v + 1
+      attrs['lineitem'] = len(self._items)
+      attrs['extended'] *= -1
+      attrs['each'] *= -1
+      self._items[v].CANCELED = 1
+    self.dispatchEvent('CancelItem', **attrs)
+
+
+  def _BeginManualItem(self, event):
+    self._manualSKU = event.sku
+    if self._ManualItemSKUisDepartment:
+      self._manualDepartment = event.sku
+      self._manualEvents = ['RequestManualDescription']
+    else:
+      self._manualEvents = 
['RequestManualDepartment','RequestManualDescription']
+
+    if self._ManualItemRequestBasePrice:
+      self._manualEvents += ['RequestManualBasePrice','RequestManualPrice']
+      self._manualBasePrice = 0
+    else:
+      self._manualEvents += ['RequestManualPrice']
+
+    self._manualEvents.append('RequestEndManualItem')
+
+    event.dispatchAfter(self._manualEvents.pop(0))
+
+  def _ManualDepartmentEntered(self, event):
+    if not len(event.value):
+      self.dispatchEvent('Warning',message='Department must be entered')
+      self.dispatchEvent('RequestManualDepartment')
+      return
+    self._manualDepartment = event.value
+    self.dispatchEvent(self._manualEvents.pop(0))
+
+  def _ManualDescriptionEntered(self, event):
+    if not len(event.value):
+      self.dispatchEvent('Warning',message='Description must be entered')
+      self.dispatchEvent('RequestManualDescription')
+      return
+    self._manualDescription = event.value
+    self.dispatchEvent(self._manualEvents.pop(0))
+
+  def _ManualBasePriceEntered(self, event):
+    try:
+      self._manualBasePrice = float(event.value)
+      if self._manualBasePrice < 0:
+        raise ValueError
+    except ValueError:
+      self.dispatchEvent('Warning',message='Base price must be entered')
+      self.dispatchEvent('RequestManualBasePrice')
+      return
+
+    v = self._manualEvents.pop(0)
+    if v == 'RequestManualPrice':
+      self.dispatchEvent(v, default=self._manualBasePrice)
+    else:
+      self.dispatchEvent(v)
+
+  def _ManualPriceEntered(self, event):
+    try:
+      self._manualPrice = float(event.value)
+      if self._manualPrice < 0:
+        raise ValueError
+    except ValueError:
+      self.dispatchEvent('Warning',message='Item price must be entered')
+      self.dispatchEvent('RequestManualPrice')
+      return
+
+    self.dispatchEvent(self._manualEvents.pop(0))
+
+
+  def _RequestBeginLayaway(self, event):
+    pass
+
+  def _RequestEndLayaway(self, event):
+    pass
+
+  #
+  #
+  #
+
+  def _RequestLogin(self, event):
+    print "Requested"
+    self.dispatchEvent('Prompt',
+                       label='Enter User ID:',
+                       response="LoginEntered")
+
+
+  def _RequestSKU(self, event):
+    endingEvents = [ 'RequestQuantity',
+                     'RequestPriceLookup',
+                     'RequestResumeTicket',
+                     'RequestTaxExemption',
+                     'RequestLogOut',
+                     'RequestExit' ]
+
+    if self._ticket:
+      endingEvents += ['RequestSuspendTicket',
+                       'RequestCancelTicket',
+                       'RequestTender:Cash',
+                       'RequestTender:Check',
+                       'RequestTender:CreditCard',
+                       'RequestTender:Gift',
+                       'RequestRemoveItem',
+                       'RequestCancelItem',
+                       'RequestDuplicateLastItem',
+                       'RequestDiscount']
+
+    self.dispatchEvent('Prompt', label='Enter %s:' % gConfig('SKULabel'), 
response='SKUEntered',
+         endingEvents=endingEvents)
+
+  def _RequestQuantity(self, event):
+    self.dispatchEvent('Prompt', response='QuantityEntered', label='Enter 
Quantity:')
+
+  def _RequestSuspendTicket(self, event):
+    pass
+
+  def _RequestResumeTicket(self, event):
+    pass
+
+  def _RequestCancelTicket(self, event):
+    self.dispatchEvent('CancelTicket', serial=self._ticket)
+
+  def _RequestPriceLookup(self, event):
+    pass
+
+  ##
+  ##
+  ##
+
+  def _RequestTenderCash(self, event):
+    self.__tender = 'CASH'
+    self.dispatchEvent('Prompt', label='Enter Cash Amount', 
response='TenderEntered')
+
+  def _RequestTenderCreditCard(self, event):
+    self.__tender = 'CREDIT'
+    self.dispatchEvent('Prompt', label='Enter Credit Card Amount', 
response='TenderEntered')
+
+  def _RequestTenderCheck(self, event):
+    self.__tender = 'CHECK'
+    self.dispatchEvent('Prompt', label='Enter Check Amount', 
response='TenderEntered')
+
+  def _RequestTenderGift(self, event):
+    self.__tender = 'GIFT'
+    self.dispatchEvent('Prompt', label='Enter Gift Certificate Amount', 
response='TenderEntered')
+
+  def _TenderEntered(self, event):
+    try:
+      self.__tenderamount = float(event.value)
+      if self.__tenderamount < 0:
+        raise ValueError
+    except ValueError:
+      self.dispatchEvent('Warning',message='Tender amount must be entered')
+      self.dispatchEvent('RequestTender' % 
{'CASH':'Cash','CREDIT':'CreditCard','CHECK':'Check','GIFT':'Gift'}[self.__tender])
+      return
+
+    if self.__tender == 'GIFT':
+      self.dispatchEvent('Prompt', label='Enter Gift Certificate Number', 
response='TenderExtraEntered')
+    elif self.__tender == 'CHECK':
+      self.dispatchEvent('Prompt', label='Enter Check Number', 
response='TenderExtraEntered')
+    elif self.__tender == 'CREDIT':
+      self.dispatchEvent('Prompt', label='Enter Approval Response Code', 
response='TenderExtraEntered')
+    else:
+      self.__endTender()
+
+  def _TenderExtraEntered(self, event):
+    self.__endTender(event.value)
+
+  def __endTender(self, extra=None):
+    self.dispatchEvent('AddTender', type=self.__tender, 
amount=self.__tenderamount, extra=extra)
+
+  ##
+  ##
+  ##
+
+  def _RequestRemoveItem(self, event):
+    pass
+
+  def _RequestCancelItem(self, event):
+    self.dispatchEvent('Prompt', response='CancelItemEntered', label='Enter 
Line # to Cancel:', default=len(self._items))
+
+  def _RequestDuplicateLastItem(self, event):
+    pass
+
+  def _RequestDiscount(self, event):
+    pass
+
+  def _RequestLogOut(self, event):
+    if self._ticket:
+      self.dispatchEvent('Warning',message='Cannot log out until ticket is 
canceled or completed')
+    else:
+      self._login = None
+      self.dispatchEvent('RequestLogin')
+
+  def _RequestExit(self, event):
+    if self._ticket:
+      self.dispatchEvent('Warning',message='Cannot exit until ticket is 
canceled or completed')
+    else:
+      sys.exit()
+
+  def _RequestTaxExemption(self, event):
+    self.dispatchEvent('Prompt', label='Enter Tax Exemption Number:', 
response='TaxExemptionEntered')
+
+  ##
+  ##
+  ##
+
+  def _RequestManualDepartment(self, event):
+    self.dispatchEvent('Prompt', label='Enter Department:', 
response='ManualDepartmentEntered')
+
+  def _RequestManualDescription(self, event):
+    self.dispatchEvent('Prompt', label='Enter Item Description:', 
response='ManualDescriptionEntered')
+
+  def _RequestManualBasePrice(self, event):
+    self.dispatchEvent('Prompt', label='Enter Base Price:', 
response='ManualBasePriceEntered')
+
+  def _RequestManualPrice(self, event):
+    if hasattr(event,'default'):
+      self.dispatchEvent('Prompt', label='Enter Sales Price:', 
response='ManualPriceEntered', default=event.default)
+    else:
+      self.dispatchEvent('Prompt', label='Enter Sales Price:', 
response='ManualPriceEntered')
+
+
+  def _RequestEndManualItem(self, event):
+    self.dispatchEvent('EndManualItem', sku=self._manualSKU,
+                       description=self._manualDescription,
+                       department=self._manualDepartment,
+                       baseprice=self._manualBasePrice,
+                       quantity=self._quantity,
+                       price=self._manualPrice,
+                       extended = self._quantity * self._manualPrice,
+                       taxable=1)
+
+    self.dispatchEvent('AddTicketItem', department=self._manualDepartment,
+                           sku=self._manualSKU,
+                           quantity=self._quantity,
+                           description=self._manualDescription,
+                           base=self._manualBasePrice,
+                           each=self._manualPrice,
+                           extended=round(self._manualPrice * 
self._quantity,2),
+                           taxable=1,
+                           lineitem=len(self._items))
+    self._quantity = 1
+    self.dispatchEvent('RequestSKU')
+
+  ##
+  ##
+  ##
+
+  def _BeginTicket(self, event):
+    self._ticket = event.serial
+    self._taxTotal = 0
+    self._subTotal = 0
+    self._payments = 0
+    self._taxExempt = 0
+    self._layaway = 0
+    self._items = []
+    self.dispatchEvent('UpdateTotal', field="subtotal", amount=0.0)
+    self.dispatchEvent('UpdateTotal', field="tax", amount=0.0)
+    self.dispatchEvent('UpdateTotal', field="total", amount=0.0)
+    self.dispatchEvent('UpdateTotal', field="balance", amount=0.0)
+
+  def _EndTicket(self, event):
+    event.dispatchAfter('ClearTicket')
+
+  def _CancelTicket(self, event):
+    event.dispatchAfter('ClearTicket')
+
+  def _ClearTicket(self, event):
+    self._ticket = 0
+    event.dispatchAfter('RequestSKU')
+
+  def _AddTicketItem(self, event):
+    self._subTotal += event.extended
+    if event.taxable:
+      self._taxTotal += event.extended
+    self._items.append(event)
+    event.lineitem=len(self._items)+1
+    self._updateTotals()
+
+  def _BeginLayaway(self, event):
+    self._layaway = 1
+
+  def _EndLayaway(self, event):
+    self._layaway = 0
+    self.dispatchEvent('EndTicket', serial=self._ticket)
+
+  def _AddTender(self, event):
+    self._payments += event.amount
+    if self._payments > self.total():
+      print self._payments-self.total()
+      event.dispatchAfter('AddTender', type='CHANGE',
+                          amount=round(self._payments-self.total(),2))
+
+    elif round(self._subTotal + self.taxes() - self._payments,2) == 0:
+      event.dispatchAfter('EndTicket', serial=self._ticket)
+    else:
+      event.dispatchAfter('RequestSKU')
+
+    self._updateTotals()
+
+
+  def _AddDiscount(self, event):
+    pass
+
+  def _CancelTicketItem(self, event):
+    pass
+
+  def _UpdateQuantity(self, event):
+    pass
+
+  def _TaxExemptionEntered(self, event):
+    if event.value:
+      self._taxExempt = 1
+    else:
+      self._taxExempt = 0
+    self._updateTotals()
+    self.dispatchEvent('RequestSKU')
+
+
+  def _updateTotals(self):
+    tax = self.taxes()
+    self.dispatchEvent('UpdateTotal', field="subtotal", amount=self._subTotal)
+    self.dispatchEvent('UpdateTotal', field="tax", amount=tax)
+    self.dispatchEvent('UpdateTotal', field="total", amount=self.total())
+    self.dispatchEvent('UpdateTotal', field="balance", amount=self.total() - 
self._payments)
+
+
+  def taxes(self):
+    return round(self._taxTotal* self._TaxRate,2) * (self._taxExempt == 0)
+
+  def total(self):
+    return self._subTotal + self.taxes()

Added: gnue-pointofsale/src/IOProxy.py
===================================================================
--- gnue-pointofsale/src/IOProxy.py     2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/IOProxy.py     2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,112 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# IOProxy.py
+#
+# DESCRIPTION:
+# Device/File/Command IO Proxy
+# This module proxies IO for the various hardware drivers
+# so the individual drivers don't have to worry about whether
+# they are writing to a file, a serial device, or piping
+# data to an external command.
+#
+# NOTES:
+# Somewhat crude and I'm not sure if the use of popen creates
+# a race condition... I have to guess not since I don't check
+# for success/fail.
+#
+
+__all__ = ['simpleOpen']
+
+import os
+from cStringIO import StringIO
+
+# This is a multiplexor of sorts
+# It returns the correct type of IOProxy
+def simpleOpen(*args, **parms):
+  if len(args) == 1 and type(args) == type({}) and not len(parms):
+    parms = args[0]
+  try:
+    tp = parms['type'].lower()
+  except:
+    tp = 'file'
+
+  rs = _ProxyMap[tp](*args, **parms)
+  rs.open()
+  return rs
+
+
+#
+# Base IOProxy
+#
+class _IOProxy:
+  def __init__(self, *args, **parms):
+    self.__dict__.update(parms)
+    if len(args):
+      self.__dict__.update(args[0])
+
+  def open(self):
+    pass
+
+  def close(self):
+    pass
+
+  def write(self, text):
+    self._stream.write(text)
+
+
+#
+# IOProxy for OS files/devices
+#
+class _FileProxy(_IOProxy):
+  def open(self):
+    print "Opening %s" % self.dest
+    self._stream = open(self.dest,'w')
+
+  def close(self):
+    print "Closing %s" % self.dest
+    self._stream.close()
+
+
+#
+# IOProxy for external commands
+#
+class _CommandProxy(_IOProxy):
+  def open(self):
+    self._stream = StringIO()
+
+  def close(self):
+    b = os.popen(self.dest,'w')
+    self._stream.seek(0)
+    b.write(self._stream.read())
+    b.close()
+    self._stream.close()
+    self._stream = None
+
+
+
+
+_ProxyMap = { 'file': _FileProxy,
+              'serial': _FileProxy,
+              'parallel': _FileProxy,
+              'device': _FileProxy,
+              'command': _CommandProxy }
+

Added: gnue-pointofsale/src/POSClient.py
===================================================================
--- gnue-pointofsale/src/POSClient.py   2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/POSClient.py   2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,185 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# POSClient.py
+#
+# DESCRIPTION:
+# Command line client startup file that parses args,
+# and imports the required UI.
+#
+# NOTES:
+#
+
+import os, sys
+
+from gnue.common.apps.GClientApp import *
+from gnue.common.apps import GDebug
+from gnue.common.apps import GConfig
+from gnue.common.datasources import GDataObjects, GConnections
+from gnue.common.utils.FileUtils import dyn_import
+from gnue.common import events
+
+from gnue.pointofsale import VERSION
+from gnue.pointofsale.Config import ConfigOptions
+
+from Handler import Handler
+
+class POSClient(GClientApp, events.EventAware):
+  #
+  # GClientApp() overrides
+  #
+  VERSION = VERSION
+  COMMAND = "gnue-pos"
+  NAME = "GNUe Point-of-Sale"
+  USAGE = GClientApp.USAGE + " <register name>"
+  COMMAND_OPTIONS = [ ]
+  SUMMARY = \
+     _("GNUe Point-of-Sale is a point of sale (cash register) program for the 
GNUe project.")
+
+  def __init__(self, connections=None):
+    self.eventController = events.EventController()
+    events.EventAware.__init__(self, self.eventController)
+
+    GClientApp.__init__(self, connections,'point-of-sale',ConfigOptions)
+    self.configurationManager.registerAlias('gConfigForms', 'forms')
+
+  #
+  # Run the client application
+  #
+  def run(self):
+
+    self.handler = Handler(self)
+
+    #
+    # Initialize interfaces
+    #
+    self.frontend = dyn_import("gnue.pointofsale.frontends.wx").Driver(self)
+    try:
+      self.backend = dyn_import("gnue.pointofsale.backends.%s" % 
gConfig('BackendProvider')).Driver(self)
+    except ImportError, mesg1:
+      try:
+        self.backend = dyn_import(gConfig('BackendProvider')).Driver(self)
+      except ImportError, mesg2:
+        self.handleStartupError('Unable to load backend provider:\n\n%s\n\n%s' 
% (mesg1, mesg2))
+
+
+    if hasattr(self.frontend,'handleStartupError') and not 
self.OPTIONS['debug-level']:
+      self.handleStartupError = self.frontend.handleStartupError
+
+##    if hasattr(self.frontend,'handleUncaughtException'):
+##      sys._orig_excepthook = sys.excepthook
+##      sys.excepthook = self.frontend.handleUncaughtException
+
+
+    #
+    # assign register name from 1st free argument
+    #
+    try:
+      self.registername = registername = self.ARGUMENTS[0]
+    except:
+      try:
+        import socket
+        self.registername = registername = socket.gethostname().split('.')[0]
+        if registername:
+          print "No register name specified; Assuming '%s'" % registername
+      except:
+        self.handleStartupError (_("No Register Name Specified."))
+
+    self._initHardware(registername)
+
+    #
+    # Get the user supplied parameters
+    #
+    ##userParameters = self.getCommandLineParameters(self.ARGUMENTS[1:])
+
+    self.handler.run()
+
+    #
+    # Testing stuff....
+    #
+##    self.dispatchEvent('BeginTicket', serial=100000)
+##    self.dispatchEvent('AddTicketItem', sku=123355, description='A Tale of 
Two Cities (Dickens, Charles) USED', department=20,
+##                                  quantity=1, each=5.95, extended=5.95)
+##    self.dispatchEvent('AddTicketItem', sku=123356, description='Harry 
Potter and the Order of the Phoenix (Rowling, J.K.)', department=20,
+##                                  quantity=2, each=29.99, extended=59.98)
+##    self.dispatchEvent('AddTicketItem',  description='Gift Certificate', 
department=999,
+##                                  quantity=1, each=30.00, extended=30.00)
+##    self.dispatchEvent('UpdateTotal', field="subtotal", amount=95.93)
+##    self.dispatchEvent('UpdateTotal', field="tax", amount=6.72)
+##    self.dispatchEvent('UpdateTotal', field="total", amount=102.65)
+##    self.dispatchEvent('Prompt', label='Enter User ID:', field="login")
+##    self.dispatchEvent('EndTicket')
+
+    self.frontend.run()
+
+
+
+  #
+  # Load all the hardware from the config file
+  #
+  def _initHardware(self, registername):
+    options = gConfigDict()
+    mapping = {}
+
+    for key in options.keys():
+      try:
+        p1, p2 = key.split("_",1)
+      except:
+        continue
+
+      if p1.lower()[:6] == 'device':
+        try:
+          num = int(p1[6:])
+        except ValueError:
+          continue
+
+        try:
+          mp = mapping[num]
+        except KeyError:
+          mp = {}
+          mapping[num] = mp
+
+        mp[p2.lower()] = options[key]
+
+
+    srt = list(mapping.items())
+    srt.sort()
+
+    for device, params in srt:
+      dr = params['driver'].lower().replace('/','.')
+      try:
+        module = dyn_import('gnue.pointofsale.hardware.%s' % dr)
+      except ImportError, mesg:
+        GDebug.printMesg(5, mesg)
+        try:
+          module = dyn_import(dr)
+        except ImportError, mesg:
+          GDebug.printMesg(5, mesg)
+          self.handleStartupError('Unable to load device #%2d (%s) -- Driver 
not found' % (device, params['driver']))
+
+      print "Loading hardware driver: %s" % params['driver']
+      module.Driver(self, params)
+
+
+if __name__ == '__main__':
+  POSClient().run()
+
+

Added: gnue-pointofsale/src/__init__.py
===================================================================
--- gnue-pointofsale/src/__init__.py    2005-04-19 12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/__init__.py    2005-04-23 12:05:40 UTC (rev 219)
@@ -0,0 +1,60 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+
+
+#
+# CREATING A RELEASE:
+#   1. Change _version to be the current version number
+#   2. Set _release to 1
+#
+# AFTER THE RELEASE:
+#   3. Increase the minor number
+#   4. Set _release = 0
+#   5. Commit to CVS
+
+_version = (0,0,1)
+_release = 0
+
+# Version will be of the form "1.1.2" if a release or "1.1.1.99" if in CVS
+if _release:
+  VERSION="%s.%s.%s" % _version
+else:
+  _t = (("%08d" % (int("%02d%02d%02d00" % _version)-1) ).replace('99','') + 
'99') + '0000000'
+  if _t[6:8] != '00':
+    extra = '.99'
+  else:
+    extra = ''
+  VERSION = "%s.%s.%s%s" % (int(_t[:2]), int(_t[2:4]), int(_t[4:6]), extra)
+
+# Hex Version will be an integer in which version 1.2.15 is represented
+# as 0x010215FF (for a release) or 0x01021500 (for a CVS snapshot)
+# This allows numeric testing of version numbers
+# (e.g., HEXVERSION >= 0x01020000)
+HEXVERSION = eval('0x%s%s' % ('%02d%02d%02d' % _version,
+                               _release and 'FF' or '00') )
+
+# Sigh... For Python compatability
+__version__ = VERSION
+__hexversion__ = HEXVERSION
+
+
+PACKAGE="GNUe-POS"
+TITLE="GNUe Point-of-Sale"

Added: gnue-pointofsale/src/backends/Base/Driver.py
===================================================================
--- gnue-pointofsale/src/backends/Base/Driver.py        2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/backends/Base/Driver.py        2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,79 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# Base/Driver.py
+#
+# DESCRIPTION:
+# Base driver file for GNUe POS backends
+#
+# NOTES:
+#
+
+from gnue.common.events.EventAware import EventAware
+from gnue.common.datasources import GConditions
+from gnue.common.datasources.GDataSource import DataSourceWrapper
+
+CONN = gConfig('BackendConnection')
+
+class Driver(EventAware):
+
+  ##########################################################
+  #
+  # TO BE REPLACED BY ACTUAL DRIVERS
+  #
+
+  def init(self):
+    pass
+
+
+  ##########################################################
+  #
+  # INTERNAL METHODS NOT TO BE
+  # REPLACED BY ACTUAL DRIVERS
+  #
+
+  def __init__(self, instance):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+
+    self.init()
+
+
+  def _getResultSet(self, table, fields, conditions={}):
+    datasource = DataSourceWrapper(self.instance.connections,
+                   fields=fields,
+                   attributes={'connection': CONN,
+                               'name': 'dts%s' % table,
+                               'table': table } )
+    resultset = datasource.createResultSet(
+            conditions=conditions,
+            readOnly=1)
+    return resultset
+
+  def _createResultSet(self, table, fields):
+    datasource = DataSourceWrapper(self.instance.connections,
+                   fields=fields,
+                   attributes={'connection': CONN,
+                               'name': 'dts%s' % table,
+                               'table': table } )
+    resultset = datasource.createEmptyResultSet()
+    return resultset
+

Added: gnue-pointofsale/src/backends/Base/__init__.py
===================================================================

Added: gnue-pointofsale/src/backends/__init__.py
===================================================================

Added: gnue-pointofsale/src/backends/gnue_sb/Driver.py
===================================================================
--- gnue-pointofsale/src/backends/gnue_sb/Driver.py     2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/backends/gnue_sb/Driver.py     2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,67 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# wx/Driver.py
+#
+# DESCRIPTION:
+# wxPython driver file for GNUe POS frontends.
+# Borrows/integrates heavily with the GNUe-Forms wxPython driver.
+#
+# NOTES:
+#
+
+from gnue.pointofsale.backends.Base.Driver import Driver as BaseDriver
+from gnue.common.datasources import GConditions
+from gnue.common.datasources.GDataSource import DataSourceWrapper
+
+CONN = gConfig('BackendConnection')
+
+class Driver(BaseDriver):
+  def init(self):
+    self.registerEventListeners({
+          'ProcessSKU': self._ProcessSKU,
+          'EndManualItem': self._EndManualItem,
+          'RequestBeginTicket': self._RequestBeginTicket,
+##          'EndTicket': self._EndTicket,
+##          'CancelTicket': self._CancelTicket
+})
+
+  def _ProcessSKU(self, event):
+    sku = event.sku
+    origEvent= event.origEvent
+    event.setResult(1)
+
+    if 1:  # Manually Keyed Item
+      origEvent.dispatchAfter('BeginManualItem', sku=sku, 
quantity=event.quantity)
+      event.setResult(0)
+    else:
+      self.dispatchEvent('AddItem', sku=123, description='Foobar', 
quantity=event.quantity, price=10, taxable=1)
+
+    if 0: # requiresQuantity
+      origEvent.dispatchAfter('RequestQuantity')
+
+
+  def _RequestBeginTicket(self, event):
+    self.dispatchEvent('BeginTicket', serial=123456)
+
+
+  def _EndManualItem(self, event):
+    pass

Added: gnue-pointofsale/src/backends/gnue_sb/__init__.py
===================================================================
--- gnue-pointofsale/src/backends/gnue_sb/__init__.py   2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/backends/gnue_sb/__init__.py   2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1 @@
+from Driver import Driver

Added: gnue-pointofsale/src/frontends/Base/Driver.py
===================================================================
--- gnue-pointofsale/src/frontends/Base/Driver.py       2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/frontends/Base/Driver.py       2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,53 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# Base/Driver.py
+#
+# DESCRIPTION:
+# Base driver file for GNUe POS frontends
+#
+# NOTES:
+#
+
+from gnue.common.events.EventAware import EventAware
+
+class Driver(EventAware):
+
+  ##########################################################
+  #
+  # TO BE REPLACED BY ACTUAL DRIVERS
+  #
+
+  def init(self):
+    pass
+
+
+  ##########################################################
+  #
+  # INTERNAL METHODS NOT TO BE
+  # REPLACED BY ACTUAL DRIVERS
+  #
+
+  def __init__(self, instance):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+
+    self.init()

Added: gnue-pointofsale/src/frontends/Base/__init__.py
===================================================================

Added: gnue-pointofsale/src/frontends/__init__.py
===================================================================

Added: gnue-pointofsale/src/frontends/wx/Driver.py
===================================================================
--- gnue-pointofsale/src/frontends/wx/Driver.py 2005-04-19 12:09:26 UTC (rev 
218)
+++ gnue-pointofsale/src/frontends/wx/Driver.py 2005-04-23 12:05:40 UTC (rev 
219)
@@ -0,0 +1,391 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# wx/Driver.py
+#
+# DESCRIPTION:
+# wxPython driver file for GNUe POS frontends.
+# Borrows/integrates heavily with the GNUe-Forms wxPython driver.
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from gnue.pointofsale.frontends.Base.Driver import Driver as BaseDriver
+from gnue.forms.uidrivers.wx import UIdriver as FormsDriver
+from gnue.forms.uidrivers.wx.GFwxApp import getWxApp
+
+from wxPython.wx import *
+from wxPython.gizmos import *
+from NumberDisplay import NumberDisplay, bigFont
+import string
+
+dollarFormat = '$%.02f'
+
+STATUS_USER = 2
+STATUS_TICKET = 1
+
+BASE_COLOR = wxNamedColour("ivory")
+LOGIN_COLOR = wxColour(255,220,220)
+LOGIN_EVENTS = ('LoginEntered','PasswordEntered')
+SPECIAL_COLOR = wxColour(220,255,220)
+SPECIAL_EVENTS = ('TaxExemptionEntered','CancelItemEntered', 'QuantityEntered')
+
+
+class Driver(BaseDriver):
+  def init(self):
+    self.totalFields = (  ('subtotal',_('Subtotal')),
+                          ('tax','%s Sales Tax' % (gConfig('TaxState'))),
+                          ('total',_('Total')),
+                          ('balance',_('Balance')) )
+
+    self.receiptColumns = ( ('lineitem',_('#'), '%s', 1, 1),
+                            ('department',_('Dept'), '%s', 0, 0),
+                            ('sku',_('Code'), '%s', 0, 0),
+                            ('quantity',_('Units'), '%s', 0, 0),
+                            ('description',_('Description'), '%s', 0, 0),
+                            ('base',_('Base Price'), dollarFormat, 1, 0),
+                            ('each',_('Price/Unit'), dollarFormat, 1, 0),
+                            ('extended',_('Extended'), dollarFormat, 1, 0),
+                            ('taxable',_('Tax?'), 'bool', 0, 1) )
+
+    self.totalFieldMapping = {}
+
+    self._buildUI()
+
+    self.registerEventListeners( {
+      'Prompt': self._Prompt,
+      'Warning': self._Warning,
+      'EmployeeLogin': self._EmployeeLogin,
+      'BeginTicket': self._BeginTicket,
+      'ClearTicket': self._ClearTicket,
+      'AddTicketItem': self._AddTicketItem,
+      'CancelTicketItem': self._CancelTicketItem,
+      'UpdateTotal': self._UpdateTotal } )
+
+
+  def run(self):
+    app = getWxApp()
+    app.MainLoop()
+
+  def _Prompt(self, event):
+    self.inputLabel.SetLabel(event.label)
+    self.inputField.Clear()
+    self.inputField.SetFocus()
+    self.__event = event.response
+    try:
+      self.statusbar.SetStatusText (event.help)
+    except AttributeError:
+      pass
+
+    if event.response in LOGIN_EVENTS:
+      self.inputField.SetBackgroundColour(LOGIN_COLOR)
+    elif event.response in SPECIAL_EVENTS:
+      self.inputField.SetBackgroundColour(SPECIAL_COLOR)
+    else:
+      self.inputField.SetBackgroundColour(BASE_COLOR)
+
+    try:
+      default = str(event.default)
+    except:
+      default = ""
+
+    try:
+      self.__endingEvents = event.endingEvents
+    except AttributeError:
+      self.__endingEvents = ()
+
+    self.inputField.SetValue(default)
+    self.inputField.SetSelection(0, len(default))
+
+  def _BeginTicket(self, event):
+    print "Beginning ticket # %s" % event.serial
+    self.statusbar.SetStatusText("Ticket # %s" % event.serial, STATUS_TICKET)
+    self.receiptList.DeleteAllItems()
+
+  def _ClearTicket(self, event):
+    print "Ending ticket"
+    self.statusbar.SetStatusText("No Current Ticket", STATUS_TICKET)
+
+  def _AddTicketItem(self, event):
+    # Insert a new row
+    i = self.receiptList.GetItemCount()
+    self.receiptList.InsertStringItem(i,"")
+
+    # And set each column
+    j = 0
+    for field, foo, format, bar, none in self.receiptColumns:
+      try:
+        v = event.__dict__[field]
+        if format == 'bool':
+          format = '%s'
+          v = v and 'Yes' or 'No'
+        self.receiptList.SetStringItem(i, j, format % v)
+      except KeyError:
+        pass
+      j += 1
+
+    self.receiptList.SetItemState(i, 
wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED,  
wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED)
+
+  def _CancelTicketItem(self, event):
+    pass
+
+  def _UpdateTotal(self, event):
+    self.totalFieldMapping[event.field].SetValue(dollarFormat % event.amount)
+
+  def _buildUI(self):
+    app = getWxApp()
+    self.frame = frame = wxFrame(None, -1, "GNUe Point-of-Sale", 
style=wxDEFAULT_FRAME_STYLE & (wxRESIZE_BORDER | wxRESIZE_BOX | wxMAXIMIZE_BOX))
+    frameSizer = wxBoxSizer(wxVERTICAL)
+    frame.SetSizer(frameSizer)
+    frame.SetAutoLayout(1)
+    EVT_SIZE(frame, self.__OnSize)
+
+    self.statusbar = frame.CreateStatusBar(3)
+    self.statusbar.SetStatusText ('Not Logged In', STATUS_USER)
+
+    topPanel = wxPanel(frame, -1)
+    topSizer = wxBoxSizer(wxHORIZONTAL)
+    topPanel.SetSizer(topSizer)
+    topPanel.SetAutoLayout(1)
+
+    bottomPanel = wxPanel(frame, -1)
+    bottomSizer = wxBoxSizer(wxHORIZONTAL)
+    bottomPanel.SetSizer(bottomSizer)
+    bottomPanel.SetAutoLayout(1)
+
+    self.receiptList = receiptList = wxListCtrl(frame, -1,
+              
style=wxLC_REPORT|wxLC_HRULES|wxLC_VRULES|wxLC_SINGLE_SEL|wxSIMPLE_BORDER)
+
+    # Add the columns for our receipt list
+    i = 0
+    for field, description, format, rightJustify, small in self.receiptColumns:
+      self.receiptList.InsertColumn(i, description,
+          format = wxLIST_FORMAT_RIGHT and rightJustify or wxLIST_FORMAT_LEFT)
+      if small:
+        self.receiptList.SetColumnWidth(i, 
self.receiptList.GetColumnWidth(i)/2)
+
+      i += 1
+
+    totalPanel2 = wxPanel(bottomPanel, -1, style=wxSIMPLE_BORDER)
+    totalSizer2 = wxBoxSizer(wxVERTICAL)
+    totalPanel2.SetSizer(totalSizer2)
+    totalPanel2.SetAutoLayout(1)
+
+    totalPanel = wxPanel(totalPanel2, -1)
+    totalSizer = wxFlexGridSizer(len(self.totalFields), 2, 2, 2)
+    totalPanel.SetSizer(totalSizer)
+    totalPanel.SetAutoLayout(1)
+
+    for name, descr in self.totalFields:
+      totalSizer.Add(wxStaticText(totalPanel, -1, descr + ': '), 0, 
wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 2)
+      field = NumberDisplay(totalPanel, name=='total')
+      totalSizer.Add(field, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 2)
+      self.totalFieldMapping[name] = field
+
+    entryPanel = wxPanel(bottomPanel, -1)
+    entrySizer = wxBoxSizer(wxVERTICAL)
+    entryPanel.SetSizer(entrySizer)
+    entryPanel.SetAutoLayout(1)
+    self.inputLabel = wxStaticText(entryPanel, -1, "Enter Product Code:")
+    self.inputField = wxTextCtrl(entryPanel, -1)
+    self.inputLabel.SetFont(bigFont)
+    self.inputField.SetFont(bigFont)
+    self.inputField.SetSize((self.inputField.GetSizeTuple()[0],30))
+    entrySizer.Add(self.inputLabel, 0, wxEXPAND|wxALIGN_LEFT)
+    entrySizer.Add(self.inputField, 0, wxEXPAND|wxALIGN_LEFT)
+
+    entryPanel.Fit()
+    totalPanel.Fit()
+    totalSizer2.Add(totalPanel, 1, wxALL|wxEXPAND, 10)
+    totalPanel2.Fit()
+
+#    bottomSizer.Add(wxPanel(bottomPanel,-1), 1, wxEXPAND)
+    bottomSizer.Add(entryPanel, 1, wxALIGN_CENTER|wxALL, 30)
+#    bottomSizer.Add(wxPanel(bottomPanel,-1), 1, wxEXPAND)
+    bottomSizer.Add(totalPanel2, 0, 
wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 20)
+    bottomPanel.Fit()
+
+    frameSizer.Add(topPanel, 0, wxEXPAND)
+    frameSizer.Add(receiptList, 3, wxEAST|wxWEST|wxEXPAND, 20)
+    frameSizer.Add(bottomPanel, 0, wxEXPAND)
+
+
+    totalSizer.Layout()
+    totalPanel.Fit()
+    topSizer.Layout()
+    bottomSizer.Layout()
+
+    frameSizer.Layout()
+#    self.frame.ShowFullScreen(1)
+    frame.SetSize((800,600))  # TODO: Maximize to screen?
+    frame.Show(1)
+
+    EVT_KEY_DOWN(self.inputField, self.__keypress)
+    self.__intext = 1
+    EVT_CHAR(self.inputField, self.__ontext)
+
+
+  def _EmployeeLogin(self, event):
+    self.statusbar.SetStatusText ('Clerk: %s (%s)' % (event.name, 
event.login), STATUS_USER)
+
+  def _Warning(self, event):
+    self.statusbar.SetStatusText(event.message)
+    wxBell();
+
+  def __OnSize(self, event):
+    self.frame.Layout()
+    w = self.receiptList.GetClientSizeTuple()[0]
+    pos = 0
+    for i in range(len(self.receiptColumns)):
+      if self.receiptColumns[i][0] == 'description':
+        pos = i
+      else:
+        w -= self.receiptList.GetColumnWidth(i)
+    self.receiptList.SetColumnWidth(pos, w)
+
+  def __keypress(self, event):
+    key = event.GetKeyCode()
+
+##    print  _specialKeys, key, self.__endingEvents
+##    print key
+
+    if key in (WXK_RETURN,WXK_NUMPAD_ENTER):
+      self.dispatchEvent(self.__event, value=self.inputField.GetValue())
+    elif key == WXK_ESCAPE:
+      self.inputField.Clear()
+    elif _specialKeys.has_key(key) and _specialKeys[key] in 
self.__endingEvents:
+      self.dispatchEvent(self.__event, value=self.inputField.GetValue(), 
next=_specialKeys[key])
+    elif self.__event == 'CancelItemEntered' and key in (WXK_UP, WXK_DOWN):
+      c = self.receiptList.GetItemCount()
+      for r in range(c):
+        if self.receiptList.GetItemState(r,wxLIST_STATE_SELECTED):
+          break
+
+      st = ""
+      
self.receiptList.SetItemState(r,0,wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED)
+      if key == WXK_UP:
+        if r > 0:
+          st = "%d" % (r)
+        else:
+          st = "1"
+      elif key == WXK_DOWN:
+        if r < c - 1:
+          st = "%d" % (r + 2)
+        else:
+          st = "%d" % (c)
+      if len(st):
+        self.inputField.SetValue(st)
+        self.inputField.SetSelection(0, len(st))
+        
self.receiptList.SetItemState(int(st)-1,wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED,wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED)
+
+    else:
+      event.Skip()
+
+  def __ontext(self, event):
+    key = event.GetKeyCode()
+    if key != 32:
+      wxCallAfter(self.__fixtext)
+    event.Skip()
+
+
+  def __fixtext(self):
+    ip = self.inputField.GetInsertionPoint()
+    self.inputField.SetValue(string.capwords(self.inputField.GetValue()))
+    self.inputField.SetInsertionPoint(ip)
+
+
+
+_specialKeys = {
+
+   WXK_F1:  'RequestTender:Cash',
+   WXK_F2:  'RequestTender:Check',
+   WXK_F3:  'RequestTender:CreditCard',
+   WXK_F4:  'RequestTender:Gift',
+
+   WXK_F5:  'RequestBeginLayaway',
+   WXK_F6:  'RequestLayawayPayment',
+   WXK_F7:  'RequestEndLayaway',
+
+   WXK_F9: 'RequestCancelTicket',
+   WXK_F12: 'RequestTaxExemption',
+
+   WXK_MULTIPLY: 'RequestQuantity',
+   WXK_DIVIDE: 'RequestCancelItem',
+   WXK_SUBTRACT: 'RequestCancelLastItem',
+   WXK_ADD: 'RequestDuplicateLastItem',
+
+   # Grrrrr.... WX's keymaps above don't seem to be right
+   391: 'RequestQuantity',
+   396: 'RequestCancelItem',
+   394: 'RequestCancelLastItem',
+   392: 'RequestDuplicateLastItem',
+
+   #
+   310: 'RequestLogOut',
+   321: 'RequestReprint',
+
+  }
+
+
+_keyButtons = (
+     ('F1','Tender Cash'),
+     ('F2','Tender Check'),
+     ('F3','Tender Credit'),
+     ('F4','Tender Gift'),
+     ('F5','New Layaway'),
+     ('F6','Layaway Payment'),
+     ('F7','Complete Layaway'),
+     ('F9','Cancel Ticket'),
+     ('F12','Tax Exempt Order'),
+     ('Print','Reprint Ticket'),
+     ('Break', 'Log Out'),
+     ('/','Cancel Item'),
+     ('*','Enter Quantity'),
+     ('+','Duplicate Item') )
+
+
+class ButtonPanel(wxPanel):
+  def __init__(self, parent, driver):
+    self.driver = driver
+    wxPanel.__init__(self, parent, -1)
+
+
+
+# F1 = Tender Cash
+# F2 = Tender Check
+# F3 = Tender Credit Card
+# F4 = Tender Gift Cert
+#
+# F5 = Begin Layaway
+# F6 = Make Layaway Payment
+# F7 = End Layaway
+#
+# F9 = Cancel Ticket
+# F12 = Tax Exemption
+#
+# PrnScrn = Reprint Ticket
+# Break = Log Out
+#
+# / = Cancel Item
+# * = Enter Quantity
+# + = Duplicate Last Item

Added: gnue-pointofsale/src/frontends/wx/NumberDisplay.py
===================================================================
--- gnue-pointofsale/src/frontends/wx/NumberDisplay.py  2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/frontends/wx/NumberDisplay.py  2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,62 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# wx/NumberDisplay.py
+#
+# DESCRIPTION:
+# class that displays numbers (dollars) in bolded, right-aligned fields
+#
+# NOTES:
+#
+
+from wxPython.wx import *
+
+bigFont = wxFont(16, wxSWISS, wxNORMAL, wxBOLD)
+#bigFont = wxSystemSettings_GetFont(wxSYS_SYSTEM_FONT)
+bigFont.SetWeight(wxBOLD)
+bigFont.SetPointSize(18)
+bigFont.SetStyle(wxNORMAL)
+
+class NumberDisplay(wxPanel):
+  def __init__(self, parent, reversed=0):
+    wxPanel.__init__(self, parent, -1, style=wxSIMPLE_BORDER)
+    sizer = wxBoxSizer(wxHORIZONTAL)
+##    self.SetSizer(sizer)
+##    self.SetAutoLayout(1)
+#    EVT_SIZE(frame, self.__OnSize)
+    self.text = wxStaticText(self,-1,"$0.00",(4,4),style = 
wxALIGN_RIGHT|wxST_NO_AUTORESIZE)
+##    sizer.Add(self.text, 0, wxALIGN_RIGHT)
+    self.SetValue = self.text.SetLabel
+
+    light = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNHILIGHT)
+    dark =  
wxColour(0)#wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNSHADOW)
+    if reversed:
+      self.SetBackgroundColour(dark)
+      self.text.SetForegroundColour(light)
+    else:
+      self.SetBackgroundColour(light)
+      self.text.SetForegroundColour(dark)
+
+    self.text.SetFont(bigFont)
+    self.text.Refresh()
+
+    self.text.SetSize((150,20))
+    self.SetSize((160,30))

Added: gnue-pointofsale/src/frontends/wx/__init__.py
===================================================================
--- gnue-pointofsale/src/frontends/wx/__init__.py       2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/frontends/wx/__init__.py       2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,33 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# wx/__init__.py
+#
+# DESCRIPTION:
+# wxPython driver file for GNUe POS frontends.
+# Borrows/integrates heavily with the GNUe-Forms wxPython driver.
+#
+# NOTES:
+#
+
+from Driver import Driver
+
+

Added: gnue-pointofsale/src/hardware/__init__.py
===================================================================

Added: gnue-pointofsale/src/hardware/barcodereader/__init__.py
===================================================================

Added: gnue-pointofsale/src/hardware/cashdrawer/__init__.py
===================================================================

Added: gnue-pointofsale/src/hardware/cashdrawer/bell.py
===================================================================
--- gnue-pointofsale/src/hardware/cashdrawer/bell.py    2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/hardware/cashdrawer/bell.py    2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,52 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# cashdrawers/bell.py
+#
+# DESCRIPTION:
+# Cash Drawer driver (for all cash drawers using ASCII 7 (BELL)
+# as the code to open the drawer. These drawers usually are
+# connected to a parallel or serial port.
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from gnue.common.events.EventAware import EventAware
+from gnue.pointofsale.IOProxy import simpleOpen
+
+class Driver(EventAware):
+
+  def __init__(self, instance, params):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+    self.__dict__.update(params)
+    self.device_params = params
+
+    self.registerEventListeners({'EndTicket': self._open})
+
+
+  def _open(self, event):
+    p = simpleOpen(self.device_params)
+    p.write('\007')
+    p.close()
+

Added: gnue-pointofsale/src/hardware/cashdrawer/escpos.py
===================================================================
--- gnue-pointofsale/src/hardware/cashdrawer/escpos.py  2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/hardware/cashdrawer/escpos.py  2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,51 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# cashdrawers/escpos.py
+#
+# DESCRIPTION:
+# Cash Drawer driver for all cash drawers using Epson ESC/POS
+# codes. These drawers usually are connected to a receipt printer
+# via a phone cable-type jack.
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from gnue.common.events.EventAware import EventAware
+from gnue.pointofsale.IOProxy import simpleOpen
+
+class Driver(EventAware):
+
+  def __init__(self, instance, params):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+    self.__dict__.update(params)
+    self.device_params = params
+    self.registerEventListeners({'EndTicket': self.open})
+
+
+  def open(self, event):
+    p = simpleOpen(self.device_params)
+    p.write('\x1bp\x002\xfa')
+    p.close()
+

Added: gnue-pointofsale/src/hardware/logprinter/plain.py
===================================================================
--- gnue-pointofsale/src/hardware/logprinter/plain.py   2005-04-19 12:09:26 UTC 
(rev 218)
+++ gnue-pointofsale/src/hardware/logprinter/plain.py   2005-04-23 12:05:40 UTC 
(rev 219)
@@ -0,0 +1,311 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# receiptprinters/plain.py
+#
+# DESCRIPTION:
+# Receipt Printer driver (plain-text)
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from gnue.common.events.EventAware import EventAware
+from gnue.common.utils.TextUtils import *
+from gnue.pointofsale.IOProxy import simpleOpen
+from cStringIO import StringIO
+import time
+
+try:
+  import Image
+except:
+  Image = None
+
+dollarFormat = '$%.02f'
+
+BLACK = 0
+RED = 1
+
+
+class Driver(EventAware):
+
+  ##########################################################
+  #
+  #
+
+  def init(self):
+    pass
+
+
+  ##########################################################
+  #
+  #
+
+  def __init__(self, instance, params):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+    self.__dict__.update(params)
+    self.device_params = params
+    self.output = ""
+    self.totals = {}
+    self.newline = "\n"
+
+    print self.dest
+
+    # Get the logo
+    logo = gConfig("ReceiptLogo")
+
+    # TODO: Disabling until I get the ESC/POS stuff worked out
+    if 0: # logo and Image:
+      self.logo = Image.open(logo)
+    else:
+      self.logo = None
+
+    self.width = hasattr(self,'width') and self.width or 40
+
+    self.totalFields = (  ('subtotal',_('Subtotal')),
+                          ('tax','MS Sales Tax (7%)'),
+                          ('total',_('Total')) )
+
+    self.totalFieldMapping = {}
+
+    self.registerEventListeners( {
+      'BeginTicket': self._BeginTicket,
+      'ReprintTicket': self.printTicket,
+      'EndTicket': self._EndTicket,
+      'CancelTicket': self._CancelTicket,
+      'AddTender': self._AddTender,
+      'AddTicketItem': self._AddTicketItem,
+      'CancelTicketItem': self._CancelTicketItem,
+      'UpdateTotal': self._UpdateTotal,
+      'ReceiptPrinter:ReceiptTorn': self._ReceiptTorn } )
+
+    self.init()
+
+  #
+  #
+  #
+  def printTicket(self, event=None):
+    if not self.output:
+      return
+
+    d = simpleOpen(self.device_params)
+    self.output.seek(0)
+    d.write(self.output.read())
+    d.close()
+
+
+  def printHeader(self):
+    self.output.write(self.reset())
+    if self.logo:
+      self.write(self.drawImage(self.logo))
+    self.write( gConfig("ReceiptHeader").replace('\\n','\n'), 
alignment=ALIGN_CENTER)
+
+
+  def printFooter(self):
+    self.write( gConfig("ReceiptFooter").replace('\\n','\n'), 
alignment=ALIGN_CENTER, color=RED)
+
+    shouter = self.dispatchEvent('Receipt:GetNotice')
+    shouter = """\
+Did you know?  Old Towne Market has
+a professional portrait studio. Let
+us take your next family portrait,
+senior picture, or holiday photo."""
+
+    if shouter:
+      self.write('\n***************************************', indent=1, 
color=1)
+      self.write(shouter, indent=2)
+      self.write('***************************************', indent=1, color=1)
+
+  #
+  #
+  #
+  def _BeginTicket(self, event):
+    try:
+      self.output.close()
+    except:
+      pass
+
+    self.tenders = []
+
+    self.output = StringIO()
+    self.doublespace = 0
+    self.totals = {}
+
+    self.printHeader()
+    self.write("\nSALES RECEIPT", double=1, alignment=ALIGN_CENTER)
+    self.write("""
+----------------------------------------
+Trans #%s  %s  %s
+""" % (event.serial, time.strftime("%m/%d/%y 
%H:%M:%S",time.localtime(time.time())),self.instance.handler._login))
+
+  def _CancelTicket(self, event):
+    self.write('\n**** CANCELED ****\n', color=RED, double=1, 
alignment=ALIGN_CENTER)
+    self.output.write(self.formfeed())
+    self.printTicket()
+
+  def _EndTicket(self, event):
+    self.output.write("                             ----------\n")
+    for field, display in self.totalFields:
+      try:
+        amt = self.totals[field]
+      except KeyError:
+        amt = 0
+
+      self.write((display + ":").ljust(28)[:28]+ " " + comify(amt).rjust(10))
+
+    self.output.write("                             ----------\n")
+    for tender in self.tenders:
+      self.write(tender)
+##    self.write("\n----------------------------------------")
+    self.write("")
+    self.printFooter()
+    self.output.write(self.formfeed())
+    self.printTicket()
+
+
+  def _AddTicketItem(self, event):
+
+    self.write(str(event.department or "").ljust(3)[:3] + " " + \
+               str(event.description or "").upper().ljust(24)[:24] + " " + \
+               comify(event.extended,2).rjust(10) + \
+               (event.taxable and '*' or ' '))
+
+
+  def _AddTender(self, event):
+    # TODO
+
+    self.tenders.append (string.capwords('TENDER ' + 
event.type).ljust(28)[:28] + " " + \
+               comify(event.amount).rjust(10))
+
+  def _CancelTicketItem(self, event):
+    pass
+
+  def _UpdateTotal(self, event):
+    self.totals[event.field] = event.amount
+
+  def write(self, text, double=0, bold=0, color=BLACK, underline=0,
+            doubleheight=0, alignment=ALIGN_LEFT, eol=1, width=0, indent=0):
+
+    splittext = str(text).split('\n')
+    i = 0
+    linehead = ""
+
+    for line in splittext:
+      divisor = 1
+
+      if underline:
+        self.output.write(self.beginUnderline())
+      if doubleheight:
+        self.output.write(self.beginDoubleHeight())
+      if double:
+        self.output.write(self.beginDouble())
+      if color:
+        self.output.write(self.beginColor(color))
+      if bold:
+        self.output.write(self.beginBold())
+
+      if self.doublespace:
+        l = ""
+        for char in tuple(line):
+          l += char + ' '
+        line = l[:-1]
+      elif double:
+        divisor = 2
+
+      self.output.write(' '*indent + lineWrap(line, alignment=alignment, 
eol=0, maxWidth=int((width or self.width)/divisor)-indent).replace('\n','\n' + 
' '* indent))
+
+      if bold:
+        self.output.write(self.endBold())
+      if color:
+        self.output.write(self.endColor(color))
+      if double:
+        self.output.write(self.endDouble())
+      if doubleheight:
+        self.output.write(self.endDoubleHeight())
+      if underline:
+        self.output.write(self.endUnderline())
+
+      if i < len(splittext)-1:
+        self.output.write(self.newline)
+
+      i += 1
+
+    if eol:
+      self.output.write(self.newline)
+
+  def writeTuple(self, list):
+    i = 0
+    for set in list:
+      if i != len(list) - 1:
+        list['eol'] = 0
+      self.write(**list)
+
+  def reset(self):
+    return ""
+
+  def formfeed(self):
+    return '\n\n\n\n\n\n\n'
+
+  def beginColor(self):
+    return ""
+
+  def endColor(self):
+    return ""
+
+  def beginBold(self):
+    return ""
+
+  def endBold(self):
+    return ""
+
+  def beginDouble(self):
+    self.doublespace = 1
+    return ""
+
+  def endDouble(self):
+    self.doublespace = 0
+    return ""
+
+  def beginDoubleHeight(self):
+    return ""
+
+  def endDoubleHeight(self):
+    return ""
+
+  def beginUnderline(self):
+    return ""
+
+  def endUnderline(self):
+    return ""
+
+  def drawImage(self, image):
+    return ""
+
+  def cutBetweenSheets(self):
+    self.__receiptTorn = 0
+    self.dispatchEvent('Prompt', label='Tear off receipt and press enter:', 
response='ReceiptPrinter:ReceiptTorn')
+    while not self.__receiptTorn:
+      pass
+
+  def _ReceiptTorn(self, event):
+    self.__receiptTorn = 1

Added: gnue-pointofsale/src/hardware/poledisplay/__init__.py
===================================================================

Added: gnue-pointofsale/src/hardware/receiptprinter/__init__.py
===================================================================

Added: gnue-pointofsale/src/hardware/receiptprinter/escpos.py
===================================================================
--- gnue-pointofsale/src/hardware/receiptprinter/escpos.py      2005-04-19 
12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/hardware/receiptprinter/escpos.py      2005-04-23 
12:05:40 UTC (rev 219)
@@ -0,0 +1,123 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# receiptprinters/escpos.py
+#
+# DESCRIPTION:
+# Receipt Printer driver (Epson ESC/POS-based)
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from plain import Driver as BaseDriver
+
+class Driver(BaseDriver):
+
+  ##########################################################
+  #
+  #
+
+  def init(self):
+    self.colors = hasattr(self,'colors') and self.colors or 1
+    self.__double = 0
+    self.__underline = 0
+    self.__doubleheight = 0
+    self.__bold = 0
+
+  def reset(self):
+    return "\x1B@"
+
+  def formfeed(self):
+    return '\n\n\n\n\n\n\n\n'
+
+  def beginColor(self, color):
+    if self.colors == 1:
+      return ""
+    else:
+      return "\x1Br" + chr(color)
+
+  def endColor(self, color):
+    if self.colors == 1:
+      return ""
+    else:
+      return "\x1Br\x00"
+
+  def beginBold(self):
+    self.__bold = 1
+    return self.__setPrintMode()
+
+  def endBold(self):
+    self.__bold = 0
+    return self.__setPrintMode()
+
+  def beginDouble(self):
+    self.__double = 1
+    return self.__setPrintMode()
+
+  def endDouble(self):
+    self.__double = 0
+    return self.__setPrintMode()
+
+  def beginDoubleHeight(self):
+    self.__doubleheight = 1
+    return self.__setPrintMode()
+
+  def endDoubleHeight(self):
+    self.__doubleheight = 0
+    return self.__setPrintMode()
+
+  def beginUnderline(self):
+    self.__underline = 1
+    return self.__setPrintMode()
+
+  def endUnderline(self):
+    self.__underline = 0
+    return self.__setPrintMode()
+
+  def __setPrintMode(self):
+    return "\x1B!" + chr(self.__bold * 8 | self.__doubleheight * 16 | \
+                         self.__double * 32 | self.__underline * 128)
+
+  def drawImage(self, image):
+    logoOffset = 0
+
+    lw, lh = image.size
+    vw = min(round(lw/8.0+.5),45)
+    vh = min(round(lh/8.0+.5),24)
+    uw = vw * 8
+    uh = vh * 8
+
+    rs = "\x1D\x2A" + chr(vw) + chr(vh) + "\x00"
+
+    for y in range(vh):
+      for x in range(uw):
+        byte = 0
+        for bit in range(8):
+          try:
+            byte |= 2**bit and (image.getpixel((x, (y+1)*8 - (bit+1)*8)) > 0)
+          except IndexError:
+            pass
+        rs += chr(byte)
+
+    return rs + "\x1D\x2F\x00"
+

Added: gnue-pointofsale/src/hardware/receiptprinter/plain.py
===================================================================
--- gnue-pointofsale/src/hardware/receiptprinter/plain.py       2005-04-19 
12:09:26 UTC (rev 218)
+++ gnue-pointofsale/src/hardware/receiptprinter/plain.py       2005-04-23 
12:05:40 UTC (rev 219)
@@ -0,0 +1,320 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2003-2005 Free Software Foundation
+#
+# FILE:
+# receiptprinters/plain.py
+#
+# DESCRIPTION:
+# Receipt Printer driver (plain-text)
+#
+# NOTES:
+#
+
+__all__ = ['Driver']
+
+from gnue.common.events.EventAware import EventAware
+from gnue.common.utils.TextUtils import *
+from gnue.pointofsale.IOProxy import simpleOpen
+from cStringIO import StringIO
+import time
+
+try:
+  import Image
+except:
+  Image = None
+
+dollarFormat = '$%.02f'
+
+BLACK = 0
+RED = 1
+
+
+class Driver(EventAware):
+
+  ##########################################################
+  #
+  #
+
+  def init(self):
+    pass
+
+
+  ##########################################################
+  #
+  #
+
+  def __init__(self, instance, params):
+    EventAware.__init__(self, instance)
+    self.instance = instance
+    self.__dict__.update(params)
+    self.device_params = params
+    self.output = ""
+    self.totals = {}
+    self.newline = "\n"
+
+    # Get the logo
+    logo = gConfig("ReceiptLogo")
+
+    # TODO: Disabling until I get the ESC/POS stuff worked out
+    if 0: # logo and Image:
+      self.logo = Image.open(logo)
+    else:
+      self.logo = None
+
+    self.width = hasattr(self,'width') and self.width or 40
+
+    self.totalFields = (  ('subtotal',_('Subtotal')),
+                          ('tax','MS Sales Tax (7%)'),
+                          ('total',_('Total')) )
+
+    self.totalFieldMapping = {}
+
+    self.registerEventListeners( {
+      'BeginTicket': self._BeginTicket,
+      'ReprintTicket': self.printTicket,
+      'EndTicket': self._EndTicket,
+      'CancelTicket': self._CancelTicket,
+      'AddTender': self._AddTender,
+      'AddTicketItem': self._AddTicketItem,
+      'CancelTicketItem': self._CancelTicketItem,
+      'UpdateTotal': self._UpdateTotal,
+      'ReceiptPrinter:ReceiptTorn': self._ReceiptTorn } )
+
+    self.init()
+
+  #
+  #
+  #
+  def printTicket(self, event=None):
+    if not self.output:
+      return
+
+    try:
+      copies = int(self.device_params['copies'])
+    except:
+      copies = 1
+
+    while copies:
+      d = simpleOpen(**self.device_params)
+      self.output.seek(0)
+      d.write(self.output.read())
+      d.close()
+      copies -= 1
+      if copies:
+        self.cutBetweenSheets()
+
+
+
+  def printHeader(self):
+    self.output.write(self.reset())
+    if self.logo:
+      self.write(self.drawImage(self.logo))
+    self.write( gConfig("ReceiptHeader").replace('\\n','\n'), 
alignment=ALIGN_CENTER)
+
+
+  def printFooter(self):
+    self.write( gConfig("ReceiptFooter").replace('\\n','\n'), 
alignment=ALIGN_CENTER, color=RED)
+
+    shouter = self.dispatchEvent('Receipt:GetNotice')
+##    shouter = """\
+##Did you know?  Old Towne Market has
+##a professional portrait studio. Let
+##us take your next family portrait,
+##senior picture, or holiday photo."""
+
+    if shouter:
+      self.write('\n***************************************', indent=1, 
color=1)
+      self.write(shouter, indent=2)
+      self.write('***************************************', indent=1, color=1)
+
+  #
+  #
+  #
+  def _BeginTicket(self, event):
+    try:
+      self.output.close()
+    except:
+      pass
+
+    self.tenders = []
+
+    self.output = StringIO()
+    self.doublespace = 0
+    self.totals = {}
+
+    self.printHeader()
+    self.write("\nSALES RECEIPT", double=1, alignment=ALIGN_CENTER)
+    self.write("""
+----------------------------------------
+Trans #%s  %s  %s
+""" % (event.serial, time.strftime("%m/%d/%y 
%H:%M:%S",time.localtime(time.time())),self.instance.handler._login))
+
+  def _CancelTicket(self, event):
+    self.write('\n**** CANCELED ****\n', color=RED, double=1, 
alignment=ALIGN_CENTER)
+    self.output.write(self.formfeed())
+    self.printTicket()
+
+  def _EndTicket(self, event):
+    self.output.write("                             ----------\n")
+    for field, display in self.totalFields:
+      try:
+        amt = self.totals[field]
+      except KeyError:
+        amt = 0
+
+      self.write((display + ":").ljust(28)[:28]+ " " + comify(amt).rjust(10))
+
+    self.output.write("                             ----------\n")
+    for tender in self.tenders:
+      self.write(tender)
+##    self.write("\n----------------------------------------")
+    self.write("")
+    self.printFooter()
+    self.output.write(self.formfeed())
+    self.printTicket()
+
+
+  def _AddTicketItem(self, event):
+    if event.quantity > 1:
+      self.output.write('    %s x $%.02f ea\n' % (event.quantity, event.each))
+    self.write(str(event.department or "").ljust(3)[:3] + " " + \
+               str(event.description or "").upper().ljust(24)[:24] + " " + \
+               comify(event.extended,2).rjust(10) + \
+               (event.taxable and '*' or ' '))
+
+
+  def _AddTender(self, event):
+    # TODO
+
+    self.tenders.append (string.capwords('TENDER ' + 
event.type).ljust(28)[:28] + " " + \
+               comify(event.amount).rjust(10))
+
+  def _CancelTicketItem(self, event):
+    pass
+
+  def _UpdateTotal(self, event):
+    self.totals[event.field] = event.amount
+
+  def write(self, text, double=0, bold=0, color=BLACK, underline=0,
+            doubleheight=0, alignment=ALIGN_LEFT, eol=1, width=0, indent=0):
+
+    splittext = str(text).split('\n')
+    i = 0
+    linehead = ""
+
+    for line in splittext:
+      divisor = 1
+
+      if underline:
+        self.output.write(self.beginUnderline())
+      if doubleheight:
+        self.output.write(self.beginDoubleHeight())
+      if double:
+        self.output.write(self.beginDouble())
+      if color:
+        self.output.write(self.beginColor(color))
+      if bold:
+        self.output.write(self.beginBold())
+
+      if self.doublespace:
+        l = ""
+        for char in tuple(line):
+          l += char + ' '
+        line = l[:-1]
+      elif double:
+        divisor = 2
+
+      self.output.write(' '*indent + lineWrap(line, alignment=alignment, 
eol=0, maxWidth=int((width or self.width)/divisor)-indent).replace('\n','\n' + 
' '* indent))
+
+      if bold:
+        self.output.write(self.endBold())
+      if color:
+        self.output.write(self.endColor(color))
+      if double:
+        self.output.write(self.endDouble())
+      if doubleheight:
+        self.output.write(self.endDoubleHeight())
+      if underline:
+        self.output.write(self.endUnderline())
+
+      if i < len(splittext)-1:
+        self.output.write(self.newline)
+
+      i += 1
+
+    if eol:
+      self.output.write(self.newline)
+
+  def writeTuple(self, list):
+    i = 0
+    for set in list:
+      if i != len(list) - 1:
+        list['eol'] = 0
+      self.write(**list)
+
+  def reset(self):
+    return ""
+
+  def formfeed(self):
+    return '\n\n\n\n\n\n\n'
+
+  def beginColor(self):
+    return ""
+
+  def endColor(self):
+    return ""
+
+  def beginBold(self):
+    return ""
+
+  def endBold(self):
+    return ""
+
+  def beginDouble(self):
+    self.doublespace = 1
+    return ""
+
+  def endDouble(self):
+    self.doublespace = 0
+    return ""
+
+  def beginDoubleHeight(self):
+    return ""
+
+  def endDoubleHeight(self):
+    return ""
+
+  def beginUnderline(self):
+    return ""
+
+  def endUnderline(self):
+    return ""
+
+  def drawImage(self, image):
+    return ""
+
+  def cutBetweenSheets(self):
+    self.__receiptTorn = 0
+    self.dispatchEvent('Prompt', label='Tear off receipt and press enter:', 
response='ReceiptPrinter:ReceiptTorn')
+    while not self.__receiptTorn:
+      pass
+
+  def _ReceiptTorn(self, event):
+    self.__receiptTorn = 1

Added: gnue-pointofsale/src/payments/__init__.py
===================================================================

Modified: hotline/forms/hotline-en.gfd
===================================================================
--- hotline/forms/hotline-en.gfd        2005-04-19 12:09:26 UTC (rev 218)
+++ hotline/forms/hotline-en.gfd        2005-04-23 12:05:40 UTC (rev 219)
@@ -196,10 +196,10 @@
     </block>
 
     <block name="blkSelect">
-      <field name="fldMitarbeiter" field="hotline_mitarbeiter" fk_key="gnue_id"
-        fk_description="hotline_name" fk_source="dtsContributor"/>
+      <field name="fldMitarbeiter" fk_source="dtsContributor" fk_key="gnue_id"
+        fk_description="hotline_name" />
       <field name="fldStart"/>
-      <field name="fldKunde" field="hotline_kunde" case="upper"/>
+      <field name="fldKunde" case="upper"/>
       <field name="fldName"/>
     </block>
 

Modified: hotline/forms/kunden.gfd
===================================================================
--- hotline/forms/kunden.gfd    2005-04-19 12:09:26 UTC (rev 218)
+++ hotline/forms/kunden.gfd    2005-04-23 12:05:40 UTC (rev 219)
@@ -27,7 +27,11 @@
 <form title="Kunden / Kontakte">
   <options/>
   <datasource name="dtsHotlineKunde" connection="hotline"
-    table="hotline_kunde"/>
+    table="hotline_kunde">
+    <sortorder>
+      <sortfield name="hotline_nummer"/>
+    </sortorder>
+  </datasource>
   <datasource name="dtsHotlineLand" connection="hotline"
     table="hotline_land" prequery="Y"/>
   <datasource name="dtsHotlineKontakt" connection="hotline"
@@ -147,39 +151,39 @@
       <entry name="entHotlineBemerkung" Char:height="1" Char:width="50"
         Char:x="20" Char:y="13" block="blkHotlineKunde"
         field="fldHotlineBemerkung" label=""/>
-    </page>
-    <page name="Kontakte">
+
+      <!-- Contacts -->
       <label name="lblName" Char:height="1" Char:width="4" Char:x="1"
-        Char:y="1" text="Name"/>
+        Char:y="14" text="Name"/>
       <label name="lblBemerkung" Char:height="1" Char:width="9" Char:x="37"
-        Char:y="1" text="Bemerkung"/>
+        Char:y="14" text="Bemerkung"/>
       <entry name="entHotlineName" Char:height="1" Char:width="35" Char:x="1"
-        Char:y="2" block="blkHotlineKontakt" field="fldHotlineName" label=""
+        Char:y="15" block="blkHotlineKontakt" field="fldHotlineName" label=""
         rows="10"/>
       <entry name="entHotlineBemerkung" Char:height="1" Char:width="35"
-        Char:x="37" Char:y="2" block="blkHotlineKontakt"
+        Char:x="37" Char:y="15" block="blkHotlineKontakt"
         field="fldHotlineBemerkung" label="" rows="10"/>
 
 
       <label name="lblTelefon" Char:height="1" Char:width="7" Char:x="1"
-        Char:y="13" text="Telefon"/>
+        Char:y="26" text="Telefon"/>
       <entry name="entHotlineTelefon" Char:height="1" Char:width="62"
-        Char:x="10" Char:y="13" block="blkHotlineKontakt"
+        Char:x="10" Char:y="26" block="blkHotlineKontakt"
         field="fldHotlineTelefon" label=""/>
       <label name="lblHandy" Char:height="1" Char:width="5" Char:x="1"
-        Char:y="14" text="Handy"/>
+        Char:y="27" text="Handy"/>
       <entry name="entHotlineHandy" Char:height="1" Char:width="62" Char:x="10"
-        Char:y="14" block="blkHotlineKontakt" field="fldHotlineHandy"
+        Char:y="27" block="blkHotlineKontakt" field="fldHotlineHandy"
         label=""/>
       <label name="lblTelefax" Char:height="1" Char:width="7" Char:x="1"
-        Char:y="15" text="Telefax"/>
+        Char:y="28" text="Telefax"/>
       <entry name="entHotlineTelefax" Char:height="1" Char:width="62"
-        Char:x="10" Char:y="15" block="blkHotlineKontakt"
+        Char:x="10" Char:y="28" block="blkHotlineKontakt"
         field="fldHotlineTelefax" label=""/>
       <label name="lblEMail" Char:height="1" Char:width="6" Char:x="1"
-        Char:y="16" text="E-Mail"/>
+        Char:y="29" text="E-Mail"/>
       <entry name="entHotlineEmail" Char:height="1" Char:width="62" Char:x="10"
-        Char:y="16" block="blkHotlineKontakt" field="fldHotlineEmail"
+        Char:y="29" block="blkHotlineKontakt" field="fldHotlineEmail"
         label=""/>
     </page>
   </layout>





reply via email to

[Prev in Thread] Current Thread [Next in Thread]