Commit 125a82ce authored by Malcolm Blaney's avatar Malcolm Blaney

Added "purchase-pre-order" substitution so that copying orders for

next week is not tied to time invoices are sent out (though this
can still happen too). Fixed some stock tracking bugs when adding
purchases, also wasn't checking the quantity of composite items
being added to purchases. Removing purchases can now be configured
to only remove purchases where quota hasn't been met. Stock
adjustments are now stored as the delta rather than the new total
so that reconcilliation can be done more easily.
parent 37d91521
......@@ -290,14 +290,22 @@ class Banking extends Base {
return array($user_list, $buyer_groups);
}
public function AllSettings() {
public function AllSettings($organisation = true) {
$settings = array();
$organiser = new Organiser($this->user, $this->owner);
$query = "";
if ($organisation) {
$organiser = new Organiser($this->user, $this->owner);
$query = 'SELECT banking.user, reference, name, number, bsb, credit, '.
'surcharge, next_week, buyer_group FROM banking LEFT JOIN users ON '.
'banking.user = users.user WHERE '.$organiser->GroupQuery();
}
else {
$query = 'SELECT banking.user, reference, name, number, bsb, credit, '.
'surcharge, next_week, buyer_group FROM banking LEFT JOIN users ON '.
'banking.user = users.user WHERE '.
'users.system_group = "'.$this->user->group.'"';
}
$mysqli = connect_db();
$query = 'SELECT banking.user, reference, name, number, bsb, credit, '.
'surcharge, buyer_group FROM banking LEFT JOIN users ON '.
'banking.user = users.user WHERE '.$organiser->GroupQuery();
if ($result = $mysqli->query($query)) {
while ($banking = $result->fetch_assoc()) {
$settings[$banking["user"]] =
......@@ -307,6 +315,7 @@ class Banking extends Base {
"bsb" => $banking["bsb"],
"credit" => (int)$banking["credit"],
"surcharge" => (int)$banking["surcharge"],
"nextWeek" => $banking["next_week"],
"buyerGroup" => $banking["buyer_group"]);
}
$result->close();
......
......@@ -98,9 +98,18 @@ class Contact extends Base {
else {
$sender_name .= " <".$sender.">";
}
$cc = $this->Substitute("system-sender-cc");
$bcc = $this->Substitute("system-sender-bcc");
$headers = "MIME-Version: 1.0\r\n".
"Content-type: text/html; charset=iso-8859-1\r\n".
"From: ".$sender_name."\r\n";
if ($cc != "") {
$headers .= "Cc: ".$cc."\r\n";
}
if ($bcc != "") {
$headers .= "Bcc: ".$bcc."\r\n";
}
if ($reply_to != "") {
$headers .= "Reply-To: ".$reply_to."\r\n";
}
......
......@@ -409,92 +409,6 @@ class Invoice extends Base {
public function Update() {
// This is called when the version of the module is updated,
// to provide a way to update or modify tables etc..
$mysqli = connect_db();
$query = 'ALTER TABLE invoice DROP invoice_text';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 1: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-group-cc" WHERE '.
'label = "invoice-stock-cc"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 2: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-group-bcc" WHERE '.
'label = "invoice-stock-bcc"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 3: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-group-email" WHERE '.
'label = "invoice-stock-email"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 4: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-group-subject" WHERE '.
'label = "invoice-stock-subject"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 5: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-intro-no-orders" WHERE '.
'label = "invoice-stock-no-orders"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 6: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-intro-orders" WHERE '.
'label = "invoice-stock-orders"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 7: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-group-format" WHERE '.
'label = "invoice-group-stock-format"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 8: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-sender" '.
'WHERE label = "organisation-sender"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 9: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-sender-name" '.
'WHERE label = "organisation-sender-name"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 10: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-email" WHERE '.
'label = "organisation-stock-email"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 11: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-subject" '.
'WHERE label = "organisation-stock-subject"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 12: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-cc" '.
'WHERE label = "organisation-stock-cc"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 13: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-bcc" '.
'WHERE label = "organisation-stock-bcc"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 14: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-organisation-format" '.
'WHERE label = "organisation-stock-format"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 15: ".$mysqli->error);
}
$query = 'UPDATE template SET label = "invoice-extra-column" WHERE '.
'label = "organisation-stock-extra-column"';
if (!$mysqli->query($query)) {
$this->Log("Invoice->Update 16: ".$mysqli->error);
}
$mysqli->close();
// Besides all the name changes above there's also a new template for
// individual order subject lines.
$template = array('"invoice-order-subject","","Order for next week"');
$this->AddTemplate($template);
}
public function UpdateScript($path) {
......
......@@ -166,7 +166,7 @@ class Organiser extends Base {
$us_message));
$sender = $this->Substitute("organiser-sender", "/!host/",
$this->user->config->ServerName());
$sender_name = $this->Substitute("system-sender-name");
$sender_name = $this->Substitute("organiser-sender-name");
if ($sender_name == "") {
$sender_name = $sender;
}
......
......@@ -276,7 +276,24 @@ class Purchase extends Base {
}
public function Cron() {
if (!$this->Run(date("H:00:00"))) return;
$run_groups = $this->RunGroups("purchase-pre-order");
for ($i = 0; $i < count($run_groups); $i++) {
$this->user = new User($this->owner, $run_groups[$i]);
if ($this->Substitute("pre-order") == "true") {
$banking = new Banking($this->user, $this->owner);
foreach ($banking->AllSettings(false) as $user => $settings) {
$timestamp = $settings["nextWeek"];
// Make sure the value for next week is at least tomorrow,
// but only up to a week away.
if ($timestamp > strtotime("24 hours") &&
$timestamp < strtotime("8 days")) {
$this->PreOrder($user, $timestamp);
}
}
}
}
}
public function Factory($fn, $p = NULL) {
......@@ -467,6 +484,7 @@ class Purchase extends Base {
if ($end != 0) {
$timestamp_query .= ' AND timestamp <= '.$end;
}
$quantity_query = "";
if ($hide_removed) {
$quantity_query = 'AND quantity != 0 ';
}
......@@ -956,6 +974,11 @@ class Purchase extends Base {
$purchase = array();
$mysqli = connect_db();
if ($composite) {
// The user could be purchasing more than one composite item.
$composite_quantity = $quantity;
// Also the price given is for one composite item, convert this to the
// total price.
$price *= $composite_quantity;
// The purchase_composite table only provides quantites of each product,
// need to check stock availability for current prices and supplier.
$values = "";
......@@ -969,7 +992,8 @@ class Purchase extends Base {
if ($result = $mysqli->query($query)) {
while ($purchase_composite = $result->fetch_assoc()) {
$name = $purchase_composite["name"];
$quantity = (float)$purchase_composite["quantity"];
$quantity = $composite_quantity *
(float)$purchase_composite["quantity"];
if (isset($available[$name])) {
$item_supplier = $available[$name]["user"];
$item_price = $available[$name][$buyer_group];
......@@ -995,17 +1019,42 @@ class Purchase extends Base {
$this->Log("Purchase->AddPurchase 1: ".$mysqli->error);
}
if ($values !== "") {
$purchases_exist = false;
// Check that purchases don't already exist for this user and timestamp
// otherwise there will be duplicate key and stock tracking issues.
$query = 'SELECT name FROM purchase WHERE user = "'.$user.'" AND '.
'timestamp = '.$timestamp;
if ($result = $mysqli->query($query)) {
if ($result->num_rows > 0) {
$purchases_exist = true;
}
$result->close();
}
else {
$this->Log("Purchase->AddPurchase 2: ".$mysqli->error);
}
if ($purchases_exist) {
$mysqli->close();
return array("error" => "Purchases already exist for this user ".
"and date.");
}
// Add the discount item to this set of purchases, so that the total
// price is the same as the composite item's price.
if ($composite_total > $price) {
$name = $product." discount";
$item_price = number_format($price - $composite_total, 2, ".", "");
$values .= ', ("'.$user.'", '.$timestamp.', "'.$product.' discount",'.
$values .= ', ("'.$user.'", '.$timestamp.', "'.$name.'", '.
'"'.$supplier.'", 1, '.$item_price.', '.$item_price.', '.
'"'.$volunteer.'")';
$purchase[] = array("date" => $timestamp * 1000, "user" => $user,
"name" => $product." discount",
"supplier" => $supplier, "quantity" => 1,
"price" => $item_price, "total" => $item_price);
"name" => $name, "supplier" => $supplier,
"quantity" => 1, "price" => $item_price,
"total" => $item_price);
// And add the discount item to stock tracking.
if (!isset($decrease[$supplier])) {
$decrease[$supplier] = array();
}
$decrease[$supplier][$name] = 1;
}
$query = 'INSERT INTO purchase VALUES '.$values;
if (!$mysqli->query($query)) {
......@@ -1015,13 +1064,26 @@ class Purchase extends Base {
}
}
else {
// If updating the quantity of an existing purchase, find the difference
// to decrease the stock by.
$query = 'SELECT quantity FROM purchase WHERE user = "'.$user.'" AND '.
'name = "'.$product.'" AND timestamp = '.$timestamp;
if ($result = $mysqli->query($query)) {
if ($purchase = $result->fetch_assoc()) {
$quantity -= (float)$purchase["quantity"];
}
$result->close();
}
else {
$this->Log("Purchase->AddPurchase 3: ".$mysqli->error);
}
$query = 'INSERT INTO purchase VALUES ("'.$user.'", '.$timestamp.', '.
'"'.$product.'", "'.$supplier.'", '.$quantity.', '.$price.', '.
$base_price.', "'.$volunteer.'") ON DUPLICATE KEY UPDATE '.
'supplier = "'.$supplier.'", quantity = '.$quantity.', '.
'price = '.$price.', base_price = '.$base_price;
if (!$mysqli->query($query)) {
$this->Log("Purchase->AddPurchase 3: ".$mysqli->error);
$this->Log("Purchase->AddPurchase 4: ".$mysqli->error);
}
if ($product != "surcharge") {
$stock->Decrease(array($supplier => array($product => $quantity)));
......@@ -1088,34 +1150,51 @@ class Purchase extends Base {
$this->Log("Purchase->RemoveAllPurchases 1: next co-op-day not set.");
return;
}
$start = $next_co_op - 86400;
$end = $next_co_op + 86400;
// Check if only removing purchases where the quota wasn't met.
$check_quota = $this->Substitute("purchase-check-quota") === "true";
$stock = new Stock($this->user, $this->owner);
$mysqli = connect_db();
$products = array();
$name_query = "";
$start = $next_co_op - 86400;
$end = $next_co_op + 86400;
$query = 'SELECT name, supplier, SUM(quantity) AS quantity FROM purchase '.
'LEFT JOIN users ON purchase.user = users.user WHERE '.
'users.system_group = "'.$this->user->group.'" AND timestamp > '.$start.
' AND timestamp < '.$end.' GROUP BY name, supplier';
if ($result = $mysqli->query($query)) {
while ($purchase = $result->fetch_assoc()) {
$name = $purchase["name"];
$supplier = $purchase["supplier"];
if (!isset($products[$supplier])) {
$products[$supplier] = array();
$quantity = (float)$purchase["quantity"];
if ($check_quota && !$stock->Quota($name, $supplier, $quantity)) {
if (!isset($products[$supplier])) {
$products[$supplier] = array();
}
$products[$supplier][$name] = $quantity;
// Add to query for delete below.
if ($name_query !== "") {
$name_query .= ' OR ';
}
$name_query .= 'name = "'.$name.'"';
}
$products[$supplier][$purchase["name"]] = $purchase["quantity"];
}
$result->close();
}
else {
$this->Log("Purchase->RemoveAllPurchases 2: ".$mysqli->error);
}
$stock = new Stock($this->user, $this->owner);
$stock->Increase($products);
if ($name_query !== "") {
$name_query = '('.$name_query.') AND ';
}
$query = 'DELETE purchase FROM purchase LEFT JOIN users ON '.
'purchase.user = users.user WHERE users.system_group = '.
'"'.$this->user->group.'" AND timestamp > '.$start.' AND '.
'timestamp < '.$end;
'purchase.user = users.user WHERE '.$name_query.
'users.system_group = "'.$this->user->group.'" AND '.
'timestamp > '.$start.' AND timestamp < '.$end;
if (!$mysqli->query($query)) {
$this->Log("Purchase->RemoveAllPurchases 3: ".$mysqli->error);
}
......@@ -1913,13 +1992,12 @@ class Purchase extends Base {
if ($new_date > $old_date && $new_date - $old_date < 86400) {
// Find the quantity for this purchase to increase stock.
// Need to look at a timestamp range due to rounding by 1ms.
$query = 'SELECT supplier, quantity, timestamp FROM purchase '.
'WHERE user = "'.$user.'" AND name = "'.$name.'" AND '.
$query = 'SELECT quantity, timestamp FROM purchase WHERE '.
'user = "'.$user.'" AND name = "'.$name.'" AND '.
'timestamp <= '.($old_date + 1).' AND '.
'timestamp >= '.($old_date - 1);
if ($result = $mysqli->query($query)) {
if ($purchase = $result->fetch_assoc()) {
$supplier = $purchase["supplier"];
$quantity = $purchase["quantity"];
$old_date = $purchase["timestamp"];
$stock->Increase(array($supplier => array($name => $quantity)));
......@@ -1935,10 +2013,20 @@ class Purchase extends Base {
$this->Log("Purchase->UpdateData 2: ".$mysqli->error);
}
}
else if ($new_date == $old_date) {
// If the timestamps are the same, quantity is only being adjusted
// and will be updated on duplicate key below.
$quantity -= $current[$name]["quantity"];
else {
// Check if there's an existing purchase with this timestamp and
// adjust quantity to be deducted from stock levels.
$query = 'SELECT quantity FROM purchase WHERE user = "'.$user.'" '.
'AND name = "'.$name.'" AND timestamp = '.$new_date;
if ($result = $mysqli->query($query)) {
if ($purchase = $result->fetch_assoc()) {
$quantity -= (float)$purchase["quantity"];
}
$result->close();
}
else {
$this->Log("Purchase->UpdateData 3: ".$mysqli->error);
}
}
// Add quantity to products array to deduct from stock.
if (!isset($products[$supplier])) {
......@@ -1958,6 +2046,19 @@ class Purchase extends Base {
$values .= '("'.$user.'", '.$new_date.', "'.$name.'", '.
'"'.$supplier.'", '.$quantity.', '.$price.', '.$base_price.', '.
'"'.$this->user->name.'")';
// Check if there's an existing purchase with this timestamp and
// adjust quantity to be deducted from stock levels.
$query = 'SELECT quantity FROM purchase WHERE user = "'.$user.'" '.
'AND name = "'.$name.'" AND timestamp = '.$new_date;
if ($result = $mysqli->query($query)) {
if ($purchase = $result->fetch_assoc()) {
$quantity -= (float)$purchase["quantity"];
}
$result->close();
}
else {
$this->Log("Purchase->UpdateData 4: ".$mysqli->error);
}
// Add quantity to products array to deduct from stock.
if (!isset($products[$supplier])) {
$products[$supplier] = array();
......@@ -1976,7 +2077,7 @@ class Purchase extends Base {
$query = 'INSERT INTO purchase VALUES '.$values.
' ON DUPLICATE KEY UPDATE quantity = VALUES(quantity)';
if (!$mysqli->query($query)) {
$this->Log("Purchase->UpdateData 3: ".$mysqli->error);
$this->Log("Purchase->UpdateData 5: ".$mysqli->error);
}
$stock->Decrease($products);
}
......
......@@ -182,6 +182,7 @@ class Reader extends Base {
'"",".reader-group","background-color","#eeeeee"',
'"",".reader-group","border","1px solid #aaaaaa"',
'"",".reader-group","border-radius","2px"',
'"",".reader-item .description","overflow","auto"',
'"",".reader a","color","#666666"',
'"",".reader a:hover","color","#999999"',
'"",".reader","font-family","Verdana,Arial"');
......
......@@ -254,16 +254,14 @@ class Stock extends Base {
$time = "11pm";
if (!$this->Run($time)) return;
$organiser = new Organiser($this->user, $this->owner);
// order availability is imported, and then used to overwrite purchase
// availabilty on the day specified by "stock-order-update".
// Order availability is imported by the user and then used here to
// overwrite purchase availabilty.
$run_groups = $this->RunGroups("stock-order-update", $time);
$mysqli = connect_db();
for ($i = 0; $i < count($run_groups); $i++) {
$group = $run_groups[$i];
$this->user = new User($this->owner, $group);
$query = 'UPDATE stock LEFT JOIN users ON stock.user = users.user SET '.
'purchase_available = order_available WHERE '.$organiser->GroupQuery();
'purchase_available = order_available WHERE '.
'users.system_group = "'.$run_groups[$i].'"';
if (!$mysqli->query($query)) {
$this->Log("Stock->Cron: ".$mysqli->error);
}
......@@ -427,13 +425,6 @@ class Stock extends Base {
public function Update() {
// This is called when the version of the module is updated,
// to provide a way to update or modify tables etc..
$mysqli = connect_db();
$query = 'ALTER TABLE stock MODIFY unit '.
'ENUM("each","kg","L","variable", "g") NOT NULL';
if (!$mysqli->query($query)) {
$this->Log('Stock->Update: '.$mysqli->error);
}
$mysqli->close();
}
public function UpdateScript($path) {
......@@ -516,7 +507,7 @@ class Stock extends Base {
$query = 'SELECT name, stock.user, unit, pack_size, base_price, '.
'wholesale_price, retail_price, category, grower FROM stock LEFT JOIN '.
'users ON stock.user = users.user LEFT JOIN stock_supply_group ON '.
'stock_supply_group.user = stock.user WHERE '.
'stock_supply_group.user = stock.user WHERE composite = 0 AND '.
'('.$organiser->GroupQuery().') AND '.
'(stock_supply_group.system_group = "'.$this->user->group.'" OR '.
'stock_supply_group.system_group = "") '.$available_query.
......@@ -701,6 +692,24 @@ class Stock extends Base {
return $supplier;
}
public function Quota($name, $user, $quantity) {
$quota = false;
$mysqli = connect_db();
$query = 'SELECT pack_size FROM stock WHERE name = "'.$name.'" AND '.
'user = "'.$user.'"';
if ($result = $mysqli->query($query)) {
if ($stock = $result->fetch_assoc()) {
$quota = $quantity >= (float)$stock["pack_size"];
}
$result->close();
}
else {
$this->Log("Stock->Quota: ".$mysqli->error);
}
$mysqli->close();
return $quota;
}
// Private functions below here ////////////////////////////////////////////
private function AllData() {
......@@ -1225,22 +1234,36 @@ class Stock extends Base {
$mysqli = connect_db();
$name = $mysqli->escape_string($_POST["name"]);
$user = $mysqli->escape_string($_POST["supplier"]);
$total = $mysqli->escape_string($_POST["total"]);
$total = (float)$mysqli->escape_string($_POST["total"]);
$description = $mysqli->escape_string($_POST["description"]);
$organiser = new Organiser($this->user, $this->owner);
if (!$organiser->MatchUser($user)) {
$mysqli->close();
return array("error" => "Supplier not found");
}
$adjustment = 0;
// Look up the original stock quantity to calculate the difference.
$query = 'SELECT quantity FROM stock WHERE name = "'.$name.'" '.
'AND user = "'.$user.'"';
if ($result = $mysqli->query($query)) {
if ($stock = $result->fetch_assoc()) {
$adjustment = $total - (float)$stock["quantity"];
}
$result->close();
}
else {
$this->Log("Stock->SaveAdjustment 1: ".$mysqli->error);
}
$query = 'UPDATE stock SET quantity = '.$total.' WHERE name = "'.$name.'" '.
'AND user = "'.$user.'"';
if (!$mysqli->query($query)) {
$this->Log("Stock->SaveAdjustment 1: ".$mysqli->error);
$this->Log("Stock->SaveAdjustment 2: ".$mysqli->error);
}
$query = 'INSERT INTO stock_adjustment VALUES ("'.$name.'", "'.$user.'", '.
$total.', "'.$description.'", "'.$this->user->name.'", '.time().')';
$adjustment.', "'.$description.'", "'.$this->user->name.'", '.time().')';
if (!$mysqli->query($query)) {
$this->Log("Stock->SaveAdjustment 2: ".$mysqli->error);
$this->Log("Stock->SaveAdjustment 3: ".$mysqli->error);
}
$mysqli->close();
return array("done" => true);
......
......@@ -118,8 +118,7 @@ class Summary extends Base {
$outstanding_text = 'You are currently in credit by: $'.
number_format($outstanding*-1, 2, ".", "");
}
$pre_order_available = $this->Substitute("pre-order") === "true" ?
true : false;
$pre_order_available = $this->Substitute("pre-order") === "true";
$pre_order_text = $this->Substitute("summary-pre-order", "/!final/",
$this->Substitute("pre-order-final"));
$purchase_page = $this->Substitute("purchase-page");
......
......@@ -26,7 +26,7 @@
if(!this.dobrado.manager){dobrado.manager={};}
(function(){'use strict';var manager=null;var purchase=[];var managerGrid=null;var managerGridId="";var allProductsGrid=null;var allProductsGridId="";var allProductsData=[];var currentProduct=null;var currentDate=null;var oneDay=86400000;var confirmRemoveMultiple=false;var currentRemove=0;var composite=false;$(function(){if($(".manager").length===0){return;}
$("#manager-details-form").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Edit User Details",width:400});$("#manager-details-form .submit").button().click(editDetails);$(".manager-view-all-dialog").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Available Products",width:830,height:520,close:function(){if(allProductsGrid){allProductsGrid.gotoCell(0,0);}}});$(".manager .view-all").click(viewAll);$("#manager-form .edit").button({disabled:true}).click(openDialog);$("#manager-form .submit").button().click(submit);$("#manager-form .search").button().click(search);$("#manager-form .remove").button().click(remove);$("#manager-username-input").val("").change(function(){setTimeout(function(){showUser();},10);});$("#manager-username-input").keypress(checkUsernameEnter);$("#manager-product-input").val("").change(function(){setTimeout(function(){showProductFromMenu();},10);});$("#manager-supplier-input").val("").change(function(){setTimeout(function(){showSupplierFromMenu();},10);});$("#manager-product-input").val("");$("#manager-price-input").val("");$("#manager-date-input").val(dobrado.formatDate()).change(clearSearchDates).datepicker({dateFormat:dobrado.dateFormat});$("#manager-start-date-input").val("").change(clearDate).datepicker({dateFormat:dobrado.dateFormat});$("#manager-end-date-input").val("").change(clearDate).datepicker({dateFormat:dobrado.dateFormat});$("#manager-quantity-input").val("");$("#manager-quantity-input").spinner({min:0,spin:setQuantity,change:setQuantity});$(".manager .toggle-search-options").click(function(){$(".manager .search-options").toggle();});$(".grid").each(function(index){if(index===0){managerGridId="#"+$(this).attr("id");}
$("#manager-details-form").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Edit User Details",width:400});$("#manager-details-form .submit").button().click(editDetails);$(".manager-view-all-dialog").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Available Products",width:830,height:520,close:function(){if(allProductsGrid){allProductsGrid.gotoCell(0,0);}}});$(".manager .view-all").click(viewAll);$("#manager-form .edit").button({disabled:true}).click(openDialog);$("#manager-form .submit").button().click(submit);$("#manager-form .search").button().click(search);$("#manager-form .remove").button().click(remove);$("#manager-username-input").val("").change(function(){setTimeout(function(){showUser();},10);});$("#manager-username-input").keypress(checkUsernameEnter);$("#manager-product-input").val("").change(function(){setTimeout(function(){showProductFromMenu();},10);});$("#manager-supplier-input").val("").change(function(){setTimeout(function(){showSupplierFromMenu();},10);});$("#manager-product-input").val("");$("#manager-price-input").val("");$("#manager-date-input").val(dobrado.formatDate()).change(clearSearchDates).datepicker({dateFormat:dobrado.dateFormat});$("#manager-start-date-input").val("").change(clearDate).datepicker({dateFormat:dobrado.dateFormat});$("#manager-end-date-input").val("").change(clearDate).datepicker({dateFormat:dobrado.dateFormat});$("#manager-quantity-input").val("");$("#manager-quantity-input").spinner({min:0,spin:setQuantity,change:setQuantity});$(".manager .toggle-search-options").click(function(){clearSearchDates();clearDate();$(".manager .search-options").toggle();});$(".grid").each(function(index){if(index===0){managerGridId="#"+$(this).attr("id");}
if(index===1){allProductsGridId="#"+$(this).attr("id");}});if($(".grid").length!==0){$(".grid").hide();var columns=[{id:"date",name:"Date",field:"date",width:110,sortable:true,formatter:Slick.Formatters.Timestamp},{id:"user",name:"Username",field:"user",width:100,sortable:true},{id:"name",name:"Product",field:"name",width:180,sortable:true},{id:"supplier",name:"Supplier",field:"supplier",width:100,sortable:true},{id:"quantity",name:"Quantity",field:"quantity",width:90,sortable:true},{id:"price",name:"Price",field:"price",width:80,sortable:true,formatter:Slick.Formatters.Dollar},{id:"total",name:"Total",field:"total",width:80,sortable:true,formatter:Slick.Formatters.Dollar}];var options={enableColumnReorder:false,forceFitColumns:true};managerGrid=dobrado.grid.instance(managerGridId,[],columns,options);managerGrid.setSelectionModel(new Slick.RowSelectionModel());managerGrid.onClick.subscribe(function(e,item){showPurchase(item.row);});managerGrid.onSort.subscribe(function(e,args){purchase.sort(function(row1,row2){var field=args.sortCol.field;var sign=args.sortAsc?1:-1;var value1=row1[field];var value2=row2[field];if(field==="quantity"||field==="price"||field==="total"){value1=parseFloat(value1);value2=parseFloat(value2);}
if(value1===value2){return 0;}
if(value1>value2){return sign;}
......@@ -130,8 +130,10 @@ if(!composite&&user===""){alert("Please enter a username.");return;}
var priceLevel="price";if(user!==""){priceLevel=manager.buyerGroup[user];if(!priceLevel){priceLevel="retail";}}
var timestamp=Date.now();var date=$("#manager-date-input").datepicker("getDate");if(date){timestamp=parseInt($.datepicker.formatDate("@",date),10);}
dobrado.log("Loading available products...","info");$.post("/php/request.php",{request:"manager",action:"loadProducts",composite:composite,username:user,product:product,timestamp:timestamp,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,"manager loadProducts")){return;}
var products=JSON.parse(response);var purchaseTotal=0;$.each(products.available,function(index,item){var quantity=0;var currentTime=timestamp;if(products.current[item.name]){quantity=products.current[item.name].quantity;currentTime=products.current[item.name].date;}
var total=quantity*item[priceLevel];purchaseTotal+=total;allProductsData.push({date:currentTime,name:item.name,supplier:item.user,quantity:quantity,unit:item.unit,price:item[priceLevel].toFixed(2),basePrice:item.price.toFixed(2),total:total.toFixed(2)});});$(".manager-view-all-dialog").dialog("open");if(allProductsGrid){allProductsGrid.setData(allProductsData);allProductsGrid.updateRowCount();allProductsGrid.render();$(allProductsGridId).show();$(allProductsGridId+" .slick-header-columns").children().eq(3).click().click();$(".manager-view-all-total").html("Current total: $"+
var productList=JSON.parse(response);var purchaseTotal=0;$.each(productList.available,function(index,item){var quantity=0;var currentTime=timestamp;if(productList.current[item.name]){quantity=productList.current[item.name].quantity;currentTime=productList.current[item.name].date;}
var total=quantity*item[priceLevel];purchaseTotal+=total;allProductsData.push({date:currentTime,name:item.name,supplier:item.user,quantity:quantity,unit:item.unit,price:item[priceLevel].toFixed(2),basePrice:item.price.toFixed(2),total:total.toFixed(2)});});if(composite){$(".manager-view-all-dialog").dialog("option","title","Editing items for "+product);}
else{$(".manager-view-all-dialog").dialog("option","title","Available Products");}
$(".manager-view-all-dialog").dialog("open");if(allProductsGrid){allProductsGrid.setData(allProductsData);allProductsGrid.updateRowCount();allProductsGrid.render();$(allProductsGridId).show();$(allProductsGridId+" .slick-header-columns").children().eq(3).click().click();$(".manager-view-all-total").html("Current total: $"+
purchaseTotal.toFixed(2));}});}
function updatePurchase(e,args){var user=$("#manager-username-input").val();var product=$("#manager-product-input").val();var supplier=$("#manager-supplier-input").val();var item=args.item;var purchaseTotal=0;$.each(allProductsData,function(i,data){purchaseTotal+=data.quantity*data.price;});$(".manager-view-all-total").html("Current total: $"+
purchaseTotal.toFixed(2));var total=item.quantity*item.price;item.total=total.toFixed(2);if(allProductsGrid){allProductsGrid.invalidate();}
......
......@@ -115,6 +115,8 @@ if (!this.dobrado.manager) {
$("#manager-quantity-input").spinner({ min: 0, spin: setQuantity,
change: setQuantity });
$(".manager .toggle-search-options").click(function() {
clearSearchDates();
clearDate();
$(".manager .search-options").toggle();
});
......@@ -919,14 +921,14 @@ if (!this.dobrado.manager) {
if (dobrado.checkResponseError(response, "manager loadProducts")) {
return;
}
var products = JSON.parse(response);
var productList = JSON.parse(response);
var purchaseTotal = 0;
$.each(products.available, function(index, item) {
$.each(productList.available, function(index, item) {
var quantity = 0;
var currentTime = timestamp;
if (products.current[item.name]) {
quantity = products.current[item.name].quantity;
currentTime = products.current[item.name].date;
if (productList.current[item.name]) {
quantity = productList.current[item.name].quantity;
currentTime = productList.current[item.name].date;
}
var total = quantity * item[priceLevel];
purchaseTotal += total;
......@@ -937,6 +939,14 @@ if (!this.dobrado.manager) {
basePrice: item.price.toFixed(2),
total: total.toFixed(2) });
});
if (composite) {
$(".manager-view-all-dialog").dialog("option", "title",
"Editing items for " + product);
}
else {
$(".manager-view-all-dialog").dialog("option", "title",
"Available Products");
}
$(".manager-view-all-dialog").dialog("open");
if (allProductsGrid) {
allProductsGrid.setData(allProductsData);
......
......@@ -28,7 +28,7 @@ if(!this.dobrado.purchase){dobrado.purchase={};}
(function(){'use strict';var purchase=null;var purchaseGrid=null;var purchaseGridId="";var allAvailableGrid=null;var allAvailableGridId="";var allAvailableData=[];var allUserGrid=null;var allUserGridId="";var allUserData=[];var allUserChanges={};var currentProduct=null;var currentWeek=true;var startTime=new Date().getTime();var tomorrow=0;var yesterday=0;var oneDay=86400000;var stockUpdate=0;var orderMode=false;$(function(){if($(".purchase").length===0){return;}
window.addEventListener("beforeunload",function(event){if(purchase&&purchase.processed&&purchase.processed.length!==0){var message="Please save your orders before leaving the page.";event.returnValue=message;return message;}});$("#purchase-details-form").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Add User Details",width:400});$("#purchase-details-form .submit").button().click(saveDetails);$(".purchase-all-available-dialog").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},title:"Available Products",width:830,height:520,close:function(){if(allAvailableGrid){allAvailableGrid.gotoCell(0,0);}}});$(".purchase-all-users-dialog").dialog({autoOpen:false,modal:true,position:{my:"top",at:"top+50",of:"body"},width:830,height:500,close:function(){if(allUserGrid){allUserGrid.gotoCell(0,0);}}});$("#purchase-form .view-all").click(viewAll);$(".purchase .save").button({disabled:true}).click(save);$("#purchase-next-week-input").click(nextWeek);$("#purchase-form .remove").button({disabled:true}).click(remove);$("#purchase-form .add").button({disabled:true}).click(add);$("#purchase-name-input").val("").change(function(){setTimeout(function(){showUser();},10);});$("#purchase-product-input").val("").change(function(){setTimeout(function(){showProduct();},10);});$("#purchase-price-input").val("");$("#purchase-date-input").val("").datepicker({dateFormat:dobrado.dateFormat,minDate:0});$("#purchase-quantity-input").val("");$("#purchase-quantity-input").spinner({disabled:true,min:0,spin:setQuantity,change:setQuantity});$("#purchase-form .add-next-week").click(toggleWeek);$(".purchase-save-order-dismiss").button().click(function(){$(".purchase-save-order-confirm").dialog("close");});$(".grid").each(function(index){if(index===0){purchaseGridId="#"+$(this).attr("id");}
if(index===1){allAvailableGridId="#"+$(this).attr("id");}
if(index===2){allUserGridId="#"+$(this).attr("id");}});if($(".grid").length!==0){$(".grid").hide();var purchaseColumns=[{id:"product",name:"Product",field:"name",width:210},{id:"supplier",name:"Supplier",field:"supplier",width:140},{id:"date",name:"Date",field:"date",width:110,formatter:Slick.Formatters.Timestamp},{id:"quantity",name:"Quantity",field:"quantity",width:100},{id:"price",name:"Price",field:"price",width:100,formatter:Slick.Formatters.Dollar},{id:"total",name:"Total",field:"total",width:80,formatter:Slick.Formatters.Dollar}];var purchaseOptions={enableColumnReorder:false,forceFitColumns:true};purchaseGrid=dobrado.grid.instance(purchaseGridId,[],purchaseColumns,purchaseOptions);purchaseGrid.setSelectionModel(new Slick.RowSelectionModel());purchaseGrid.onClick.subscribe(function(e,item){showPurchase(item.row);});if($(".grid").length>=2){var allAvailableColumns=[{id:"product",name:"Product",field:"name",width:210,sortable:true},{id:"supplier",name:"Supplier",field:"supplier",width:140,sortable:true},{id:"date",name:"Date",field:"date",width:110,sortable:true,formatter:Slick.Formatters.Timestamp,editor:Slick.Editors.Date},{id:"quantity",name:"Quantity",field:"quantity",width:100,sortable:true,editor:Slick.Editors.Number},{id:"price",name:"Price",field:"price",width:100,sortable:true,formatter:Slick.Formatters.Units},{id:"total",name:"Total",field:"total",width:80,sortable:true,formatter:Slick.Formatters.Dollar}];var allAvailableOptions={editable:true,enableColumnReorder:false,forceFitColumns:true};$(allAvailableGridId).appendTo($(".purchase-all-available-dialog"));allAvailableGrid=dobrado.grid.instance(allAvailableGridId,[],allAvailableColumns,allAvailableOptions);allAvailableGrid.setSelectionModel(new Slick.RowSelectionModel());allAvailableGrid.onClick.subscribe(function(e,item){showAllAvailablePurchase(item.row);});allAvailableGrid.onCellChange.subscribe(updateAvailablePurchaseData);allAvailableGrid.onSort.subscribe(function(e,args){allAvailableData.sort(function(row1,row2){var field=args.sortCol.field;var sign=args.sortAsc?1:-1;var value1=row1[field];var value2=row2[field];if(field!=="name"&&field!=="supplier"){value1=parseFloat(value1);value2=parseFloat(value2);}
if(index===2){allUserGridId="#"+$(this).attr("id");}});if($(".grid").length!==0){$(".grid").hide();var purchaseColumns=[{id:"product",name:"Product",field:"name",width:210},{id:"supplier",name:"Supplier",field:"supplier",width:140},{id:"date",name:"Date",field:"date",width:110,formatter:Slick.Formatters.Timestamp},{id:"quantity",name:"Quantity",field:"quantity",width:100},{id:"price",name:"Price",field:"price",width:100,formatter:Slick.Formatters.Dollar},{id:"total",name:"Total",field:"total",width:80,formatter:Slick.Formatters.Dollar}];var purchaseOptions={enableColumnReorder:false,forceFitColumns:true};purchaseGrid=dobrado.grid.instance(purchaseGridId,[],purchaseColumns,purchaseOptions);purchaseGrid.setSelectionModel(new Slick.RowSelectionModel());purchaseGrid.onClick.subscribe(function(e,item){showPurchase(item.row);});purchaseGrid.onSelectedRowsChanged.subscribe(function(e,item){if(item.rows.length===1){showPurchase(item.rows[0]);}});if($(".grid").length>=2){var allAvailableColumns=[{id:"product",name:"Product",field:"name",width:210,sortable:true},{id:"supplier",name:"Supplier",field:"supplier",width:140,sortable:true},{id:"date",name:"Date",field:"date",width:110,sortable:true,formatter:Slick.Formatters.Timestamp,editor:Slick.Editors.Date},{id:"quantity",name:"Quantity",field:"quantity",width:100,sortable:true,editor:Slick.Editors.Number},{id:"price",name:"Price",field:"price",width:100,sortable:true,formatter:Slick.Formatters.Units},{id:"total",name:"Total",field:"total",width:80,sortable:true,formatter:Slick.Formatters.Dollar}];var allAvailableOptions={editable:true,enableColumnReorder:false,forceFitColumns:true};$(allAvailableGridId).appendTo($(".purchase-all-available-dialog"));allAvailableGrid=dobrado.grid.instance(allAvailableGridId,[],allAvailableColumns,allAvailableOptions);allAvailableGrid.setSelectionModel(new Slick.RowSelectionModel());allAvailableGrid.onClick.subscribe(function(e,item){showAllAvailablePurchase(item.row);});allAvailableGrid.onSelectedRowsChanged.subscribe(function(e,item){if(item.rows.length===1){showAllAvailablePurchase(item.rows[0]);}});allAvailableGrid.onCellChange.subscribe(updateAvailablePurchaseData);allAvailableGrid.onSort.subscribe(function(e,args){allAvailableData.sort(function(row1,row2){var field=args.sortCol.field;var sign=args.sortAsc?1:-1;var value1=row1[field];var value2=row2[field];if(field!=="name"&&field!=="supplier"){value1=parseFloat(value1);value2=parseFloat(value2);}
if(value1===value2){return 0