New config:  houseCoupon UPC prefix is configurable via the Scanning page in /install
InstallUtilities::dbStructureModify accepts an array of queries
InstallUtilities::duplicateStructure returns false on error
Table creation for localtrans_today use the above to add
indexes on the trans_no and datetime columns
Normalize primary key columns on entire transaction when using
the UNDO command
Tender module for cashing patronage rebate checks via POS
New table equity_history_sum and new view equity_live_balance.
These supplement (and will eventually replace) stockSum_purch
and newBalanceStockToday_test respectively. Naming convention is
similar to AR. Summarizing past transactions as a table rather than
via a view adds a noticeable performance boost.
BUGFIX: syntax error
BUGFIX: field_name and fetchField are not synonyms
Converted EquityAll, HourlyCustomers, and HourlySales to use
reporting class. Extended reporting class to allow form & results
displayed simultaneously as well as styled but unsorted results.
HourlySalesReport should be able to replace both existing
reports rather than having separate ones for department & superdept
SCALE command pulls current weight into input box
Report with a chart!
Another library on top of d3.js might be nice. It's powerful
but complex.
Reports refactored for classes / consistency:
* Hourly Transactions
* Movement w/ margin
* Movement by Price
* New Items
* Open Rings
* Patronage Rebate
* Product Recall
I also update Hourly Sales so that it automatically links
to Hourly Transactions for the same dates & departments
(and vice versa). I updated the default menus to use the
newer hourly reports.
BUGFIX: Insert query column misaligned when creating shelf tags
by department or manufacturer
Class-based Report Refactors:
* RecentSales
* SalePerformance
* StockSummary
* SuspensionHistory
* TenderInOut
* Trends

I extended the ProductMovement report to search non-numeric
UPCs differently. I use this for tracking POS logging records.

SQLManager has a new week() method for getting the week #.

SalesToday is NOT an instance of FannieReportPage although it is
class based. There's not that much data and it's only live so
export makes no sense. I added a line graph using d3.js.
BUGFIX: query response variable was incorrect
Single line graph separated out as standalone js/css. Hourly sales
and transactions use the same charting. FannieReportPage tags columns
with a class attribute to make them JS-accessible.
BasicModel::load() and BasicModel::save() cope with missing columns
BasicModel::find() also needs to avoid missing columns
Hook function for Model classes to register a callback that is executed
after a column is successfully added
New BasicModel functionality to normalize lane tables in conjunection
with server tables.
Safely Renaming a Column in custdata, pt 1

======================================
STEP ONE: Define a New Column
======================================
Edit CustdataModel.php and define a ChargeLimit column.
Re-run BasicModel on it to update accessors.

php BasicModel.php CustdataModel.php

Add a hook function Custdata::hookAddColumnChargeLimit
to copy the current values in custdata.MemDiscountLimit
to the new column. The view memChargeBalance also
needs to be rebuilt using the new column.

Edit install/sql/op/custdata.php so new installs will
have the column without needing an update. Ditto
for install/sql/trans/memChargeBalance.php.

Since the lane table needs to match, set
$normalize_lanes to true for CustdataModel. The hook
function needs a different definition for the lane-side
memchargebalance view. The install scripts should
also be updated on the lane side.

Now it's time to run the update. Manually creating a
backup of custdata would not be a bad idea.

php BasicModel.php --update <database name> CustdataModel.php

======================================
STEP TWO: Code Revisions
======================================
First pass goal is to use both columns. Any place
that updates custdata.MemDiscountLimit should
also update custdata.ChargeLimit. Places that
use the value of custdata.MemDiscountLimit should
use custdata.ChargeLimit instead.

None of this code should break if the new column
is not yet present. Models simplify some of that but
there are still some sections of defensive coding.