Difference between revisions of "VbzCart/tables"

from HTYP, the free directory anyone can edit if they can prove to me that they're not a spambot
Jump to navigation Jump to search
(→‎Stock Management: moved all tables to separate pages; +stock_history_legacy)
(moved the rest of the tables and procs into separate pages; removed obsolete table-design notes)
Line 4: Line 4:
 
* [[/customer]] tables
 
* [[/customer]] tables
  
==Tables==
+
==Main Data==
*"#" indicates Primary Key fields
 
*"@" indicates autonumbered fields
 
 
===Catalog===
 
===Catalog===
 
These tables describe and price the items displayed in the catalog pages and in shopping carts.
 
These tables describe and price the items displayed in the catalog pages and in shopping carts.
Line 26: Line 24:
 
====future====
 
====future====
 
It may be that Departments and Suppliers should be handled by the Topics tree, but this involves creating some infrastructure which is going to take some doing. (Specifically, we need to be able to assign named values – like "catkey" – to any topic, so that each Department topic and Supplier topic can refer to the proper catalog number.)
 
It may be that Departments and Suppliers should be handled by the Topics tree, but this involves creating some infrastructure which is going to take some doing. (Specifically, we need to be able to assign named values – like "catkey" – to any topic, so that each Department topic and Supplier topic can refer to the proper catalog number.)
====leftover stuff====
 
Are these still being used? To be checked. 2008-12-06
 
 
SQL for updating availability from current stock:
 
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_CatItems_fr_StkItems_StkBins()
 
    UPDATE cat_items AS i LEFT JOIN (
 
      SELECT
 
        si.ID_Item,
 
        SUM(si.Qty) AS QtyInStock
 
      FROM stk_items AS si LEFT JOIN stk_bins AS sb ON si.ID_Bin=sb.ID
 
      WHERE (sb.isForSale) AND ISNULL(sb.WhenVoided) AND ISNULL(si.WhenRemoved)
 
      GROUP BY ID_Item
 
    ) AS sig ON i.ID=sig.ID_Item
 
      SET
 
        i.QtyInStock = sig.QtyInStock,
 
        i.isForSale = i.isInPrint OR (sig.QtyInStock > 0);</mysql>
 
<section end=sql />
 
 
<section begin=sql /><mysql>CREATE OR REPLACE VIEW _cat_pages AS
 
  SELECT
 
    CONCAT_WS('-','S',ID) AS AB,
 
    LOWER(CatKey) AS Path,
 
    ID,
 
    'S' AS Type
 
  FROM cat_supp
 
  UNION
 
  SELECT
 
    CONCAT_WS('-','D',ID) AS AB,
 
    LOWER(CatWeb_Dept) AS Path,
 
    ID,
 
    'D' AS Type
 
  FROM qryCat_Depts
 
  UNION
 
  SELECT
 
    CONCAT_WS('-','T',ID) AS AB,
 
    REPLACE(LOWER(CatWeb),'-','/') AS Path,
 
    ID,
 
    'T' AS Type
 
  FROM qryCat_Titles
 
  UNION
 
  /* image pages (every size except thumbnail and small) */
 
  SELECT
 
    CONCAT_WS('-','I',i.ID) AS AB,
 
    LOWER(CONCAT_WS('/',REPLACE(t.CatWeb,'-','/'),AttrFldr,i.Ab_Size)) AS Path,
 
    i.ID,
 
    'I' AS Type
 
  FROM cat_images AS i LEFT JOIN qryCat_Titles AS t ON i.ID_Title=t.ID WHERE (i.isActive) AND (i.Ab_Size NOT IN ('th','sm'));</mysql>
 
<section end=sql />
 
 
<section begin=sql /><mysql>CREATE PROCEDURE Upd_CatPages()
 
  REPLACE INTO cat_pages
 
  SELECT * FROM _cat_pages
 
  ORDER BY Path;</mysql>
 
<section end=sql />
 
 
This --
 
* LOWER(CONCAT_WS('/',REPLACE(t.CatWeb,'-','/'),AttrFldr,i.Ab_Size)) AS Path,
 
...is intended to be a simplified version of this:
 
* LOWER(CONCAT(REPLACE(t.CatWeb,'-','/'),'/',CONCAT_WS('/',AttrFldr,i.Ab_Size))) AS Path,
 
(saved here in case the simplification doesn't work)
 
 
 
===Catalog Entry===
 
===Catalog Entry===
 
These are tables used to make it easier to update the catalog. The key concept here is "catalog title groups" (CTGs). A CTG is a set of titles which are all available with the same set of options (e.g. sizes) at the same prices (e.g. $10 S, $11 M-XL, $12 2XL). The final vbz catalog (cat_items) is the result of a sort of vector multiplication of Titles (cat_titles) and CTGs plus any items in stock.
 
These are tables used to make it easier to update the catalog. The key concept here is "catalog title groups" (CTGs). A CTG is a set of titles which are all available with the same set of options (e.g. sizes) at the same prices (e.g. $10 S, $11 M-XL, $12 2XL). The final vbz catalog (cat_items) is the result of a sort of vector multiplication of Titles (cat_titles) and CTGs plus any items in stock.
Line 94: Line 30:
  
 
See [[VbzCart catalog building]] for queries and processes.
 
See [[VbzCart catalog building]] for queries and processes.
====ctg_supp====
+
* [[/ctg_supp]]
Information about suppliers which isn't needed for customer web site
+
* [[/ctg_prc_funcs]]
<section begin=sql /><mysql>CREATE TABLE `ctg_supp` (
+
* [[/ctg_sources]]
  ID INT NOT NULL COMMENT "cat_supp.ID - supplier to which information applies",
+
* [[/ctg_groups]]
  ID_PriceFunc INT DEFAULT NULL COMMENT "ctg_prc_funcs.ID - function to use when calculating prices for this supplier",
+
* [[/ctg_titles]]
  PRIMARY KEY(`ID`)
+
* [[/ctg_items]]
)
+
* [[/ctg_updates]]
ENGINE = MYISAM;</mysql>
+
* [[/ctg_upd1]]
<section end=sql />
+
* [[/ctg_upd2]]
====ctg_prc_funcs====
 
Functions for calculating our retail price from the wholesale cost we pay:
 
: retail = {(wholesale * PriceFactor) + PriceAddend} rounded up to next PriceRound
 
<section begin=sql /><mysql>CREATE TABLE `ctg_prc_funcs` (
 
  ID          INT NOT NULL AUTO_INCREMENT,
 
  Name        VARCHAR(63) NOT NULL COMMENT "descriptive name for this price function",
 
  PriceFactor DECIMAL(9,2) COMMENT "amount by which to multiple wholesale cost",
 
  PriceAddend DECIMAL(9,2) COMMENT "amount by which to pad wholesale cost",
 
  PriceRound  DECIMAL(9,2) COMMENT "round-up-to-next this amount",
 
  Notes      VARCHAR(255) COMMENT "usage notes etc.",
 
  PRIMARY KEY(`ID`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
====ctg_sources====
 
<section begin=sql /><mysql>CREATE TABLE `ctg_sources` (
 
  ID          INT NOT NULL AUTO_INCREMENT,
 
  Name        VARCHAR(63) NOT NULL COMMENT "name of source",
 
  Abbr        VARCHAR(15) NOT NULL COMMENT "abbreviation, for drop-down lists",
 
  ID_Supplier INT NOT NULL COMMENT "Suppliers.ID - supplier for this source",
 
  DateAvail  DATETIME DEFAULT NULL COMMENT "earliest date on which this source is valid",
 
  ID_Supercede INT DEFAULT NULL COMMENT "[Supplier Catalogs].ID of catalog which supercedes this one (NULL = this one is current)",
 
  isCloseOut  BOOL DEFAULT FALSE COMMENT "TRUE = this catalog is a list of discontinued items no longer in print (closeouts); FALSE = normal catalog",
 
  PRIMARY KEY(`ID`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
* To discontinue a supplier, set ID_Supercede=ID for all active sources.
 
====ctg_groups====
 
Master list of groups (CTGs)
 
<section begin=sql /><mysql>CREATE TABLE `ctg_groups` (
 
  ID          INT NOT NULL AUTO_INCREMENT,
 
  ID_Supplier INT DEFAULT NULL COMMENT "Suppliers.ID -- supplier to which this group applies (NULL = no fixed supplier)",
 
  Name        VARCHAR(63) DEFAULT NULL COMMENT "Name of this group (brief)",
 
  Descr      VARCHAR(127) DEFAULT NULL COMMENT "Description (for display; if NULL, use ItemType fields)",
 
  isActive    BOOL DEFAULT FALSE,
 
  Code        VARCHAR(7) DEFAULT NULL COMMENT "suffix for Items.CatNum",
 
  Sort        VARCHAR(31) DEFAULT NULL COMMENT "sorting key within group list",
 
  ID_Redir    INT DEFAULT NULL COMMENT "TGroups.ID -- if not null, redirect all references to the current ID to use ID_Redir instead",
 
PRIMARY KEY(`ID`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====ctg_titles====
 
Specifies which groups a title belongs to
 
<section begin=sql /><mysql>CREATE TABLE `ctg_titles` (
 
  ID          INT NOT NULL AUTO_INCREMENT,
 
  ID_Title    INT DEFAULT NULL COMMENT "cat_titles.ID",
 
  ID_Group    INT DEFAULT NULL COMMENT "ctg_groups.ID",
 
  ID_Source  INT DEFAULT NULL COMMENT "ctg_sources.ID - source which enables this selection",
 
  WhenDiscont DATETIME DEFAULT NULL COMMENT "non-sourced discontinuation",
 
  GroupCode  VARCHAR(7) DEFAULT NULL COMMENT "catalog code extension for this title group (optional)",
 
  GroupDescr  VARCHAR(127) DEFAULT NULL COMMENT "description extension for this title group (required if GroupCode is used)",
 
  GroupSort  VARCHAR(7) DEFAULT NULL COMMENT "sorting override",
 
  isActive    BOOL DEFAULT FALSE COMMENT "this group-title membership is active?",
 
  Supp_CatNum VARCHAR(15) DEFAULT NULL COMMENT "catalog # for restock from supplier",
 
  Notes      VARCHAR(255) DEFAULT NULL COMMENT "notes about this particular title's availability in this group",
 
  PRIMARY KEY(`ID`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====ctg_items====
 
Specifies which item varieties/prices are available in a group
 
<section begin=sql /><mysql>CREATE TABLE `ctg_items` (
 
  ID            INT NOT NULL AUTO_INCREMENT,
 
  ID_Group      INT NOT NULL COMMENT "ctg_groups.ID",
 
  ID_ItTyp      INT NOT NULL COMMENT "cat_ittyps.ID -- basic item type",
 
  ID_ItOpt      INT DEFAULT NULL COMMENT "cat_itopts.ID -- option for this item",
 
  Descr          VARCHAR(63) COMMENT "name or description for this item/option",
 
  PriceBuy      DECIMAL(9,2) DEFAULT NULL COMMENT "cost to us",
 
  PriceSell      DECIMAL(9,2) DEFAULT NULL COMMENT "our price to retail customer",
 
  PriceList      DECIMAL(9,2) DEFAULT NULL COMMENT "supplier's suggested retail price",
 
  ID_ShipCost    INT DEFAULT NULL COMMENT "cat_ship_costs.ID",
 
  isActive      BOOL DEFAULT FALSE COMMENT "this group/type/option is available",
 
  PRIMARY KEY(`ID`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====ctg_updates====
 
There may be a better way to do this, but haven't been able to figure it out yet. This table keeps track of the correspondence between {items as generated from the catalog title group data} and {items in cat_items}. Off the top of my head, I can't remember why we need to keep more fields than just ID_Item and IDS_Item, but I did look at this table very closely awhile back and ended up with all those extra fields still in it. TO DO: document why they're needed.
 
<section begin=sql /><mysql>CREATE TABLE `ctg_updates` (
 
  ID_Item        INT NOT NULL AUTO_INCREMENT COMMENT "cat_items.ID",
 
  IDS_Item      VARCHAR(63) NOT NULL UNIQUE COMMENT "cat_titles.ID + CatSfx",
 
  isActive      BOOL NOT NULL DEFAULT FALSE COMMENT "current sources do generate a record for this item",
 
  CatSfx        VARCHAR(31) DEFAULT NULL COMMENT "suffix which uniquely identifies this item's genome, so to speak, within all items for the same title",
 
  CatNum        VARCHAR(63) NOT NULL COMMENT "CatNum = Title.CatNum + CatSfx",
 
  ID_Title      INT DEFAULT NULL,
 
  ID_CTG_Group  INT DEFAULT NULL,
 
  ID_CTG_Title  INT DEFAULT NULL,
 
  ID_CTG_Item    INT DEFAULT NULL,
 
  ID_ItTyp      INT DEFAULT NULL COMMENT "needed for join with cat_items, even though it comes from ID_CTG_Item",
 
  ID_ItOpt      INT DEFAULT NULL COMMENT "needed for join with cat_items, even though it comes from ID_CTG_Item",
 
  PRIMARY KEY(`IDS_Item`),
 
  INDEX(`ID_Item`),
 
  INDEX(`CatNum`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
* '''isActive''' is necessary because otherwise there was no way to convey that a formerly-existing item is no longer active; while it ''should'' be possible to pull this information back out from qryCtg_Items_updates_joinable when updating cat_items from ctg_updates, the necessary join (showing all items in cat_items not found in qryCtg_Items_updates_joinable) seems to take a prohibitively long time to run. Perhaps this can be optimized later, which would also eliminate the need for most of the fields in ctg_updates.
 
====ctg_upd1====
 
Experimental, but hopefully will replace ctg_updates which never worked entirely properly. This table contains only the essential fields from qryCtg_src.
 
<section begin=sql /><mysql>CREATE TABLE `ctg_upd1` (
 
  CatSfx            VARCHAR(31),
 
  isCloseOut        BOOL,
 
  ID_CTG_Title      INT,
 
  ID_CTG_Item      INT,
 
  ID_Title          INT,
 
  ID_ItTyp          INT,
 
  ID_ItOpt          INT,
 
  ID_ShipCost      INT,
 
  PriceBuy          DECIMAL(9,2),
 
  PriceSell        DECIMAL(9,2),
 
  PriceList        DECIMAL(9,2),
 
  ItOpt_Descr_part  VARCHAR(63),
 
  NameSng          VARCHAR(63),
 
  GrpItmDescr      VARCHAR(63),
 
  TitleGroupDescr  VARCHAR(63),
 
  OptionDescr      VARCHAR(63),
 
  ItOpt_Sort        VARCHAR(31),
 
  GrpCode          VARCHAR(15),
 
  GrpDescr          VARCHAR(31),
 
  GrpSort          VARCHAR(31),
 
  IDS_Item          VARCHAR(31),
 
  CatNum            VARCHAR(31),
 
  ItOpt_Descr      VARCHAR(63),
 
  PRIMARY KEY(`IDS_Item`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
====ctg_upd2====
 
Experimental, but hopefully will replace ctg_updates which never worked entirely properly.
 
<section begin=sql /><mysql>CREATE TABLE `ctg_upd2` (
 
  ID_Item        INT DEFAULT NULL            COMMENT "cat_items.ID",
 
  IDS_Item      VARCHAR(63) NOT NULL UNIQUE COMMENT "cat_titles.ID + CatSfx",
 
  isForSale      BOOL DEFAULT FALSE          COMMENT "to be recalculated (assume false, then set true if available or in stock)",
 
  QtyInStock    INT DEFAULT NULL            COMMENT "to be recalculated",
 
  Supp_CatNum    VARCHAR(32)                COMMENT "preserved from cat_items",
 
  QtyMin_Stk    INT DEFAULT NULL            COMMENT "preserved from cat_items",
 
  cntDups        INT DEFAULT 0,
 
  PRIMARY KEY(`IDS_Item`),
 
  UNIQUE KEY(`ID_Item`)
 
)
 
ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
  
 
===Ordering===
 
===Ordering===
Line 275: Line 65:
 
* [[/core_restocks]] - OBSOLETE
 
* [[/core_restocks]] - OBSOLETE
 
* [[/rstk_lines]] - OBSOLETE
 
* [[/rstk_lines]] - OBSOLETE
 
 
===Shopping===
 
===Shopping===
 
These tables store data generated by the user during the shopping process.
 
These tables store data generated by the user during the shopping process.
====shop_carts====
+
* [[/shop_carts]]
Shopping cart data is kept separate from order data because we end up with a lot of carts that never become orders; eventually they get cleaned out. Order data may eventually get cleaned out too, but with different criteria; for now, we are keeping order data indefinitely.
+
* [[/shop_cart_lines]]
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_carts`;
+
* [[/shop_cust_data]]
CREATE TABLE `shop_carts` (
+
* [[/shop_cust_data_types]]
  `ID`        INT NOT NULL AUTO_INCREMENT,
+
* [[/shop_log]]
  `ID_Session` INT DEFAULT NULL      COMMENT "shop_sessions.ID, when implemented -- session in which cart was created",
+
* [[/shop_clients]]
  `WhenCreated` DATETIME NOT NULL    COMMENT "when the cart was first created",
+
* [[/shop_sessions]]: to be implemented. A new session should be created whenever the "same" client doesn't remember the session cookie (could be a different computer on the same IP address), or if the session has timed out.
  `WhenViewed`  DATETIME DEFAULT NULL COMMENT "when the cart's contents were last displayed by the customer",
 
  `WhenUpdated` DATETIME DEFAULT NULL COMMENT "when the cart's contents were last changed",
 
  `WhenOrdered` DATETIME DEFAULT NULL COMMENT "when the cart's contents were transferred to an order",
 
  `ID_Order`  INT DEFAULT NULL      COMMENT "core_orders.ID of order into which cart was transferred",
 
  `ID_Cust`    INT DEFAULT NULL      COMMENT "core_custs.ID of customer for this order",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
* '''ID_Cust''' is for future use when customers can log in to retrieve their personal data
 
* '''ID_Session''' is for future use when sessions are tracked
 
 
 
====shop_cart_lines====
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_cart_lines`;
 
CREATE TABLE `shop_cart_lines` (
 
  `ID`        INT NOT NULL AUTO_INCREMENT,
 
  `ID_Cart`    INT NOT NULL COMMENT "shop_carts.ID",
 
  `ID_Item`    INT NOT NULL COMMENT "cat_items.ID",
 
  `Qty`        INT NOT NULL COMMENT "quantity ordered; 0 = removed from order",
 
  `WhenAdded`  DATETIME NOT NULL COMMENT "when this item was first added to the order",
 
  `WhenEdited` DATETIME NOT NULL COMMENT "when the quantity for this item was last changed",
 
  `PriceItem`  DECIMAL(9,2) NOT NULL COMMENT "price of item quoted at order time",
 
  `PriceShItm` DECIMAL(9,2) NOT NULL COMMENT "per-item shipping quoted for this item at order time",
 
  `PriceShPkg` DECIMAL(9,2) NOT NULL COMMENT "per-package shipping quoted for this item at order time",
 
/* These fields are redundant, and are used mainly to speed up cart display. They also preserve a record of
 
  how the item was presented to the customer. */
 
  `CatNum`    VARCHAR(63) COMMENT "cat_items.CatNum of item as sold",
 
  `ID_Title`  INT NOT NULL COMMENT "cat_titles.ID",
 
  `DescText`  VARCHAR(255) NOT NULL COMMENT "plain-text description of item being ordered (e.g. for emails)",
 
  `DescHtml`  VARCHAR(255) NOT NULL COMMENT "HTML description of item being ordered, for web-page display",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====shop_cust_data====
 
This is a simple table for retaining customer data entered at check-out time before converting the cart to an order.
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_cust_data`;
 
CREATE TABLE `shop_cust_data` (
 
  `ID`        INT NOT NULL AUTO_INCREMENT,
 
  `ID_Cart`    INT NOT NULL COMMENT "shop_carts.ID",
 
  `ID_Key`    INT NOT NULL COMMENT "shop_cust_data_types.ID of data type",
 
  `Val`        VARCHAR(255) NOT NULL COMMENT "Value of data item",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====shop_cust_data_types====
 
This table is probably unnecessary, but I can't convince myself of that. If nothing else, it will help prevent inconsistent usage.
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_cust_data_types`;
 
CREATE TABLE `shop_cust_data_types` (
 
  `ID`        INT NOT NULL AUTO_INCREMENT,
 
  `Name`      VARCHAR(31) NOT NULL COMMENT "Short name, for reports or whatever",
 
  `Descr`      VARCHAR(255) DEFAULT NULL COMMENT "Longer description of data item, e.g. usage notes",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====shop_log====
 
Under construction
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_log`;
 
CREATE TABLE `shop_log` (
 
  `ID`        INT      NOT NULL AUTO_INCREMENT,
 
  `ID_Client`  INT      NOT NULL COMMENT "shop_clients.ID",
 
  `EvWhen`    DATETIME NOT NULL COMMENT "When this event occurred",
 
  `Code`      INT      NOT NULL COMMENT "type of event",
 
  `Descr`      VARCHAR(255) DEFAULT NULL COMMENT "Any details specific to this occurrence",
 
  `Notes`      VARCHAR(255) DEFAULT NULL COMMENT "Human-entered notes",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
====shop_clients====
 
Under construction
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `shop_clients`;
 
CREATE TABLE `shop_clients` (
 
  `ID`        INT NOT NULL AUTO_INCREMENT,
 
  `CRC`        INT UNSIGNED NOT NULL COMMENT "crc32(Address+Browser) - unique integer defined by client specs",
 
  `Address`    VARCHAR(15) NOT NULL COMMENT "IP address of client",
 
  `Domain`    VARCHAR(255) COMMENT "Domain name of client",
 
  `Browser`    VARCHAR(255) COMMENT "Browser USER_AGENT string",
 
  `WhenFirst`  DATETIME NOT NULL COMMENT "When this client was first seen",
 
  `WhenFinal`  DATETIME COMMENT "When this client was most recently seen",
 
  PRIMARY KEY(`ID`),
 
  UNIQUE KEY(`CRC`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
* Decided to use CRC32 instead of compression, because the compressed strings always came out at least as long as the original string. They were probably designed to be effective for rather longer data.
 
 
 
====shop_sessions====
 
To be implemented. A new session should be created whenever the "same" client doesn't remember the session cookie (could be a different computer on the same IP address), or if the session has timed out.
 
  
 
===Stock Management===
 
===Stock Management===
Line 381: Line 85:
 
===Browsing===
 
===Browsing===
 
Topics and images
 
Topics and images
====brs_topics====
+
* [[/brs_topics]]
<section begin=sql /><mysql>DROP TABLE IF EXISTS `brs_topics`;
+
* [[/brs_titles_x_topics]]
CREATE TABLE `brs_topics` (
 
  `ID`        INT          NOT NULL AUTO_INCREMENT,
 
  `ID_Parent` INT          DEFAULT NULL COMMENT "brs_topics.ID of parent topic",
 
  `Name`      varchar(128) NOT NULL    COMMENT "generic name (context-free, but as short as possible)",
 
  `NameTree`  varchar(64)  DEFAULT NULL COMMENT "name within context of parent; defaults to Name",
 
  `NameFull`  varchar(255) DEFAULT NULL COMMENT "descriptive standalone name (context free, can be long)",
 
  `Sort`      varchar(15)  DEFAULT NULL COMMENT "optional sorting key",
 
  `Variants`  varchar(255) DEFAULT NULL COMMENT "synonyms and other keywords which should find this topic",
 
  `Mispeled`  varchar(255) DEFAULT NULL COMMENT "same as Variants, but these are WRONG spellings; avoid displaying",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
====brs_titles_x_topics====
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `brs_titles_x_topics`;
 
CREATE TABLE `brs_titles_x_topics` (
 
  `ID_Title` INT NOT NULL COMMENT "cat_titles.ID",
 
  `ID_Topic` INT NOT NULL COMMENT "brs_topics.ID",
 
  PRIMARY KEY(`ID_Title`,`ID_Topic`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
===Miscellaneous===
 
* '''stats''' &ndash; computed variables, cache timestamps, and other variable stuff
 
<section begin=sql /><mysql> CREATE TABLE `stats` (
 
    `Name` varchar(32) NOT NULL COMMENT 'name of variable',
 
    `Value` varchar(255) COMMENT 'value of variable',
 
  PRIMARY KEY(`Name`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
* '''updates''' &ndash; timestamps on tables so we know when update queries need to be run
 
<section begin=sql /><mysql> CREATE TABLE `updates` (
 
    `Name` varchar(32) NOT NULL COMMENT 'name of thing which gets updated',
 
    `Updated` datetime COMMENT 'when last updated',
 
  PRIMARY KEY(`Name`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
 
Function to record an update:
 
<section begin=sql /><mysql> CREATE PROCEDURE SetUpdate(IN iName varchar(32))
 
BEGIN
 
  REPLACE INTO updates(Name,Updated) values(iName, NOW());
 
END</mysql>
 
<section end=sql />
 
  
 
==Calculated Tables==
 
==Calculated Tables==
===_depts===
+
These are used for caching data which takes a long time (more than 0.3 seconds or so) to calculate and which depends on things which a lot less often than pages are viewed.
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_depts`;
+
* [[/_depts]]
CREATE TABLE `_depts` (
+
** &larr; depts x suppliers &ndash; {{vbzcart|proc|Upd_Depts_fr_Depts_Suppliers}}
  `ID` int(11) unsigned NOT NULL auto_increment,
+
** &larr; _dept_ittyps by ID_Dept &ndash; {{vbzcart|proc|Upd_Depts_fr_DeptIttyps}}
  `ID_Supp` int(11) unsigned NOT NULL COMMENT 'needed to simplify calculation of _titles',
+
* [[/_titles]]
  `CatNum` varchar(63) NOT NULL,
+
** &larr; titles x _depts &ndash; {{vbzcart|proc|Upd_Titles_fr_Depts}}
  `CatKey` varchar(7),
+
** &larr; cat_items (grouped by ID_Title) &ndash; {{vbzcart|proc|Upd_Titles_fr_CatItems}}
  `CatWeb_Dept` varchar(63) NOT NULL,
+
** &larr; _title_ittyps (grouped by ID_Title) &ndash; {{vbzcart|proc|Upd_Titles_fr_TitleIttyps}}
  `CatWeb_Title` varchar(63) NOT NULL,
+
* [[/_title_ittyps]] - list of available titles for each item type
  `cntForSale` int DEFAULT NULL,
+
** &larr; cat_items (grouped by ID_Title), cat_titles &ndash; {{vbzcart|proc|Upd_TitleIttyps_fr_CatItems_Titles}}
  `cntInPrint` int DEFAULT NULL,
+
** &larr; _titles &ndash; {{vbzcart|proc|Upd_TitleIttyps_fr_Titles}}
  `qtyInStock` int DEFAULT NULL,
+
** &larr; cat_ittyps &ndash; {{vbzcart|proc|Upd_TitleIttyps_fr_CatIttyps}}
  PRIMARY KEY (`ID`)
+
* [[/_dept_ittyps]]
) ENGINE=MyISAM DEFAULT CHARSET=latin1;</mysql>
+
** &larr; _title_ittyps &ndash; {{vbzcart|proc|Upd_DeptIttyps_fr_TitleIttyps}}
<section end=sql />
+
** &larr; cat_ittyps &ndash; {{vbzcart|proc|Upd_DeptIttyps_fr_CatIttyps}}
====&larr; depts x suppliers====
+
* [[/_supplier_ittyps]]
SQL to fill _depts (except inPrint/inStock fields):
+
** &larr; suppliers x _titles x _title_ittyps x cat_ittyps ... &ndash; {{vbzcart|proc|Upd_SupplierIttyps}}
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_Depts_fr_Depts_Suppliers()
+
* [[/_stk_containers]]
    BEGIN
+
** &larr; stk_bins, cat_items... &ndash; {{vbzcart|proc|Upd_StkContainers}}
      DELETE FROM _depts;
+
==Data Flow Management==
      INSERT INTO _depts(`ID`,`ID_Supp`,`CatKey`,`CatNum`,`CatWeb_Dept`,`CatWeb_Title`)
+
These tables are all maintained by hand (except for timestamps and data_log), but must match the actual dependencies in the SQL code.
        SELECT
+
* [[/data_tables]] - list of tables involved in data caching, either as a source or as a cache
          d.ID,
+
* [[/data_procs]] - list of [[../procs|stored procedures]] which update one table from others
          d.ID_Supplier AS ID_Supp,
+
* [[/data_flow]] - which procs update which caches from which sources
          UPPER(IFNULL(d.PageKey,d.CatKey)) AS CatKey,
+
* [[/data_log]] - log of all cache updates done automatically
          UPPER(CONCAT_WS("-",s.CatKey,d.CatKey)) AS CatNum,
 
          LOWER(CONCAT_WS("/",s.CatKey,IFNULL(d.PageKey,d.CatKey))) AS CatWeb_Dept,
 
          LOWER(CONCAT_WS("/",s.CatKey,d.CatKey)) AS CatWeb_Title
 
        FROM depts AS d LEFT JOIN suppliers AS s ON d.ID_Supplier=s.ID;
 
    END</mysql>
 
<section end=sql />
 
====&larr; _dept_ittyps by ID_Dept====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_Depts_fr_DeptIttyps()
 
    UPDATE _depts AS d LEFT JOIN (
 
      SELECT
 
        ID_Dept,
 
        SUM(di.cntForSale) AS cntForSale,
 
        SUM(di.cntInPrint) AS cntInPrint,
 
        SUM(di.qtyInStock) AS qtyInStock
 
      FROM _dept_ittyps AS di GROUP BY ID_Dept
 
      ) AS di ON di.ID_Dept=d.ID
 
      SET
 
        d.cntForSale = di.cntForSale,
 
        d.cntInPrint = di.cntInPrint,
 
        d.qtyInStock = di.qtyInStock;</mysql>
 
<section end=sql />
 
===_titles===
 
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_titles`;
 
CREATE TABLE `_titles` (
 
  `ID` int(11) unsigned NOT NULL,
 
  `ID_Supp` int(11) unsigned NOT NULL COMMENT "avoids need for joining with cat_depts",
 
  `CatNum` varchar(63) NOT NULL,
 
  `CatWeb` varchar(63) NOT NULL,
 
  `cntForSale` int DEFAULT NULL,
 
  `cntInPrint` int DEFAULT NULL,
 
  `qtyInStock` int DEFAULT NULL,
 
  `currMinSell` DECIMAL(9,2) DEFAULT NULL COMMENT "minimum selling price for an item of this title",
 
  `currMaxSell` DECIMAL(9,2) DEFAULT NULL COMMENT "maximum selling price for an item of this title",
 
  PRIMARY KEY (`ID`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1;</mysql>
 
<section end=sql />
 
==== &larr; titles x _depts ====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_Titles_fr_Depts()
 
    REPLACE INTO _titles(ID,ID_Supp,CatNum,CatWeb)
 
    SELECT
 
      t.ID,
 
      d.ID_Supp,
 
      UPPER(CONCAT_WS("-",d.CatNum,t.CatKey)) AS CatNum,
 
      LOWER(CONCAT_WS("/",d.CatWeb_Title,t.CatKey)) AS CatWeb
 
    FROM titles AS t LEFT JOIN _depts AS d ON t.ID_dept=d.ID
 
    ORDER BY CatNum;</mysql>
 
<section end=sql />
 
==== &larr; cat_items (grouped by ID_Title) ====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_Titles_fr_CatItems()
 
    UPDATE _titles AS t LEFT JOIN (
 
      SELECT
 
        ID_Title,
 
        SUM(IF(i.isForSale,1,0)) AS cntForSale,
 
        SUM(IF(i.isInPrint,1,0)) AS cntInPrint,
 
        SUM(i.qtyInStock) AS qtyInStock,
 
        MIN(i.PriceSell) AS currMinSell,
 
        MAX(i.PriceSell) AS currMaxSell
 
      FROM cat_items AS i GROUP BY ID_Title
 
      ) AS ig ON ig.ID_Title=t.ID
 
      SET
 
        t.cntForSale = ig.cntForSale,
 
        t.cntInPrint = ig.cntInPrint,
 
        t.qtyInStock = ig.qtyInStock;</mysql>
 
<section end=sql />
 
 
 
==== &larr; _title_ittyps (grouped by ID_Title)====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_Titles_fr_TitleIttyps()
 
      UPDATE _titles AS t LEFT JOIN (
 
        SELECT
 
          ID_Title,
 
          SUM(ti.cntForSale) AS cntForSale,
 
          SUM(ti.cntInPrint) AS cntInPrint,
 
          SUM(ti.qtyInStock) AS qtyInStock
 
        FROM _title_ittyps AS ti GROUP BY ID_Title
 
        ) AS ti ON ti.ID_Title=t.ID
 
        SET
 
          t.cntForSale = ti.cntForSale,
 
          t.cntInPrint = ti.cntInPrint,
 
          t.qtyInStock = ti.qtyInStock;</mysql>
 
<section end=sql />
 
 
 
===_title_ittyps===
 
list of available titles for each item type
 
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_title_ittyps`;
 
CREATE TABLE `_title_ittyps` (
 
  `ID_Title` int unsigned NOT NULL,
 
  `ID_ItTyp` int unsigned NOT NULL,
 
  `ID_Dept` int unsigned NOT NULL,
 
  `TitleName` varchar(127),
 
  `ItTypNameSng` varchar(63) COMMENT "cat_ittyps.NameSng",
 
  `ItTypNamePlr` varchar(63) COMMENT "cat_ittyps.NamePlr",
 
  `ItTypSort` varchar(31) COMMENT "cat_ittyps.Sort",
 
  `cntForSale` int COMMENT "# of different items available for type (either in stock or in print)",
 
  `cntInPrint` int COMMENT "# of different items (for type) in print for this title",
 
  `cntInStock` int COMMENT "# of different items (for type) which are for sale due to at least being in stock",
 
  `qtyInStock` int COMMENT "-1 = some in stock, but don't know how many",
 
  `currMinPrice` DECIMAL(9,2) DEFAULT NULL COMMENT 'minimum price for this item type',
 
  `currMaxPrice` DECIMAL(9,2) DEFAULT NULL COMMENT 'maximum price for this item type',
 
  `CatNum` varchar(63) DEFAULT NULL,
 
  `CatWeb` varchar(63) DEFAULT NULL,
 
  `CatDir` varchar(63) DEFAULT NULL,
 
  PRIMARY KEY (`ID_Title`,`ID_ItTyp`,`ID_Dept`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1;</mysql>
 
<section end=sql />
 
 
 
* '''2007-09-20''' Added cntInStock field because we need to be able to figure out how many line items are available but ''not'' in stock (cntForSale-cntInStock)
 
==== &larr; cat_items (grouped by ID_Title)====
 
This procedure initially fills the table. (This is a revised version which combines two earlier procedures.) (I had a note that "the ID_Dept field is informational, not a grouping", but I don't remember what that meant and it doesn't make sense.)
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_TitleIttyps_fr_CatItems_Titles()
 
    REPLACE INTO _title_ittyps(
 
      ID_Title,
 
      ID_ItTyp,
 
      ID_Dept,
 
      TitleName,
 
      cntForSale,
 
      cntInPrint,
 
      cntInStock,
 
      qtyInStock,
 
      currMinPrice,
 
      currMaxPrice)
 
    SELECT
 
      i.ID_Title,
 
      i.ID_ItTyp,
 
      t.ID_Dept,
 
      t.Name AS TitleName,
 
      i.cntForSale,
 
      i.cntInPrint,
 
      i.cntInStock,
 
      i.qtyInStock,
 
      i.currMinPrice,
 
      i.currMaxPrice
 
    FROM (
 
      SELECT
 
        ID_Title, ID_ItTyp,
 
        SUM(IF(isForSale,1,0)) AS cntForSale,
 
        SUM(IF(isInPrint,1,0)) AS cntInPrint,
 
        SUM(IF(qtyInStock>0,1,0)) AS cntInStock,
 
        SUM(qtyInStock) AS qtyInStock,
 
        MIN(PriceSell) AS currMinPrice,
 
        MAX(PriceSell) AS currMaxPrice
 
      FROM cat_items AS i
 
      GROUP BY ID_Title, ID_ItTyp
 
    ) AS i LEFT JOIN titles AS t ON i.ID_Title=t.ID;</mysql>
 
<section end=sql />
 
* '''2007-09-20'''
 
** Removed "WHERE i.isForSale" because this prevented the availability of discontinued items from being properly zeroed out
 
** Added cntInStock calculation; see table schema
 
==== &larr; _titles ====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_TitleIttyps_fr_Titles()
 
    UPDATE _title_ittyps AS ti LEFT JOIN _titles AS t ON t.ID=ti.ID_Title
 
      SET
 
        ti.CatNum = t.CatNum,
 
        ti.CatWeb = t.CatWeb;</mysql>
 
<section end=sql />
 
==== &larr; cat_ittyps ====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_TitleIttyps_fr_CatIttyps()
 
      UPDATE _title_ittyps AS ti LEFT JOIN cat_ittyps AS it ON ti.ID_ItTyp=it.ID
 
        SET
 
          ti.ItTypNameSng = it.NameSng,
 
          ti.ItTypNamePlr = it.NamePlr,
 
          ti.ItTypSort = it.Sort;</mysql>
 
<section end=sql />
 
 
 
===_dept_ittyps===
 
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_dept_ittyps`;
 
CREATE TABLE `_dept_ittyps` (
 
  `ID_ItTyp` int unsigned NOT NULL,
 
  `ID_Dept` int unsigned NOT NULL,
 
  `cntForSale` int unsigned NOT NULL,
 
  `cntInPrint` int unsigned NOT NULL,
 
  `qtyInStock` int unsigned NOT NULL,
 
  `ItTypNameSng` varchar(64) DEFAULT NULL COMMENT 'cat_ittyps.NameSng',
 
  `ItTypNamePlr` varchar(64) DEFAULT NULL COMMENT 'cat_ittyps.NamePlr; default to ItTypNameSng',
 
  PRIMARY KEY (`ID_ItTyp`,`ID_Dept`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1;</mysql>
 
<section end=sql />
 
====&larr; _title_ittyps====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_DeptIttyps_fr_TitleIttyps()
 
    REPLACE INTO _dept_ittyps(ID_ItTyp,ID_Dept,cntForSale,cntInPrint,qtyInStock)
 
    SELECT
 
      ID_ItTyp,
 
      ID_Dept,
 
      SUM(cntForSale) AS cntForSale,
 
      SUM(cntInPrint) AS cntInPrint,
 
      SUM(qtyInStock) AS qtyInStock
 
    FROM _title_ittyps
 
    GROUP BY ID_ItTyp, ID_Dept;</mysql>
 
<section end=sql />
 
====&larr; cat_ittyps====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_DeptIttyps_fr_CatIttyps()
 
    UPDATE _dept_ittyps AS di LEFT JOIN cat_ittyps AS it ON it.ID=di.ID_ItTyp
 
      SET
 
        di.ItTypNameSng = it.NameSng,
 
        di.ItTypNamePlr = IFNULL(it.NamePlr,it.NameSng);</mysql>
 
<section end=sql />
 
 
 
===_supplier_ittyps===
 
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_supplier_ittyps`;
 
CREATE TABLE `_supplier_ittyps` (
 
  `ID` int(11) unsigned NOT NULL,
 
  `ItemType` varchar(63) NOT NULL,
 
  `ItemCount` int NOT NULL,
 
  `Name` varchar(63) NOT NULL,
 
  `CatKey` varchar(7) NOT NULL,
 
  PRIMARY KEY (`ID`,`ItemType`)
 
) ENGINE=MyISAM DEFAULT CHARSET=latin1;</mysql>
 
<section end=sql />
 
====&larr; suppliers x _titles x _title_ittyps x cat_ittyps ...====
 
<section begin=sql /><mysql>  CREATE PROCEDURE Upd_SupplierIttyps()
 
    BEGIN
 
      DELETE FROM _supplier_ittyps;
 
      INSERT INTO _supplier_ittyps
 
      SELECT
 
        s.ID,
 
        if(Count(ti.ID_Title)=1,it.NameSng,it.NamePlr) AS ItemType,
 
        Count(ti.ID_Title) AS ItemCount,
 
        s.Name, s.CatKey
 
      FROM (
 
        (suppliers AS s LEFT JOIN _titles AS tc ON tc.ID_Supp=s.ID)
 
        LEFT JOIN _title_ittyps AS ti ON ti.ID_title=tc.ID)
 
        LEFT JOIN cat_ittyps AS it ON ti.ID_ItTyp=it.ID
 
      GROUP BY s.ID, s.Name, s.CatKey, it.NameSng, it.NamePlr, ID_Parent
 
      HAVING SUM(tc.cntForSale)
 
      ORDER BY s.Name, SUM(tc.cntForSale) DESC;
 
    END</mysql>
 
<section end=sql />
 
Later we might refine this into a way to view unavailable suppliers as well... but that's very low priority, and it might be better to have a separate table for it anyway (keep "active" operations fast).
 
 
 
===_stk_containers===
 
Aggregate table listing all places where stock can be, for history:
 
<section begin=sql /><mysql> DROP TABLE IF EXISTS `_stk_containers`;
 
 
CREATE TABLE `_stk_containers` (
 
  `IDS` varchar(31) NOT NULL COMMENT "Type + ID_forType: unique ID for this container, across all types",
 
  `Type` char(1) NOT NULL COMMENT "identifies which type of container each record refers to",
 
  `ID_forType` INT NOT NULL COMMENT "record's unique ID for that type",
 
  `Name` varchar(63) NOT NULL COMMENT "auto-generated but hopefully somewhat descriptive and human-readable name for this container",
 
  `When` DATETIME COMMENT "applicable date, as a somewhat more human-readable cue for some of the container types",
 
  PRIMARY KEY(`IDS`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
==== &larr; stk_bins, cat_items... ====
 
SQL to update (replace/fill) _stk_containers (in progress; Packages and Restocks not yet migrated):
 
<section begin=sql /><mysql>CREATE PROCEDURE Upd_StkContainers()
 
  REPLACE INTO _stk_containers
 
  /*
 
  SELECT
 
      CONCAT("P.",p.ID) AS IDS,
 
      "P" AS Type,
 
      p.ID AS ID_forType,
 
      CONCAT(o.Number,"-",p.Seq) AS Name,
 
      s.WhenShipped AS TimeStamp
 
    FROM (Packages AS p LEFT JOIN Orders AS o ON p.ID_Order=o.ID) LEFT JOIN Shipments AS s ON p.ID_Shipment=s.ID;
 
    UNION /**/
 
  SELECT
 
      CONCAT("L.",l.ID) AS IDS,
 
      "L" AS Type,
 
      l.ID AS ID_forType,
 
      l.Code AS Name,
 
      l.WhenCreated AS TimeStamp
 
    FROM stk_bins AS l
 
    UNION
 
  /*
 
  SELECT
 
      CONCAT("R.",r.ID) AS IDS,
 
      "R" AS Type,
 
      r.ID AS ID_forType,
 
      "po"&r.PurchOrdNum&"/ord"&SuppOrdNum&"/inv"&SuppInvcNum AS Name,
 
      RestockEffDate(r.ID) AS TimeStamp
 
    FROM Restocks AS r
 
    UNION /**/
 
  SELECT
 
      CONCAT("M.",i.ID) AS IDS,
 
      "M" AS Type,
 
      i.ID AS ID_forType,
 
      i.CatNum AS Name,
 
      NULL AS TimeStamp
 
    FROM cat_items AS i WHERE i.IsMaster;</mysql>
 
<section end=sql />
 
 
 
==Data Management==
 
These tables are all maintained by hand, but must match what actually happens in the SQL code.
 
===data_tables===
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `data_tables`;
 
 
 
CREATE TABLE `data_tables` (
 
    `ID` INT NOT NULL AUTO_INCREMENT,
 
    `Name` varchar(63) NOT NULL COMMENT "name of table being tracked",
 
    `WhenUpdated` DATETIME DEFAULT NULL COMMENT "when the table's data was last modified",
 
    `Notes` varchar(255) DEFAULT NULL COMMENT "descriptive notes",
 
  PRIMARY KEY(`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
===data_procs===
 
List of stored procedures which update one table from others
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `data_procs`;
 
 
 
CREATE TABLE `data_procs` (
 
    `ID` INT NOT NULL AUTO_INCREMENT COMMENT "procedure ID",
 
    `Name` varchar(127) NOT NULL COMMENT "name of SQL stored procedure",
 
    `isActive` BOOL COMMENT "FALSE = skip this record",
 
    `doesClear` BOOL COMMENT "TRUE = clears all data from table before starting",
 
    `Notes` varchar(255) DEFAULT NULL COMMENT "explanatory notes",
 
    PRIMARY KEY (`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
===data_flow===
 
Keeps track of data dependencies between tables
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `data_flow`;
 
 
 
CREATE TABLE `data_flow` (
 
    ID_Srce INT NOT NULL COMMENT "data_tables.ID of SOURCE table",
 
    ID_Dest INT NOT NULL COMMENT "data_tables.ID of DESTINATION table",
 
    ID_Proc INT NOT NULL COMMENT "data_procs.ID of stored procedure which calculates Dest data from Srce data",
 
    Notes varchar(255) COMMENT "loose explanatory or descriptive notes",
 
    PRIMARY KEY (`ID_Srce`,`ID_Dest`,`ID_Proc`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
===data_log===
 
Log of all data updates performed internally
 
<section begin=sql /><mysql>DROP TABLE IF EXISTS `data_log`;
 
 
 
CREATE TABLE `data_log` (
 
    ID INT NOT NULL AUTO_INCREMENT COMMENT "log line identifier",
 
    WhenStarted DATETIME NOT NULL COMMENT "when this event was started",
 
    WhenFinished DATETIME COMMENT "when it was completed",
 
    ID_TableDest INT NOT NULL COMMENT "data_tables.ID of table being updated",
 
    ID_TableSrce INT NOT NULL COMMENT "data_tables.ID of triggering/source table",
 
    ID_Proc INT NOT NULL COMMENT "data_procs.ID of stored procedure used",
 
    Caller varchar(63) COMMENT "identifying string from code which caused the update",
 
    Notes varchar(255) COMMENT "historical human-created notes",
 
    PRIMARY KEY (`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
 
==Miscellaneous==
 
==Miscellaneous==
===error/message log===
+
* [[/stats]]
This is a log specifically of administration activity (shipping, catalog updates, etc.) so should probably be renamed admin_log or similar. There will be a separate log for shopping activity.
+
* [[/event_log]]
<section begin=sql /><mysql>DROP TABLE IF EXISTS `event_log`;
+
* [[/discarded]] - discarded tables
 
 
CREATE TABLE `event_log` (
 
    ID INT NOT NULL AUTO_INCREMENT COMMENT "log line identifier",
 
    EvWhen DATETIME NOT NULL COMMENT "when the event started",
 
    EvWhere varchar(255) COMMENT "where in the code the event happened (suitable for filtering)",
 
    Params varchar(255) COMMENT "any relevant parameters",
 
    Descr varchar(255) COMMENT "description of error",
 
    Code INT DEFAULT NULL COMMENT "numeric error code unique to location (EvWhere)",
 
    VbzUser varchar(127) COMMENT "VbzCart username, for when we have a user system",
 
    SysUser varchar(127) COMMENT "who logged into the operating system",
 
    Machine varchar(64) COMMENT "network name of machine from which the event was initiated, if applicable",
 
    isError BOOL COMMENT "FALSE = this is just a message or normal event; TRUE = there is a problem to fix",
 
    isSevere BOOL COMMENT "TRUE = important enough to send email to admin immediately",
 
    Notes varchar(255) DEFAULT NULL COMMENT "manually-entered notes",
 
    PRIMARY KEY (`ID`)
 
) ENGINE = MYISAM;</mysql>
 
<section end=sql />
 
* Have to use "Ev" prefix because When and Where are keywords.
 
* Decided not to have "WhenStarted" and "WhenFinished" because:
 
** There are relatively few events that "complete"
 
** This leaves no place to record completion status (e.g. how many records were affected)
 
** It makes the code more complicated and less elegant
 
 
 
==Future Changes==
 
''This is probably obsolete, and may have been partially implemented. Review later.''
 
 
 
Shipping code should be tied to the item Option (i.e. no ID_ShipCode field in cat_items), which would include size and other variants (i.e. cat_itypes and cat_iopts will be combined). Each Supplier should have its own list of item types, to allow for different variation (e.g. LB tie-dye shirts of a given size weigh more than ZR shirts of the same size because they use thicker cloth). I'm using the schema as it is because converting the existing data will be a complicated task and we need to get this thing working ASAP, but I picture something like this in the near future:
 
*'''cat_itypes''' - catalog item types
 
**'''ID'''#@ - int(4)
 
**'''Name''' - text
 
**'''ID_Parent''' - int(4) -- cat_itypes.ID of parent type, if any
 
*'''cat_ioptns''' - catalog item options
 
**'''ID'''#@ - int(4)
 
**'''Key''' - string(8) -- Supplier.CatKey-Title.CatKey[-Key] = complete catalog number (Key can be blank)
 
**'''Descr''' - string(64) -- description of this option (e.g. "eXtra Large")
 
*'''cat_itype_ioptns''' - options available for each item type, per supplier
 
**'''ID''' - int(4) -- unique identifier
 
**'''ID_Supplier'''# - int(4)
 
**'''ID_IType'''# - int(4)
 
**'''ID_IOptn'''# - int(4)
 
**'''ID_ShipCode''' - int(4) -- shipping cost type for this combination
 
**'''Descr''' - string(128) -- description of this option/type combination (e.g. "eXtra Large shortsleeve t-shirt")
 
We would then remove the ID_IType and ID_ShipType fields from cat_items; ID_IOpt would point to cat_itype_ioptns instead of cat_ioptns... or we could rename it ID_ITypeOptn, but that's... a bit ugly. I'll have to talk myself into it. Also, I think I used a scheme something like this in an earlier revision of the MS Access version of VbzCart and ended up changing it to what it is now; that may have been for a good reason, or it may have been because it seemed too unwieldy... I think I was generating a cat_itype_ioptns entry for ''every possible combination'' of IType and IOptn, and that doesn't make sense. You just need a tool to help manage them, and to copy existing sets for tweaking. It also didn't have an ID_Supplier key.
 

Revision as of 21:22, 8 March 2009

Navigation

{{#lst:VbzCart|navbar}}: tables

Subpages

Main Data

Catalog

These tables describe and price the items displayed in the catalog pages and in shopping carts.

A "Title" is a group of items with a common description, e.g. different sizes or styles of a shirt, different media (CD, cassette) for an audio recording.

In the previous version of the cart software, we had to have items of somewhat different appearance (e.g. longsleeve and shortsleeve shirts) sharing a single title, so as to remove the necessity to always have pictures for each. In this version, a title can point to another title for its picture, thus keeping it clear whether the picture is truly representative or just an approximation.

  • /cat_supp: catalog suppliers (i.e. manufacturers, wholesalers)
  • /cat_depts: catalog departments within a supplier
  • /cat_titles: titles within a department - a particular "thing" which may be available in multiple varieties
  • /cat_items: items within a title - a particular version of a title
  • /cat_ittyps: item types - every item has one, but they are often all the same
  • /cat_ioptns: these typically distinguish items within a title
  • /cat_images: image data; so far, we only keep track of images by title
  • /cat_ship_costs: shipping costs for different items
  • /cat_pages: mapping URLs to various catalog entities (suppliers, depts, titles)
  • /cat_pages_old: catalog designations sometimes change; where possible, this lets us redirect old URLs

future

It may be that Departments and Suppliers should be handled by the Topics tree, but this involves creating some infrastructure which is going to take some doing. (Specifically, we need to be able to assign named values – like "catkey" – to any topic, so that each Department topic and Supplier topic can refer to the proper catalog number.)

Catalog Entry

These are tables used to make it easier to update the catalog. The key concept here is "catalog title groups" (CTGs). A CTG is a set of titles which are all available with the same set of options (e.g. sizes) at the same prices (e.g. $10 S, $11 M-XL, $12 2XL). The final vbz catalog (cat_items) is the result of a sort of vector multiplication of Titles (cat_titles) and CTGs plus any items in stock.

A "source" is usually a printed catalog from a supplier, though it can also be the manufacturer's web site on a particular date.

See VbzCart catalog building for queries and processes.

Ordering

The items a customer wants to order are saved in a cart. The cart also saves session information, e.g. customer's IP/domain, but not the customer's shipping or payment data. Later on, we'll allow customers to make changes to carts after the cart has already been assigned to an order, so will need session info stored separately, which is why it's in a separate table. See VbzCart ordering for details.

There is a handful of tables just for managing customer contact information, so I'm putting those on a separate page: VbzCart customer tables

Restocking

obsolete tables

Shopping

These tables store data generated by the user during the shopping process.

Stock Management

Browsing

Topics and images

Calculated Tables

These are used for caching data which takes a long time (more than 0.3 seconds or so) to calculate and which depends on things which a lot less often than pages are viewed.

Data Flow Management

These tables are all maintained by hand (except for timestamps and data_log), but must match the actual dependencies in the SQL code.

Miscellaneous