Transaction control in Base

Creating a macro - Writing a Script - Using the API (OpenOffice Basic, Python, BeanShell, JavaScript)
Post Reply
User avatar
DrewJensen
Volunteer
Posts: 1734
Joined: Sat Oct 06, 2007 9:01 pm
Location: Cumberland, MD - USA

Transaction control in Base

Post by DrewJensen »

You want to manage your inventory. First thing I would say is that you should back away from thinking of this as a problem to do with a form (at first), rather think of it as transactions or adujustments.

In other words you have 2 basic adjustments. I will call these two basic adjustments, Pulldown and Replenish
Pulldown - where stock is leaving your inventory. [OUT]
Replenish - add stock to your inventory. [IN]

Ok, that's fine. So to begin lets look at a table to maintain the actual inventory. The Base application comes with a pre-defined products table, I know you started a test database, but I will go ahead and use this table structure for the moment.:

Code: Select all

Products(
   "ProductID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
   "CategoryID" INTEGER,
   "Discontinued" BOOLEAN,
   "LeadTime" VARCHAR(50),
   "ProductDescription" VARCHAR(250),
   "ProductName" VARCHAR(50),
   "ReorderLevel" INTEGER,
   "Serialnumber" VARCHAR(50),
   "SupplierID" INTEGER,
   "UnitPrice" DECIMAL(17),
   "UnitsInStock" INTEGER,
   "UnitsOnOrder" INTEGER
)
For our purposes the column we want to concentrate on is the UnitsInStock.

From reading your posts I take it you want to track each adjustment to this columns value. To do this we would want another table to hold these adujustment records. How about we just call this InvAdujustments:

Code: Select all

InvAdjustments(
   "AdjID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
   "ProductID" INTEGER,
   "AdjDate" DATE,
   "AdjType" INTEGER,
   "Units" INTEGER
)

In this table then each record has a primary key, a foreign key to the product table, a date the adjustment happened, a type of adjustment PullDown or Replenish [In fact we could expand on these basic types - say for breakage, spoilage, customer return, etc] and the number of units in this adjustment.

OK, now for that Adjustment type field we would want a third table to hold what the valid adjustments are, and whether they are a PullDown or a Replenish. I will name the table AdjustmentTypes:

Code: Select all

AdjustmentTypes(
   "AdjType" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
   "StockType" TINYINT NOT NULL,
   "AdjName" VARCHAR(50)
)

In this helper table we have AdjType which is the key we will copy to the Adjustments table, StockType which will be either 1 ( PullDown )or 2 ( Replenish ), and an Adjust ment name. The reason for this type of setup is so that we can associate more then two types of business activity to our two inventory adjustments. In other words we might populate this table at first as


0, 1, "Sale" [ Pullsdown the inventory ]
1, 2, "Purchase" [ Replenishes the inventory ]
3, 1, "Breakage" [ Pullsdown the inventory ]
4, 2, "Return" [ Replenishes the inventory ]


Alright then. This is a very basic structure for tracking an inventory and adjustments to this inventory. In practice you would want to embellish this with other information but good enough for an example.

Back to our basic adjustments. Lets start calling these "Adustment Transactions" for purposes of a business work flow discussion. One way we could implement this business process in a database is to add something called triggers to the InvAdjustments table. A trigger is simply a little snippet of code that will run when ever a predefined actions takes place on the table - for example if we added a trigger for an Insert action on the table we would do something like this in my own weird Psudeo code:

Code: Select all

IF InsertRecord.AdjType -> AdjustmentTypes.StockType = 1 THEN
   REM we are pullind down the inventory
   Products.UnitsOnHand = Products.UnitsOnHand - InsertRecord.Units
ELSE
   IF InsertRecord.AdjType -> AdjustmentTypes.StockType = 2 THEN
      REM we are replenishing the inventory
       Products.UnitsOnHand = Products.UnitsOnHand + InsertRecord.Units
   ELSE
      REM we don't know what to do, so don't do anything
      RAISE AN ERROR AND DO NOT ALLOW THE INSERT (UNKOWN STOCK ADJUSTMENT TYPE)
   ENDIF
ENDIF

One way to handle this "Adjustment Transaction" is with something called a Trigger on the InvAdjustments table. Another common way would be to write a "Stored Procedure" in the database. A stored procedure is, for all intents and purposes, equivalent to a Basic procedure in Base. In both cases the procedure can be called arbitrarily, instead of being associated with a record event for a given table. The logic inside this procedure is exactly the same as the pseudo code I showed for the Trigger, a few steps would be added, such as opening record sets for the proper tables but that is just so much detail..

The question then is which approach to take with a Base program. First thing is this. The native database engine for Base does support the creation of table triggers, but these triggers MUST be written in Java and have an internal structure much closer to a stored procedure in other database engines. So, if you are comfortable working in Java we could go down that road, but given your newness to databases in general I assume that OOBasic would be a better language and environment to work in. With this in mind then a Basic procedure seems like the ticket. [As a side note, Base can work with any number of different database engines such as MySQL. If we chose to use MySQL 5.x or higher then the use of a trigger or a traditional stored procedure would be quite viable option]

Looking at what we want this Basic procedure to do we see first that we will want to pass to it all the information necessary to enter one InvAdjustment record. So we would declare the procedure with something like

Code: Select all

sub spInvAdjustment(
   ProductID AS INTEGER,
   AdjDate AS DATE,
   AdjType AS INTEGER,
   Units AS INTEGER,
   dbConnection as variant )

end sub
That procedure definition should look familiar, it is pretty much just a cut and paste of the table definition for InvAdjustment. The only thing that I dropped was a parameter for the primary key field, as this is automatically generated by the database on insert to the tatble. The only thing I added was the parameter for an active connection to the database. ( This is also a difference between a Basic procedure and a real Stored procedure where we would running in the database to begin with, here we are not so we need a way to talk to it )

We could accept this as a starting point if we like, and make everything else happen in the procedure. I think for tonight that is just what I will do.

[ this might look intimidating, but it really isn't and I will be happy to walk you through it all, step by step..so just read along]

Here is a basic sub procedure that implements the rudimentary steps of the pseudo code from earlier. It is not a finished product by any means, but it actually runs to completion and assuming the data passed in is valid does what we expect.

Code: Select all

sub spInvAdjustment( ProductID AS INTEGER, AdjDate AS DATE,   AdjType AS INTEGER,   Units AS INTEGER, dbConnection as variant )
  
      ' a variable for the stock adjustment
      ' to type to perform
dim StockType as integer

      ' and some constants for what it means
      ' just for readability - and good coding
      ' style
const cPullDown    = 1
const cReplenish    = 2

      ' just for convienence as string
      ' to use to build our sql commands
dim sSQL as string

   sSQL = "Select ""StockType"" FROM ""AdjustmentTypes"" WHERE ""AdjType"" = ? "

      ' a variable for a prepared statement
      ' object that lets us apply SQL
      ' commands to the databse
dim pStmt as variant

   pStmt = dbConnection.PrepareStatement( sSQL )
  
   pStmt.setInt( 1, AdjType )
  
      ' a record set variable
dim rs as variant
  
   rs = pStmt.ExecuteQuery
  
      ' we should have gotten back one record
   if not rs.isBeforeFirst then
      ' if we are not before the first record
      ' then we got back zero records
      print "oops I don't know that Adjustment type"
      exit sub
   else
      ' move to the first record
      ' and retrive the value from
      ' the frist column as an integer
      rs.next
      StockType = rs.getInt( 1 )
   end if
  
      ' so the next step is to actually
      ' write our data to the InvAdjustments
      ' table - we can resuse our prepared
      ' statement for this
   sSQL = "INSERT INTO ""InvAdjustments"" ( ""ProductID"", ""AdjDate"", ""AdjType"", ""Units"" ) VALUES ( ?,?,?,? ) "
   pStmt = dbConnection.PrepareStatement( sSQL )

dim aDate as new com.sun.star.util.Date
   aDate.Day = Day(AdjDate)
   aDate.Month = Month(AdjDate)
   aDate.Year = Year(AdjDate)
  
   pStmt.setInt( 1, ProductID )
   pStmt.setDate( 2, aDate )
   pStmt.setInt( 3, AdjType )
   pStmt.setInt( 4, Units )

   pStmt.executeUpdate
  
      ' here we decide what we will do
      ' about the inventory items in stock
   select case StockType
      case cPullDown
         sSQL = "UPDATE ""Products"" SET ""UnitsInStock"" = ""UnitsInStock"" - ? WHERE ""ProductID"" = ? "
      case cReplenish
         sSQL = "UPDATE ""Products"" SET ""UnitsInStock"" = ""UnitsInStock"" + ? WHERE ""ProductID"" = ? "
      end select
  
   pStmt = dbConnection.PrepareStatement( sSQL )
  
   pStmt.setInt( 1, Units )
   pStmt.setInt( 2, ProductID )
  
   pStmt.executeUpdate
end sub
Former member of The Document Foundation
Former member of Apache OpenOffice PMC
LibreOffice on Ubuntu 18.04
TerryE
Volunteer
Posts: 1402
Joined: Sat Oct 06, 2007 10:13 pm
Location: UK

Re: Transaction control in Base

Post by TerryE »

Placeholder for Change Control and to remove topic from "View unanswered posts" list.
Ubuntu 11.04-x64 + LibreOffice 3 and MS free except the boss's Notebook which runs XP + OOo 3.3.
Post Reply