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

SF.net SVN: ledger-smb:[4641] addons/1.3/wxPOS



Revision: 4641
          http://ledger-smb.svn.sourceforge.net/ledger-smb/?rev=4641&view=rev
Author:   hasorli
Date:     2012-04-12 09:20:29 +0000 (Thu, 12 Apr 2012)
Log Message:
-----------
WX POS (almost there) -  Update code from Andres Basile (..hidden..) ID: 3516949

Modified Paths:
--------------
    addons/1.3/wxPOS/LedgerSMB.pl
    addons/1.3/wxPOS/scripts/AR.pm
    addons/1.3/wxPOS/scripts/IC.pm

Added Paths:
-----------
    addons/1.3/wxPOS/README
    addons/1.3/wxPOS/ledgersmb.png
    addons/1.3/wxPOS/scripts/Console.pm
    addons/1.3/wxPOS/scripts/LSMBDB.pm
    addons/1.3/wxPOS/scripts/Login.pm

Modified: addons/1.3/wxPOS/LedgerSMB.pl
===================================================================
--- addons/1.3/wxPOS/LedgerSMB.pl	2012-04-11 03:58:10 UTC (rev 4640)
+++ addons/1.3/wxPOS/LedgerSMB.pl	2012-04-12 09:20:29 UTC (rev 4641)
@@ -1,23 +1,38 @@
 #!/usr/bin/perl -w
+=head1 NAME
 
+LedgerSMB - Wx Client
+
+=head1 SYNOPSIS
+
+This is the base executable that starts the whole application.
+
+=back
+
+=head1 AUTHOR
+
+Andres Basile (..hidden..)
+
+=cut
+
 use strict;
 use Wx;
-use lib "scripts";
 
 package LsmbWx;
 use base 'Wx::App';
 
 sub OnInit {
  my ($class) = @_;
- use ConsoleWx;
- my $console = ConsoleWx->new();
+ $class->SetAppName( "LedgerSMB" );
+ use lib "scripts";
+ use Console;
+ my $console = Console->new();
+ $console->SetIcon( Wx::GetWxPerlIcon() );
+ $console->Show(1);
  $class->SetTopWindow($console);
- $console->Show(1);
- $console->SetIcon( Wx::GetWxPerlIcon() );
  return 1;
 }
 
-# LooP
 package main;
 
 my $app = LsmbWx->new;

Added: addons/1.3/wxPOS/README
===================================================================
--- addons/1.3/wxPOS/README	                        (rev 0)
+++ addons/1.3/wxPOS/README	2012-04-12 09:20:29 UTC (rev 4641)
@@ -0,0 +1,58 @@
+LedgerSMB WX CLient
+GNU/GPL idem LedgerSMB
+
+This code is a Work In Progress, my apologies for the hardcodes, comments and other stuff that is not completed.
+Andres Basile (..hidden..).-
+
+URGENT TODO:
+-Finish invoice post.
+-Printing invoice.
+
+I tried to write a kind of framework where it would be easy to expand modules to accomplish differents works like a standalone client that works against the database.
+
+-INSTALLING:
+As root you need to install WX Perl libraries:
+# cpan -i wx
+
+Once that, you need to select a valid LedgerSMB user and add it an entity_note like:
+(the note will contain a subject as: "WX" and a note like a hash dump: VAR1 = {...})
+
+entity_note.class = 1
+entity_note.note = $VAR1 = {'wh' => '1', 'custom' => 'default customer'}
+entity_note.subject = WX
+entity_note.entity_id = entity.id (for that user)
+
+-RUNNING:
+TIP:
+Like a secure line is encripted and a database should be listen locally, I use ssh with compresion in order to get a better connection:
+
+$ ssh -C -L 5432:localhost:5432 myServer.com
+
+As user:
+$ ./LedgerSMB.pl
+
+-UNDERSTANDING:
+I will try to explain my thinking on this code, I hope someone found it useful for its own use. I'm trying to leave each file with its own pod documentation explaining how to use the class and methods.
+
+LedgerSMB.pl: This is the base executable that init WX framework.
+
+scripts/Console.pm: It is the main window and works as a handler for the modules.
+
+scripts/Login.pm: It is a dialog window used once at start, it asks for credentials in order to stablish a database connection, if this is acomplished, it will pass the object to the Console and it will be saved on a global hash. Also it will get user preferences. (PLEASE, take in mind that right now it is saved as a user note with subject WX and it is mandatory to run the application)
+
+scripts/LSMBDB.pm: This would be an interface between WX dialogs and LedgerSMB database. I choose to write a single class with a lot of functions in order to re use them between differents modules. In an ideal world, it would get the variables and pass them to functions/store procedures on the db.
+
+scripts/IC.pm: The first module. Each module will be its own action handler, this mean that new() method will get an $action argument and should be able to resolve it by itself. It will get a $self->{sesion} hash that is a global for the whole application.
+*Parts: It checks for parts avaiables on a warehouse and list them. It looks for the user warehouse in order to set it as default for the search.
+
+scripts/AR.pm: 
+*Invoice: Here I'm trying a little more complicated module, what I'm looking for is a easy to human till screen where once selected the customer, the parts are readed by a barcode scan en everything under the hood is automatically done leaving the invoice ready to post.
+*Transaction: It would be for that.
+*Customer: It would be like an address book of customer.
+
+-EXTENDING:
+To add a new module you should:
+1. Add a Menu entry pointing to: $self->load_module($MODULE_NAME, $ACTION) on Console.pm
+2. Add a couple of methods on LSMBDB.pm to interact with the database.
+3. Add a file $MODULE_NAME.pm under scripts/ directory. Take a look to IC.pm->new() to see how it handles the sesion and action arguments recived.
+

Added: addons/1.3/wxPOS/ledgersmb.png
===================================================================
(Binary files differ)


Property changes on: addons/1.3/wxPOS/ledgersmb.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: addons/1.3/wxPOS/scripts/AR.pm
===================================================================
--- addons/1.3/wxPOS/scripts/AR.pm	2012-04-11 03:58:10 UTC (rev 4640)
+++ addons/1.3/wxPOS/scripts/AR.pm	2012-04-12 09:20:29 UTC (rev 4641)
@@ -1,53 +1,227 @@
 package AR;
 
 use base qw(Wx::Frame);
-use Wx::Event qw(EVT_BUTTON);
-use Wx qw(wxDefaultPosition wxDefaultSize);
+use Wx::Event qw(EVT_BUTTON EVT_LISTBOX_DCLICK EVT_COMBOBOX EVT_LIST_ITEM_RIGHT_CLICK EVT_LIST_ITEM_SELECTED EVT_LIST_ITEM_DESELECTED);
+use Wx qw(wxDefaultPosition wxDefaultSize wxLI_HORIZONTAL wxCB_READONLY wxICON_ERROR wxOK wxCENTRE wxLC_REPORT wxLIST_NEXT_ALL wxLIST_STATE_SELECTED wxLIST_STATE_DONTCARE wxLIST_NEXT_BELOW wxLC_VIRTUAL);
 use Wx::Grid;
 
 sub new {
- my ($class, $dbh, $parent, $action) = @_;
- $class->{dbh} = $dbh;
- return $class->$action($parent);
+ my ($class, $sesion, $action) = @_;
+ my $self = {};
+ $self->{sesion} = $sesion;
+ bless $self, $class;
+ return $self->$action();
 }
 
+####### Invioce
 sub Invoice {
- my ($class, $parent) = @_;
- $class->{WxPanel} = Wx::Panel->new($parent, -1, wxDefaultPosition, wxDefaultSize);
- $class->{custom} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Customer', [50, 100], [100, 20]);
- my $button = Wx::Button->new($class->{WxPanel}, -1, 'Search', [150, 100], [100, 20]);
- EVT_BUTTON($class->{WxPanel}, $button, sub{$class->CustomSearch});
- $class->{WxPanel}->{'in_num'} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Invoice Number', [50, 220], [100, 20]);
- $class->{WxPanel}->{'or_num'} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Order Number', [50, 240], [100, 20]);
- return $class->{WxPanel};
+ my ($self) = @_;
+ $self->{sesion}->{tk} = undef;
+ $self->{sesion}->{tk}->{items} = 0;
+ $self->{tab} = Wx::Panel->new($self->{sesion}->{nb}, -1, wxDefaultPosition, wxDefaultSize);
+### Customer
+ my $dft_custom = $self->{sesion}->{defaults}->{custom};
+ Wx::StaticLine->new($self->{tab}, -1, [0, 0], [800, 2], wxLI_HORIZONTAL);
+ Wx::StaticText->new($self->{tab}, -1, 'Customer', [10, 10], [90, 24]);
+ $self->{custom} = Wx::TextCtrl->new($self->{tab}, -1, $dft_custom, [110, 10], [90, 24]);
+ my $cust_but = Wx::Button->new($self->{tab}, -1, 'Search', [210, 10], [90, 24]);
+ EVT_BUTTON($self->{tab}, $cust_but, sub{$self->_listCustomByName});
+ $self->{customer_list} = Wx::ComboBox->new($self->{tab}, -1, '', [10, 40], [290, 24], [''], wxCB_READONLY);
+ $self->{inv_type} = Wx::RadioBox->new($self->{tab}, -1, 'Invoice type: ', [320, 10], [120, 58], ['A', 'B', 'C']);
+### Header
+ Wx::StaticText->new($self->{tab}, -1, 'Inv Num: ', [450, 10], [180, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Ord Num: ', [450, 40], [180, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Record In: ', [630, 10], [180, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Currency: ', [630, 40], [180, 24]);
+ Wx::StaticLine->new($self->{tab}, -1, [0, 70], [800, 2], wxLI_HORIZONTAL);
+### Parts
+ Wx::StaticText->new($self->{tab}, -1, 'Warehouse: '.$self->{sesion}->{defaults}->{wh}, [10,120], [120, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Description', [140, 120], [90, 24]);
+ $self->{desc} = Wx::TextCtrl->new($self->{tab}, -1, '', [240, 120], [180, 24]);
+ my $parts_but = Wx::Button->new($self->{tab}, -1, 'Search', [440, 120], [60, 24]);
+ EVT_BUTTON($self->{tab}, $parts_but, sub{$self->_listPartsByName});
+ Wx::StaticLine->new($self->{tab}, -1, [0, 150], [800, 2], wxLI_HORIZONTAL);
+##
+ Wx::StaticText->new($self->{tab}, -1, 'Qty: ', [10, 160], [30, 24]);
+ $self->{part_qty} = Wx::TextCtrl->new($self->{tab}, -1, '1', [50, 160], [30, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Number: ', [90, 160], [60, 24]);
+ $self->{part_numb} = Wx::TextCtrl->new($self->{tab}, -1, '', [160, 160], [90, 24]);
+ my $parts_add = Wx::Button->new($self->{tab}, -1, 'Add', [260, 160], [60, 24]);
+ EVT_BUTTON($self->{tab}, $parts_add, sub{$self->_addParts});
+ Wx::StaticText->new($self->{tab}, -1, 'Selected: ', [600, 160], [90, 24]);
+ $self->{sel_del} = Wx::StaticText->new($self->{tab}, -1, '', [690, 160], [30, 24]);
+ my $parts_del = Wx::Button->new($self->{tab}, -1, 'Del', [730, 160], [60, 24]);
+ EVT_BUTTON($self->{tab}, $parts_del, sub{$self->_delParts});
+### Ticket
+ $self->{list} = Wx::ListCtrl->new($self->{tab}, -1, [10, 200], [780, 280], wxLC_REPORT);
+ $self->{list}->InsertColumn(1, 'Number');
+ $self->{list}->InsertColumn(2, 'Description');
+ $self->{list}->InsertColumn(3, 'Price');
+ $self->{list}->InsertColumn(4, 'Tax');
+ $self->{list}->InsertColumn(5, 'Ratio');
+ $self->{list}->InsertColumn(6, 'SubTotal');
+ $self->{list}->InsertColumn(7, 'Qty');
+ $self->{list}->InsertColumn(8, 'Total');
+ EVT_LIST_ITEM_SELECTED($self->{tab}, $self->{list}, sub{$self->_selParts});
+ EVT_LIST_ITEM_DESELECTED($self->{tab}, $self->{list}, sub{$self->_selParts});
+### Post
+ Wx::StaticLine->new($self->{tab}, -1, [0, 490], [800, 2], wxLI_HORIZONTAL);
+ Wx::StaticText->new($self->{tab}, -1, 'Net: ', [100, 500], [90, 24]);
+ $self->{net_total} = Wx::StaticText->new($self->{tab}, -1, '', [200, 500], [90, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Tax: ', [300, 500], [90, 24]);
+ $self->{tax_total} = Wx::StaticText->new($self->{tab}, -1, '', [400, 500], [90, 24]);
+ Wx::StaticText->new($self->{tab}, -1, 'Total: ', [500, 500], [90, 24]);
+ $self->{total} = Wx::StaticText->new($self->{tab}, -1, '', [600, 500], [90, 24]);
+ my $invoice_post = Wx::Button->new($self->{tab}, -1, 'Post', [700, 500], [90, 24]);
+ EVT_BUTTON($self->{tab}, $invoice_post, sub{$self->_InvoicePost});
+ return $self->{tab};
 }
+#######
 
 sub Transaction {
- my ($class, $parent) = @_;
- $class->{WxPanel} = Wx::Panel->new($parent, -1, wxDefaultPosition, wxDefaultSize);
- $class->{custom} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Customer', [50, 100], [100, 20]);
- $class->{'in_num'} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Invoice Number', [50, 120], [100, 20]);
- $class->{'or_num'} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Order Number', [50, 140], [100, 20]);
- return $class->{WxPanel};
+ my ($self) = @_;
+ $self->{tab} = Wx::Panel->new($self->{sesion}->{nb}, -1, wxDefaultPosition, wxDefaultSize);
+ Wx::StaticText->new($self->{tab}, -1, 'WIP', [400, 300], [100, 24]);
+ return $self->{tab};
 }
 
 sub Customer {
- my ($class, $parent) = @_;
- $class->{WxPanel} = Wx::Panel->new($parent, -1, wxDefaultPosition, wxDefaultSize);
- $class->{custom} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Name', [50, 120], [100, 20]);
- my $button = Wx::Button->new($class->{WxPanel}, -1, 'Search', [50, 160], [100, 20]);
- EVT_BUTTON($class->{WxPanel}, $button, sub{$class->CustomSearch});
- $class->{list} = Wx::Grid->new($class->{WxPanel}, -1, [50, 200], [500, 500]);
- $class->{list}->CreateGrid(0, 2);
- return $class->{WxPanel};
+ my ($self) = @_;
+ $self->{tab} = Wx::Panel->new($self->{sesion}->{nb}, -1, wxDefaultPosition, wxDefaultSize);
+ Wx::StaticText->new($self->{tab}, -1, 'WIP', [400, 300], [100, 24]);
+ return $self->{tab};
 }
 
-sub CustomSearch {
+sub _listCustomByName {
  my($self, $event) = @_;
  my $customer = $self->{custom}->GetValue();
- my $sql = "SELECT id, name FROM public.entity WHERE entity_class = \'1\' AND name ILIKE \'%".$customer."%\'";
- my ($list) = $self->{dbh}->selectall_arrayref($sql);
- $self->{customer_list} = Wx::ListBox->new($self->{WxPanel}, -1, [250, 100], [150, 80], $list);
+ my $list;
+ my $i = 0;
+ foreach (@{$self->{sesion}->{ldb}->getEntityList(2, undef, $customer)}) {
+  push (@{$list}, "$_->[1]");
+  $self->{customer_search}->{$_->[1]} = $_->[0];
+  $i++;
+ }
+ if ($list) {
+  $self->{customer_list} = undef;
+  $self->{customer_list} = Wx::ComboBox->new($self->{tab}, -1, $list->[0], [10, 40], [290, 24], $list);
+ }
+ else {
+  $self->{customer_list} = undef;
+  $self->{customer_list} = Wx::ComboBox->new($self->{tab}, -1, '', [10, 40], [290, 24], ['']);
+ }
  return $self->{customer_list};
 }
+
+sub _listPartsByName {
+ my($self, $event) = @_;
+ my $desc = $self->{desc}->GetValue();
+ my $wh = $self->{sesion}->{defaults}->{wh};
+ my $list;
+ foreach (@{$self->{sesion}->{ldb}->getPartsList(undef, $desc, $wh)}) {
+  push (@{$list}, "$_->[1]");
+  $self->{parts_search}->{$_->[1]} = $_->[0];
+ }
+ if ($list) {
+  $self->{parts_list} = undef;
+  $self->{parts_list} = Wx::ComboBox->new($self->{tab}, -1, $list->[0], [510, 120], [280, 24], $list, wxCB_READONLY);
+  EVT_COMBOBOX($self->{tab}, $self->{parts_list}, sub{$self->_getPartsNumber});
+ }
+ else {
+  $self->{parts_list} = undef;
+  $self->{parts_list} = Wx::ComboBox->new($self->{tab}, -1, '', [510, 120], [280, 24], [''], wxCB_READONLY);
+ }
+ return $self->{parts_list};
+}
+
+sub _getPartsNumber {
+ my($self, $event) = @_;
+ my $numb = $self->{parts_search}->{$self->{parts_list}->GetValue()};
+ $self->{part_numb}->SetValue($numb);
+ return $self->{part_numb};
+}
+
+sub _addParts {
+ my($self, $event) = @_;
+ my $part = $self->{part_numb}->GetValue();
+ my $cust = $self->{customer_search}->{$self->{customer_list}->GetValue()};
+### Check for customer
+ if (!$cust) {
+  Wx::MessageBox("Please, try again selecting a customer.", "Customer not Found!", wxOK|wxCENTRE|wxICON_ERROR, $self->{tab});
+  return 0;
+ }
+### Check for part
+ if (!$part) {
+  Wx::MessageBox("Please, try again selecting a part.", "Part not Found!", wxOK|wxCENTRE|wxICON_ERROR, $self->{tab});
+  return 0;
+ }
+ my ($numb, $desc, $sell, $inv_acc, $inc_acc, $img) = @{$self->{sesion}->{ldb}->getPartsByNumber($part)};
+ if (!$numb) {
+  Wx::MessageBox("Please, try again.", "Part not Found!", wxOK|wxCENTRE|wxICON_ERROR, $self->{tab});
+  return 0;
+ }
+ my $qty = $self->{part_qty}->GetValue();
+ my $total_tax = 0;
+ my $total_tax_ratio = 0;
+### Calculates total ratio and total tax ammount
+ foreach my $tax (@{$self->{sesion}->{ldb}->getTaxByPair($cust, $part)}) {
+  $total_tax_ratio += $tax->[1];
+  $total_tax += $tax->[1] * $sell;
+ }
+ my $st = $total_tax + $sell;
+ my $total = $st * $qty;
+ my $pp = $self->{list}->InsertStringItem($self->{sesion}->{tk}->{items}, $numb);
+ $self->{list}->SetItem($pp, 1, $desc, -1);
+ $self->{list}->SetItem($pp, 2, $sell, -1);
+ $self->{list}->SetItem($pp, 3, $total_tax, -1);
+ $self->{list}->SetItem($pp, 4, $total_tax_ratio, -1);
+ $self->{list}->SetItem($pp, 5, $st, -1);
+ $self->{list}->SetItem($pp, 6, $qty, -1);
+ $self->{list}->SetItem($pp, 7, $total, -1);
+
+ $self->{sesion}->{tk}->{tnet} += $sell;
+ $self->{sesion}->{tk}->{ttax} += $total_tax;
+ $self->{sesion}->{tk}->{total} += $total;
+ $self->{net_total}->SetLabel($self->{sesion}->{tk}->{tnet});
+ $self->{tax_total}->SetLabel($self->{sesion}->{tk}->{ttax});
+ $self->{total}->SetLabel($self->{sesion}->{tk}->{total});
+ $self->{sesion}->{tk}->{items}++;
+ return $part;
+}
+
+sub _selParts {
+ my($self) = @_;
+ $self->{sel_del}->SetLabel($self->{list}->GetSelectedItemCount);
+ return 1;
+}
+
+sub _delParts {
+ my($self) = @_;
+ if ($self->{list}->GetSelectedItemCount < 1) {
+  Wx::MessageBox("Please, try again selecting an item.", "Item not deleted!", wxOK|wxCENTRE|wxICON_ERROR, $self->{tab});
+  return 0;
+ }
+ my $item = $self->{list}->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ my $sell = $self->{list}->GetItem($item, 2)->GetText();
+ my $total_tax = $self->{list}->GetItem($item, 3)->GetText();
+ my $total = $self->{list}->GetItem($item, 7)->GetText();
+ $self->{sesion}->{tk}->{tnet} -= $sell;
+ $self->{sesion}->{tk}->{ttax} -= $total_tax;
+ $self->{sesion}->{tk}->{total} -= $total;
+ $self->{net_total}->SetLabel($self->{sesion}->{tk}->{tnet});
+ $self->{tax_total}->SetLabel($self->{sesion}->{tk}->{ttax});
+ $self->{total}->SetLabel($self->{sesion}->{tk}->{total});
+ $self->{list}->DeleteItem($item);
+ $self->{sesion}->{tk}->{items}--;
+ $self->{sel_del}->SetLabel($self->{list}->GetSelectedItemCount);
+#use Data::Dumper;
+#print Dumper($item_col);
+ return 1;
+}
+
+sub _InvoicePost {
+ my($self) = @_;
+ Wx::MessageBox("At this point we should be able to track each account and the account to set.", "DEBUG", wxOK|wxCENTRE|wxICON_ERROR, $self->{tab});
+ return 1;
+}
 1;

Added: addons/1.3/wxPOS/scripts/Console.pm
===================================================================
--- addons/1.3/wxPOS/scripts/Console.pm	                        (rev 0)
+++ addons/1.3/wxPOS/scripts/Console.pm	2012-04-12 09:20:29 UTC (rev 4641)
@@ -0,0 +1,141 @@
+=head1 NAME
+
+LedgerSMB - Wx Console
+
+=head1 SYNOPSIS
+
+This is the main window, menu and handler for the LedgerSMB Wx client
+
+=head1 METHODS
+
+=over
+
+=item new()
+
+This method creates a new Frame that will work as main window, then it uses Login package poping up
+a small dialog asking for database credentials. Once the database handler is gotten, it will show
+a menu.
+
+=item _load_module()
+
+This is the main handler. It will get from the menu $module and $action in order to init a $module->new($action).
+
+=item $self->{sesion}
+
+This is a hash reference that works as a container for all that variables shared by the whole program. It will be passed as argument to the modules.
+
+Initialized by Login module:
+->{user} = User logged to database.
+->{comp} = Database name.
+->{serv} = Server ip or host where the database is listening.
+->{logo} = Wx bitmap containing LedgerSMB 200x100 logo.
+->{ldb} = LedgerSMB database interface. LSMBDP object with an active connection to database.
+->{user_info} = User full name, etc.
+->{defaults} = This is a hash stored into note table on the database. It would be like WX user defaults and each user should have at least a warehouse assigned in order to work.
+
+Initialized by Console:
+->{nb} = It is the NoteBook object that works as window parent.
+
+=back
+
+=head1 AUTHOR
+
+Andres Basile (..hidden..)
+
+=cut
+
+package Console;
+
+use base qw(Wx::Frame);
+use Wx::Event;
+use Wx::Menu;
+use Wx qw(wxDefaultPosition wxDefaultSize wxDEFAULT_FRAME_STYLE wxNO_FULL_REPAINT_ON_RESIZE wxCLIP_CHILDREN wxBITMAP_TYPE_PNG wxMAXIMIZE);
+use Wx::Event qw(EVT_TREE_SEL_CHANGED EVT_CLOSE EVT_IDLE EVT_MENU);
+
+sub new {
+ my ($class) = @_;
+ my $self = $class->SUPER::new(undef, -1, 'LedgerSMB', wxDefaultPosition, [800, 600], wxDEFAULT_FRAME_STYLE);
+
+### First at all, login, get a db handler and other stuff initialized by Login dialog as sesion.
+#
+ use Login;
+ my $login = Login->new();
+ if ($login->ShowModal()) {
+  die();
+ }
+### Getting my initialized hash sesion with lsmbdb object (an object layer to access db), login options, logo image object
+ $self->{sesion} = $login->{sesion};
+ $self->{sesion}->{defaults} = eval $self->{sesion}->{user_info}->{defaults};
+###
+ $login->Destroy();
+#
+###
+
+### Notebook (tabs), this will be the window parent inside the frame.
+#
+ $self->{sesion}->{nb} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNO_FULL_REPAINT_ON_RESIZE|wxCLIP_CHILDREN);
+#
+###
+
+### Menu
+# TODO: It should be loaded/created on demand according to user acl.
+ my($ID_FILE, $ID_ABOUT, $ID_EXIT, $ID_AR, $AR_INVOICE, $AR_TRANSACTION, $AR_CUSTOMER, $ID_AP, $ID_IC, $IC_PARTS) = (100 .. 200);
+ my $bar = Wx::MenuBar->new;
+
+ my $file = Wx::Menu->new;
+ $file->Append($ID_ABOUT, "&About");
+ EVT_MENU($self, $ID_ABOUT, \&_about);
+ $file->Append($ID_EXIT, "&Exit");
+ EVT_MENU($self, $ID_EXIT, \&_logout);
+ $bar->Append($file, "&File" );
+
+ my $ar = Wx::Menu->new;
+ $ar->Append($AR_INVOICE, "&Invoice");
+ EVT_MENU($self, $AR_INVOICE, sub{$self->_load_module(AR, Invoice)});
+ $ar->Append($AR_TRANSACTION, "&Transaction");
+ EVT_MENU($self, $AR_TRANSACTION, sub{$self->_load_module(AR, Transaction)});
+ $ar->Append($AR_CUSTOMER, "&Customer");
+ EVT_MENU($self, $AR_CUSTOMER, sub{$self->_load_module(AR, Customer)});
+ $bar->Append($ar, "&AR" );
+
+ my $ic = Wx::Menu->new;
+ $ic->Append($IC_PARTS, "&Parts");
+ EVT_MENU($self, $IC_PARTS, sub{$self->_load_module(IC, Parts)});
+ $bar->Append($ic, "&Inventory" );
+
+ $self->SetMenuBar($bar);
+ return $self;
+}
+
+### About
+sub _about {
+ my ($self) = @_;
+ use Wx qw(wxOK wxCENTRE);
+ my $about = Wx::MessageBox("LedgerSMB WX Client\nEmployee: ".$self->{sesion}->{user_info}->{name}."\nWarehouse: ".$self->{sesion}->{defaults}->{wh}, "Version 0.2", wxOK|wxCENTRE, $self);
+ return 1;
+}
+
+### Exit
+sub _logout {
+ my ($self) = @_;
+ $self->SUPER::Close();
+}
+
+### Handler
+sub _load_module {
+ my ($self, $module, $action) = @_;
+ $self->{sesion}->{nb}->DeleteAllPages();
+ use lib "scripts";
+ require $module.".pm";
+ my $tab = $module->new($self->{sesion}, $action);
+ $self->{sesion}->{nb}->AddPage($tab, $module."::".$action, 0);
+ $tab->Show();
+ return $self;
+}
+
+###
+sub DESTROY {
+ my ($self) = @_;
+ $self->{sesion}->{ldb}->{dbh}->disconnect();
+}
+1;

Modified: addons/1.3/wxPOS/scripts/IC.pm
===================================================================
--- addons/1.3/wxPOS/scripts/IC.pm	2012-04-11 03:58:10 UTC (rev 4640)
+++ addons/1.3/wxPOS/scripts/IC.pm	2012-04-12 09:20:29 UTC (rev 4641)
@@ -1,3 +1,38 @@
+=head1 NAME
+
+LedgerSMB - Wx Inventory Control
+
+=head1 SYNOPSIS
+
+This module is on charge of inventory operations.
+
+=head1 METHODS
+
+=over
+
+=item new ($sesion, $action)
+
+This will get, set and go an action from the menu that is a method of this class. It will
+work as a kind of handler and will return a reference to that action.
+$sesion = It is a hash with all objects and variables needed.
+$action = Method name to invoque as String
+
+=item Parts ()
+
+This is an action that will show a panel asiking for number or description.
+
+=item _listPartsByName ()
+
+A private function used by the Search action that invoques lsmbdb interface to get parts list from the db.
+
+=back
+
+=head1 AUTHOR
+
+Andres Basile (..hidden..)
+
+=cut
+
 package IC;
 
 use base qw(Wx::Frame);
@@ -2,51 +37,68 @@
 use Wx::Event qw(EVT_BUTTON);
-use Wx qw(wxDefaultPosition wxDefaultSize);
+use Wx qw(wxDefaultPosition wxDefaultSize wxMAXIMIZE wxCB_READONLY);
 use Wx::Grid;
 
 sub new {
- my ($class, $dbh, $parent, $action) = @_;
- $class->{dbh} = $dbh;
- return $class->$action($parent);
+ my ($class, $sesion, $action) = @_;
+ my $self = {};
+ $self->{sesion} = $sesion;
+ $self->{wh_dft_id} = $self->{sesion}->{defaults}->{wh};
+ bless $self, $class;
+ $self->_getWarehouses();
+ return $self->$action();
 }
 
-sub Search {
- my ($class, $parent) = @_;
- $class->{WxPanel} = Wx::Panel->new($parent, -1, wxDefaultPosition, wxDefaultSize);
- $class->{numb} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Number', [50, 100], [100, 20]);
- $class->{desc} = Wx::TextCtrl->new($class->{WxPanel}, -1, 'Description', [50, 120], [100, 20]);
- my $button = Wx::Button->new($class->{WxPanel}, -1, 'Search', [50, 160], [100, 20]);
- EVT_BUTTON($class->{WxPanel}, $button, sub{$class->SearchThis});
- $class->{list} = Wx::Grid->new($class->{WxPanel}, -1, [50, 200], [500, 500]);
- $class->{list}->CreateGrid(0, 2);
- return $class->{WxPanel};
+sub _getWarehouses {
+ my ($self) = @_;
+ $self->{wh_des}->{''} = '';
+ foreach (@{$self->{sesion}->{ldb}->getWarehouseList()}) {
+  $self->{wh_des}->{$_->[1]} = $_->[0];
+  if ($_->[0] == $self->{wh_dft_id}) {
+   $self->{wh_dft_des} = $_->[1];
+  }
+ }
+ return $self;
 }
 
-sub SearchThis {
+sub Parts {
+ my ($self) = @_;
+ $self->{WxPanel} = Wx::Panel->new($self->{sesion}->{nb}, -1, wxDefaultPosition, wxDefaultSize, wxMAXIMIZE);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Number', [10, 20], [90, 24]);
+ $self->{numb} = Wx::TextCtrl->new($self->{WxPanel}, -1, '', [110, 20], [90, 24]);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Description', [10, 60], [90, 24]);
+ $self->{desc} = Wx::TextCtrl->new($self->{WxPanel}, -1, '', [110, 60], [180, 24]);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Warehouse', [310, 40], [90, 24]);
+ $self->{wh} = Wx::ComboBox->new($self->{WxPanel}, -1, $self->{wh_dft_des}, [400, 40], [180, 24], [keys(%{$self->{wh_des}})]);
+ my $button = Wx::Button->new($self->{WxPanel}, -1, 'Search', [110, 100], [60, 24]);
+ EVT_BUTTON($self->{WxPanel}, $button, sub{$self->_listPartsByName});
+ return $self->{WxPanel};
+}
+
+sub _listPartsByName {
  my($self, $event) = @_;
  my $numb = $self->{numb}->GetValue();
  my $desc = $self->{desc}->GetValue();
- $self->{list}->ClearGrid();
- if ($self->{dbh}) {
-  my $sql;
-  if ($numb) {
-   $sql = "SELECT partnumber, description FROM public.parts WHERE partnumber = \'".$numb."\'";
-  }
-  else {
-   $sql = "SELECT partnumber, description FROM public.parts WHERE description ILIKE \'%".$desc."%\'";
-  }
-  my ($list) = $self->{dbh}->selectall_arrayref($sql);
-  my $i = 0;
-  foreach (@{$list}) {
-   $self->{list}->AppendRows();
-   $self->{list}->SetCellValue($i, 0, $_->[0]);
-   $self->{list}->SetCellValue($i, 1, $_->[1]);
-   $i++;
-  }
+ my $wh = $self->{wh_des}->{$self->{wh}->GetValue()};wxCB_READONLY
+ $self->{list} = Wx::Grid->new($self->{WxPanel}, -1, [10, 160], [790, 480], wxCB_READONLY);
+ $self->{list}->CreateGrid(0, 4);
+ $self->{list}->SetColLabelValue(0, 'Number');
+ $self->{list}->SetColSize(0, 100);
+ $self->{list}->SetColLabelValue(1, 'Description');
+ $self->{list}->SetColSize(1, 440);
+ $self->{list}->SetColLabelValue(2, 'Warehouse');
+ $self->{list}->SetColSize(2, 100);
+ $self->{list}->SetColLabelValue(3, 'Qty');
+ $self->{list}->SetColSize(3, 60);
+ my $list = $self->{sesion}->{ldb}->getPartsList($numb, $desc, $wh);
+ my $i = 0;
+ foreach (@{$list}) {
+  $self->{list}->AppendRows();
+  $self->{list}->SetCellValue($i, 0, $_->[0]);
+  $self->{list}->SetCellValue($i, 1, $_->[1]);
+  $self->{list}->SetCellValue($i, 2, $_->[2]);
+  $self->{list}->SetCellValue($i, 3, $_->[3]);
+  $i++;
  }
- else {
-  Wx::MessageBox("Database handler not working.", "ERROR", wxOK|wxCENTRE, $self);
- }
- return $self;
+ return $self->{list};
 }
 1;
-

Added: addons/1.3/wxPOS/scripts/LSMBDB.pm
===================================================================
--- addons/1.3/wxPOS/scripts/LSMBDB.pm	                        (rev 0)
+++ addons/1.3/wxPOS/scripts/LSMBDB.pm	2012-04-12 09:20:29 UTC (rev 4641)
@@ -0,0 +1,131 @@
+=head1 NAME
+
+LedgerSMB - Wx LedgerSMB DataBase interface
+
+=head1 SYNOPSIS
+
+This object provides a database abstraction layer for the modules. The idea is to provide an easy way
+to select, update and insert.
+
+=head1 METHODS
+
+=over
+
+=cut
+
+package LSMBDB;
+use DBI;
+
+=item new (USER, PASSWORD, COMPANY, HOST)
+
+This method must recieve database basic information to estabish a connection, then it, will be blassed
+and returned.
+
+=cut
+
+sub new {
+ my ($class, $user, $pass, $comp, $host) = @_;
+ my $self = {};
+ $self->{dbh} = DBI->connect("dbi:Pg:dbname=$comp;host=$host", $user, $pass, {RaiseError => 0, AutoCommit => 0});
+ if ($self->{dbh}) {
+  bless $self, $class;
+  return $self;
+ }
+ else {
+  return 0;
+ }
+}
+
+=item getPartsList ([$part_id, $part_description])
+
+=back
+
+=head1 AUTHOR
+
+Andres Basile (..hidden..)
+
+=cut
+
+sub getPartsList {
+ my ($self, $numb, $desc, $wh) = @_;
+ my $sql = "SELECT p.partnumber as id, p.description AS part, w.description AS warehouse, SUM(i.qty) AS onhand FROM inventory i JOIN warehouse w ON (w.id = i.warehouse_id) JOIN parts p ON (p.id = i.parts_id) WHERE 1 = 1 ";
+ if ($numb) {
+  $sql .= "AND p.partnumber = \'".$numb."\' ";
+ }
+ if ($desc) {
+  $sql .= "AND p.description ILIKE \'%".$desc."%\' ";
+ }
+ if ($wh) {
+  $sql .= "AND w.id = \'".$wh."\' ";
+ }
+  $sql .= "GROUP BY p.description, w.description, p.partnumber ORDER BY p.partnumber LIMIT 20";
+ my ($list) = $self->{dbh}->selectall_arrayref($sql);
+ return $list;
+}
+
+sub getPartsByNumber {
+ my ($self, $numb) = @_;
+ my $sql = "SELECT partnumber, description, sellprice, inventory_accno_id, income_accno_id,image FROM parts WHERE partnumber = \'".$numb."\'";
+ my ($part) = $self->{dbh}->selectrow_arrayref($sql);
+ return $part;
+}
+
+sub getWarehouseList {
+ my ($self) = @_;
+ my $sql = "SELECT id, description FROM public.warehouse";
+ my ($list) = $self->{dbh}->selectall_arrayref($sql);
+ return $list;
+}
+
+sub getEntityList {
+ my ($self, $type, $numb, $desc) = @_;
+ my $sql;
+ if ($numb) {
+  $sql = "SELECT id, name FROM public.entity WHERE entity_class = \'".$type."\' AND id = \'%".$numb."%\'";
+ }
+ elsif ($desc) {
+  $sql = "SELECT id, name FROM public.entity WHERE entity_class = \'".$type."\' AND name ILIKE \'%".$desc."%\'";
+ }
+ else {
+  $sql = "SELECT id, name FROM public.entity WHERE entity_class = \'".$type."\'";
+ }
+ $sql .= " LIMIT 20";
+ my ($list) = $self->{dbh}->selectall_arrayref($sql);
+ return $list;
+}
+
+sub getEntityListClasses {
+ my ($self) = @_;
+ my $sql = "select entity__list_classes()";
+ my ($list) = $self->{dbh}->execute($sql);
+ return $list;
+}
+
+sub getMenuList {
+ my ($self) = @_;
+ my $sql = "SELECT id, label, parent, position FROM public.menu_node";
+ my ($list) = $self->{dbh}->selectall_arrayref($sql);
+ return $list;
+}
+
+sub getEntityId {
+ my ($self) = @_;
+ my $sql = "SELECT person__get_my_entity_id()";
+ my ($list) = $self->{dbh}->selectrow_arrayref($sql);
+ return $list;
+}
+
+sub getUserByUsername {
+ my ($self, $name) = @_;
+ my $sql = "SELECT u.username, e.name, ee.ssn, en.note as defaults FROM users u JOIN entity e ON (u.entity_id = e.id) JOIN entity_employee ee ON (e.id = ee.entity_id) JOIN entity_note en ON (e.id = en.entity_id) WHERE en.subject = \'WX\' AND u.username = \'".$name."\'";
+ my ($list) = $self->{dbh}->selectrow_hashref($sql);
+ return $list;
+}
+
+sub getTaxByPair {
+ my ($self, $cust, $part) = @_;
+ my $sql = "SELECT a.id, t.rate, a.description, pt.parts_id, eca.id FROM tax t JOIN partstax pt ON (t.chart_id = pt.chart_id) JOIN parts p ON (p.id = pt.parts_id) JOIN customertax ct ON (t.chart_id = ct.chart_id) JOIN account a ON (a.id = ct.chart_id) JOIN entity_credit_account eca ON (eca.id = ct.customer_id) WHERE p.partnumber = \'".$part."\' and eca.entity_id = \'".$cust."\'"; 
+ my ($list) = $self->{dbh}->selectall_arrayref($sql);
+ return $list;
+}
+1;

Added: addons/1.3/wxPOS/scripts/Login.pm
===================================================================
--- addons/1.3/wxPOS/scripts/Login.pm	                        (rev 0)
+++ addons/1.3/wxPOS/scripts/Login.pm	2012-04-12 09:20:29 UTC (rev 4641)
@@ -0,0 +1,80 @@
+=head1 NAME
+
+LedgerSMB - Wx Login
+
+=head1 SYNOPSIS
+
+This is a dialog that will pop up at start asking about user, pass and company. This credential is used to get a database handler.
+
+=head1 METHODS
+
+=over
+
+=item new ()
+
+This method creates a new Dialog showing an intro login screen. From the class, ShowModal is used to keep the dialog up till a valid handler is created.
+
+=item Login ()
+
+It is an event function that get the text on the fields and tries a connection to the database.
+If the connection is made, it will be saved under ldb key and EndModal with a return 0 code will
+be trigged so the conditional on console.pm will avoid die.
+
+=back
+
+=head1 AUTHOR
+
+Andres Basile (..hidden..)
+
+=cut
+
+package Login;
+
+use base qw(Wx::Dialog);
+use Wx::Event qw(EVT_BUTTON);
+use Wx qw(wxDefaultPosition wxDefaultSize wxOK wxCENTRE wxTE_PASSWORD wxCLOSE_BOX wxBITMAP_TYPE_PNG);
+
+sub new {
+ my ($class) = @_;
+ my $self = Wx::Dialog->new(undef , -1, "LedgerSMB", wxDefaultPosition, [300, 320]);
+ bless $self, $class;
+ $self->{WxPanel} = Wx::Panel->new($self, -1, wxDefaultPosition, [300, 320]);
+ Wx::InitAllImageHandlers();
+ $self->{sesion}->{logo} =  Wx::Bitmap->new("ledgersmb.png", wxBITMAP_TYPE_PNG);
+ Wx::StaticBitmap->new($self->{WxPanel}, -1, $self->{sesion}->{logo}, [50, 10], [200, 100]);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'User', [50, 120], [80, 24]);
+ $self->{user} = Wx::TextCtrl->new($self->{WxPanel}, -1, 'user', [130, 120], [120, 24]);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Password', [50, 160], [90, 24]);
+ $self->{pass} = Wx::TextCtrl->new($self->{WxPanel}, -1, 'pass', [130, 160], [120, 24], wxTE_PASSWORD);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Company', [50, 200], [90, 30]);
+ $self->{comp} = Wx::TextCtrl->new($self->{WxPanel}, -1, 'database', [130, 200], [120, 24]);
+ Wx::StaticText->new($self->{WxPanel}, -1, 'Server', [50, 240], [90, 30]);
+ $self->{serv} = Wx::TextCtrl->new($self->{WxPanel}, -1, 'localhost', [130, 240], [120, 24]);
+ my $button = Wx::Button->new($self->{WxPanel}, -1, 'Enter', [100, 280], [100, 24]);
+ EVT_BUTTON($self, $button, \&Login);
+ return $self;
+}
+
+sub Login {
+ my($self, $event) = @_;
+ $self->{sesion}->{user} = $self->{user}->GetValue();
+ $self->{sesion}->{comp} = $self->{comp}->GetValue();
+ $self->{sesion}->{serv} = $self->{serv}->GetValue();
+ my $pass = $self->{pass}->GetValue();
+ use LSMBDB;
+ $self->{sesion}->{ldb} = LSMBDB->new($self->{sesion}->{user}, $pass, $self->{sesion}->{comp}, $self->{sesion}->{serv});
+ if ( $self->{sesion}->{ldb} ) {
+  $self->{sesion}->{user_info} = $self->{sesion}->{ldb}->getUserByUsername($self->{sesion}->{user});
+  if ($self->{sesion}->{user_info}) {
+   $self->EndModal(0);
+  }
+  else {
+   Wx::MessageBox("User exists on database but has not WX profile.\nPlease add a new entity_note on the employee.\nSee README for details.", "Login Error!", wxOK|wxCENTRE, $self);
+  }
+ }
+ else {
+  Wx::MessageBox("Please, try again.", "Login Error!", wxOK|wxCENTRE, $self);
+ }
+ return $self;
+}
+1;

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.