Note: This is an enhanced imread file. The code segments in red show the code which you will add to the applicable file. When there is code in black, surrounded by red, it is only the red you are adding. The code shown in black is already in the file you are editing. The imread.txt file which ships with the CIM Tool Kit is not color coded. It is designed to be copied and pasted. If you need the visual color reference, you can refer to this file online or copy it to your PC.

Step 1 - Add a quantity field to your products.dbf. It needs to be a numeric field, probably with a length of 4 (depends on your anticipated inventory level, eg 9999). I have placed a quick DOS utility on my web site which you can use if you don't have a 100% dBASEIII plus compatible database editor http://www.emporiumplus.com/download/newfield.zip You can then fill that field, either by using the prodquan.mv applet for mass insertion or custadm.mv for individual product quantity updates. See the comments for the prodquan.mv or custread.txt for their respective usage.

Step 2 - Install the im.mv module as a fulfillment type module. FTP it to your Merchant modules/fulfill directory. Then you add it through Merchant admin with path modules/fulfill/im.mv This module will decrement your quantity for the ordered products when the customer's order goes through. It will not decrement when someone simply places an item in the basket. That would cause your inventory to deplete with orders which may not ever be completed, eg the customer changed their mind and left your site. The downside is that two people could place your last item in their basket before either checked out. One would have to be backordered. If the chance of this happening is high (very active site), you might want to modify the entries discussed in Step 3 below to deal with a special message when the quantity reaches 1 or 2.

Step 3 - If you are using the Open UI look and feel, skip this step and go to Step 6, installing the cim_sys.mv module.

The following are changes required in the mmui.mv file to prevent the Add to Basket and Buy Now buttons from showing when quantity is 0. You should back up your original mmui.mv in case you make errors in your editing. In each place where you make changes you should preceed the change with a <MvCOMMENT> tagged info block. That helps you identify changes you've made in your code. Be sure you save this imread.txt file, so that when you update your Merchant, you will be able to go back in and make the changes again. It should not take you more than five minutes to make the following changes. In each case in mmui.mv you will see the code that is between the <MvELSE> and </MvIF> tags as the standard code already there. What you are adding is code to check the value in the new quantity field which you must have already added to your products database (para 1). So you need to either replace the whole section or just add the 4 lines from the <MvCOMMENT> to the <MvELSE> and add the </MvIF> after the original lines of code. If you want to add a special message about low inventory, you would probably do it right after the <MvELSE> tag. You will do this in each of the functions named. Note that each is a little different in its 4 lines because of the table layout in those functions.

1) FUNCTION MMUI_Search <MvCOMMENT>wcw inventory management</MvCOMMENT> <MvIF EXPR = "{Products.d.quantity LT 1}"> <small>Sorry, this item is out of stock...</small> <MvELSE> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> <MvLOCALIZED NAME = "l.alt_text" ID = "MER-MMU-00512"> <MvLOCALIZED-TEXT LANGUAGE = "en-US">Add One to Basket</MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE">Eines in den Warenkorb legen</MvLOCALIZED-TEXT> </MvLOCALIZED> <A HREF = "&[ g.sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=SRCH&Store_Code=&[ Stores.d.code:attribute ]&Search=&[ g.Search:attribute ]&Offset=&[ g.Offset:attributes ]&[ l.previous_stack_param ]"><IMG SRC = "&[ MMUI_Store.d.addone:entities ]" BORDER = 0 ALT = "&[l.alt_text];"></A> <MvLOCALIZED NAME = "l.alt_text" ID = "MER-MMU-00513"> <MvLOCALIZED-TEXT LANGUAGE = "en-US">Buy One Now</MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE">Jetzt eines kaufen</MvLOCALIZED-TEXT> </MvLOCALIZED> <MvIF EXPR = "{ MMUI_Store.d.ordr_acnt AND ( BasketList.d.cust_id EQ 0 ) }"> <A HREF = "&[ g.secure_sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=LOGN&Order=1&Store_Code=&[ Stores.d.code:attribute]"><IMG SRC = "&[ MMUI_Store.d.buyone:entities ]" BORDER = 0 ALT = "&[l.alt_text];"></A> <MvELSE> <A HREF = "&[ g.secure_sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=OINF&Store_Code=&[ Stores.d.code:attribute]"><IMG SRC = "&[ MMUI_Store.d.buyone:entities ]" BORDER = 0 ALT = "&[l.alt_text];"></A> </MvIF> </FONT> </MvIF>

2) FUNCTION MMUI_ProductList <MvCOMMENT>wcw inventory management</MvCOMMENT> <MvIF EXPR = "{Products.d.quantity LT 1}"> <small>Sorry, this item is out of stock...</small> <MvELSE> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> <MvLOCALIZED NAME = "l.alt_text" ID = "MER-MMU-00514"> <MvLOCALIZED-TEXT LANGUAGE = "en-US">Add One to Basket</MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE">1 in den Warenkorb legen</MvLOCALIZED-TEXT> </MvLOCALIZED> <A HREF = "&[ g.sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=PLST&Store_Code=&[ Stores.d.code:attribute ]&Offset=&[ g.Offset:attribute ]&[ l.previous_stack_param ]"><IMG SRC = "&[ MMUI_Store.d.addone:entities ]" BORDER = 0 ALT = "&[l.alt_text]"></A> <MvLOCALIZED NAME = "l.alt_text" ID = "MER-MMU-00515"> <MvLOCALIZED-TEXT LANGUAGE = "en-US">Buy One Now</MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE">Jetzt eines kaufen</MvLOCALIZED-TEXT> </MvLOCALIZED> <MvIF EXPR = "{ MMUI_Store.d.ordr_acnt AND ( BasketList.d.cust_id EQ 0 ) }"> <A HREF = "&[ g.secure_sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=LOGN&Order=1&Store_Code=&[ Stores.d.code:attribute ]"><IMG SRC = "&[ MMUI_Store.d.buyone:entities ]" BORDER = 0 ALT = "&[l.alt_text]"></A> <MvELSE> <A HREF = "&[ g.secure_sessionurl ]Action=ADPR&Attributes=Yes&Product_Code=&[ Products.d.code:attribute ]&Quantity=1&Screen=OINF&Store_Code=&[ Stores.d.code:attribute ]"><IMG SRC = "&[ MMUI_Store.d.buyone:entities ]" BORDER = 0 ALT = "&[l.alt_text]"></A> </MvIF> </FONT> </MvIF>

3) FUNCTION MMUI_Category <MvCOMMENT>wcw inventory management</MvCOMMENT> <MvIF EXPR = "{Products.d.quantity LT 1}"> <TD><B>Sorry, this item is out of stock...</B></TD><TD> </TD> <MvELSE> <FORM METHOD = "post" ACTION = "&[ g.sessionurl ]"> <TD ALIGN = "left" VALIGN = "top"> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> <INPUT TYPE = "hidden" NAME = "Action" VALUE = "ADPR"> <INPUT TYPE = "hidden" NAME = "Product_Code" VALUE = "&[ Products.d.code:entities ]"> <INPUT TYPE = "hidden" NAME = "Quantity" VALUE = 1> <INPUT TYPE = "hidden" NAME = "Screen" VALUE = "CTGY"> <INPUT TYPE = "hidden" NAME = "Attributes" VALUE = "Yes"> <INPUT TYPE = "hidden" NAME = "Store_Code" VALUE = "&[ Stores.d.code:entities ]"> <INPUT TYPE = "hidden" NAME = "Category_Code" VALUE = "&[ Categories.d.code:entities ]"> <INPUT TYPE = "hidden" NAME = "Offset" VALUE = "&[ g.Offset:entities ]"> <MvLOCALIZED ID = "MER-MMU-00063"> <MvLOCALIZED-TEXT LANGUAGE = "en-US"><INPUT TYPE = "submit" VALUE = "Add 1 To Basket"></MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE"><INPUT TYPE = "submit" VALUE = "1 in den Warenkorb legen"></MvLOCALIZED-TEXT> </MvLOCALIZED> </FONT> </TD> </FORM> <FORM METHOD = "post" ACTION = "&[ g.secure_sessionurl ]"> <TD ALIGN = "left" VALIGN = "top"> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> <INPUT TYPE = "hidden" NAME = "Action" VALUE = "ADPR"> <INPUT TYPE = "hidden" NAME = "Product_Code" VALUE = "&[ Products.d.code:entities ]"> <INPUT TYPE = "hidden" NAME = "Quantity" VALUE = 1> <MvIF EXPR = "{ MMUI_Store.d.ordr_acnt AND ( BasketList.d.cust_id EQ 0 ) }"> <INPUT TYPE = "hidden" NAME = "Screen" VALUE = "LOGN"> <INPUT TYPE = "hidden" NAME = "Order" VALUE = 1> <MvELSE> <INPUT TYPE = "hidden" NAME = "Screen" VALUE = "OINF"> </MvIF> <INPUT TYPE = "hidden" NAME = "Attributes" VALUE = "Yes"> <INPUT TYPE = "hidden" NAME = "Store_Code" VALUE = "&[ Stores.d.code:entities ]"> <MvLOCALIZED ID = "MER-MMU-00064"> <MvLOCALIZED-TEXT LANGUAGE = "en-US"><INPUT TYPE = "submit" VALUE = "Buy 1 Now"></MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE"><INPUT TYPE = "submit" VALUE = "Jetzt 1 kaufen"></MvLOCALIZED-TEXT> </MvLOCALIZED> </FONT> </TD> </FORM> </MvIF>

4) FUNCTION MMUI_ProductDisplay <MvCOMMENT>wcw inventory management</MvCOMMENT> <MvIF EXPR = "{Products.d.quantity LT 1}"> <B>Sorry, this item is out of stock...</B> <MvELSE> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> <MvLOCALIZED ID = "MER-MMU-00073"> <MvLOCALIZED-TEXT LANGUAGE = "en-US">Quantity:</MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE">Menge:</MvLOCALIZED-TEXT> </MvLOCALIZED> <INPUT TYPE = "text" NAME = "Quantity" VALUE = 1 SIZE = 4> <MvLOCALIZED ID = "MER-MMU-00074"> <MvLOCALIZED-TEXT LANGUAGE = "en-US"><INPUT TYPE = "submit" VALUE = "Add To Basket"></MvLOCALIZED-TEXT> <MvLOCALIZED-TEXT LANGUAGE = "de-DE"><INPUT TYPE = "submit" VALUE = "In den Warenkorb legen"></MvLOCALIZED-TEXT> </MvLOCALIZED> </FONT> </MvIF>

Optionally, if you want to display the quantity in stock you can add the following code into this MMUI_ProductDisplay function. It is your choice at what specific location you want it to appear. The code to add is: <TR><TD> <FONT FACE = "&[ MMUI_Store.d.body_font:entities ]" SIZE = "&[ MMUI_Store.d.body_fsize:entities ]"> Quantity in Stock: <B><MVEVAL EXPR="{ Products.d.quantity }"></B> </FONT> </TD></TR>

Step 4 - It is possible for two customers to place the last item in their basket. Until one submits payment the basket contents are not actually committed and the product quantity has not changed. When the payment is authorized, the product quantity decrements by the number in the basket. This could result in the second customer who was simultaneously shopping to not have the product available when he checks out. To ensure that he is notified that this has happened you will need to insert the following two code segments into the mmui.mv file, inside the MMUI_DisplayBasket function. Near the top of the function locate the line: <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.found" VALUE = "{ Basket_FindFirst_ID( g.Basket_ID ) }"> Right after that line, insert the following 12 lines. <MvCOMMENT>wcw recheck function</MvCOMMENT> <MvIF EXPR = "{l.found}"> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.ok" VALUE = "{ Product_Find_Code( Baskets.d.code ) }"> <MvIF EXPR = "{l.ok AND (Baskets.d.quantity GT Products.d.quantity) }"> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.ok" VALUE = "{ Basket_Delete_Line( Baskets.d.line_id ) }"> <BR><FONT color="#ff0000"> <A HREF="&[g.sessionurl:entities];Screen=PROD&Store_Code=&[Stores.d.code:attribute]&Product_Code=&[Baskets.d.code:attribute]"> <MvEVAL EXPR = "{Baskets.d.name}"></A> has just been sold prior to your checkout. Please restart your checkout.<BR><BR></FONT> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.found" VALUE = "{ Basket_FindFirst_ID( g.Basket_ID ) }"> </MvIF> </MvIF> <MvCOMMENT>wcw end recheck</MvCOMMENT>

Next, scroll down and locate the line: <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.found" VALUE = "{ Basket_FindNext_ID( g.Basket_ID ) }"> Right after that line, insert the following 12 lines.

<MvCOMMENT>wcw recheck function</MvCOMMENT> <MvIF EXPR = "{l.found}"> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.ok" VALUE = "{ Product_Find_Code( Baskets.d.code ) }"> <MvIF EXPR = "{l.ok AND (Baskets.d.quantity GT Products.d.quantity) }"> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.ok" VALUE = "{ Basket_Delete_Line( Baskets.d.line_id ) }"> <TR><TD colspan="6"><FONT color="#ff0000"> <A HREF="&[g.sessionurl:entities];Screen=PROD&Store_Code=&[Stores.d.code:attribute]&Product_Code=&[Baskets.d.code:attribute]"> <MvEVAL EXPR = "{Baskets.d.name}"></A> has just been sold prior to your checkout. Please restart your checkout.</FONT></TD></TR> <MvDO FILE = "{ g.Module_Library_DB }" NAME = "l.found" VALUE = "{ Basket_FindNext_ID( g.Basket_ID ) }"> </MvIF> </MvIF> <MvCOMMENT>wcw end recheck</MvCOMMENT>

Note: there are two other lines which look similar to the ones you are locating. Do not confuse these with them. There will still be a few seconds when a customer enters their CC and submits it that an out of stock purchase could be made if two people submit their final payment screen at the same time.

Step 5 - In the mmui.mv file locate the MMUI_Begin_Screen function. On the line before the first <TABLE tag in this function, place the following line: <MvDO FILE = "{g.Module_Root$'modules/fulfill/im.mv'}" NAME = "l.showbox" VALUE = "{Show_IM_Errors()}">

Step 6 - Next to prevent customers from ordering more items than you have in stock, add the cim_sys.mv module to the modules/system/ directory. Install it to the mall, then install it to your store.

Step 7 - If you are individually adding new records to the product file through the admin interface you need to add code to blank out a new record before it is added. Otherwise, data from a previous record will be inserted into fields which were not provided new data. Hence, in the /lib/db.mv find the function below. Just after the Open_Products line, add the 12 lines shown.

<MvFUNCTION NAME = "Product_Insert" PARAMETERS = "code, name, thumbnail, image, price, cost, desc, weight, taxable" STANDARDOUTPUTLEVEL = ""> <MvASSIGN NAME = "l.product_id" VALUE = 0> <MvIF EXPR = "{ _DB_Store_Open_Products() }">

<MvCOMMENT>wcw allows manual addition with extra fields</MvCOMMENT> <MvIF EXPR = "{NOT fexists(g._DB_Store_Directory$'prodstru.dbf')}"> <MvPRIMARY NAME = "Products" INDEX = "{ g._DB_Store_Directory $ 'prd_id.mvx' }"> <MvREVEALSTRUCTURE DATABASE="{ g._DB_Store_Directory $ 'prodstru.dbf' }"> </MvIF> <MvOPEN NAME="structure" DATABASE="{ g._DB_Store_Directory $ 'prodstru.dbf' }"> <MvWHILE EXPR = "{NOT structure.d.eof}"> <MvASSIGN NAME="Products.d.&[structure.d.field_name]" VALUE=""> <MvSKIP NAME="structure"> </MvWHILE> <MvCLOSE NAME="structure"> <MvCOMMENT>wcw end code to insert</MvCOMMENT>

If you are not adding products one at a time, but instead using the flat file import, you will be using prodquan.mv to import the quantities, so you will not need to add the code in Step 7. Not using the step 7 code will save processing time. Alternatively, you could leave out this code and update the quantity of newly added products either by using prodquan.mv (for mass updates) or custadm.mv for individual product quantity changes.

Step 8 - In admin under the store fulfillment module configuration, be sure the Inventory Management module is checked, then select Update. There are also configuration settings you can add if you want to be notified when stock levels reach a certain amount.