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();
$query = "";
if ($organisation) {
$organiser = new Organiser($this->user, $this->owner);
$mysqli = connect_db();
$query = 'SELECT banking.user, reference, name, number, bsb, credit, '.
'surcharge, buyer_group FROM banking LEFT JOIN users ON '.
'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();
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;
}
......
This diff is collapsed.
......@@ -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;}
if(value1>value2){return sign;}
else{return sign* -1;}});allAvailableGrid.invalidate();});}
......
......@@ -184,6 +184,11 @@ if (!this.dobrado.purchase) {
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 a second grid module is on the page initialise columns to view
// all available products.
......@@ -217,6 +222,11 @@ if (!this.dobrado.purchase) {
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) {
......
This diff is collapsed.
......@@ -51,6 +51,10 @@ if (!this.dobrado.stock) {
var currentProduct = null;
// Allow showing available products only.
var showAvailable = false;
// Remember the last product saved during import to check for duplicates.
var lastProductSaved = "";
// Stop submit function being called again before it returns.
var saving = false;
$(function() {
// Don't run if the module isn't on the page.
......@@ -178,14 +182,14 @@ if (!this.dobrado.stock) {
// quantity adjustments.
if ($(".grid").length === 2 && stock.trackQuantity) {
var adjustmentColumns =
[ { id: "product", name: "Product", field: "name", width: 220,
[ { id: "product", name: "Product", field: "name", width: 200,
sortable: true },
{ id: "supplier", name: "Supplier", field: "supplier",
width: 140, sortable: true },
width: 130, sortable: true },
{ id: "date", name: "Date", field: "date", width: 110,
sortable: true, formatter: Slick.Formatters.Timestamp },
{ id: "adjustment", name: "Total", field: "adjustment",
width: 90, sortable: true }];
{ id: "adjustment", name: "Adjustment", field: "adjustment",
width: 120, sortable: true }];
var adjustmentOptions = {
enableColumnReorder: false,
forceFitColumns: true
......@@ -401,6 +405,7 @@ if (!this.dobrado.stock) {
$.isNumeric(price) && size !== currentProduct.size) {
var newPrice = currentProduct.size / size * price;
$("#stock-price-input").val(newPrice.toFixed(2));
currentProduct.size = size;
}
// Need to always update wholesale and retail prices because they don't
// get pulled from the import data.
......@@ -551,7 +556,6 @@ if (!this.dobrado.stock) {
}
var data = adjustmentData[row];
$("#stock-quantity-adjust-input").val(data.adjustment);
$("#stock-quantity-reason-input").val(data.description);
}
......@@ -601,7 +605,24 @@ if (!this.dobrado.stock) {
}
function submit() {
// Don't allow submit to be called more than once before returning.
if (saving) {
return false;
}
var name = $("#stock-name-input").val();
if (name === "") {
return false;
}
// Want to check for submitting duplicate rows during import, which exist
// due to half boxes. Just ask the user to confirm the second entry.
if (name === lastProductSaved) {
if (!confirm("This product was just saved, continue?")) {
showImportData(false);
return false;
}
}
var supplier = $("#stock-user-input").val();
// Want to set new and total quantity to zero if empty, but using
// parseFloat and isNaN covers all cases.
......@@ -628,7 +649,9 @@ if (!this.dobrado.stock) {
var composite = $("#stock-composite-input:checked").length;
var selectedRow = 0;
if ($.inArray(supplier, stock.users) === -1) {
alert("Supplier not found.");
alert("If an account was just created for " + supplier +
" please reload the page.");
$("#stock-user-input").val("");
return false;
}
var oldName = "";
......@@ -636,6 +659,8 @@ if (!this.dobrado.stock) {
oldName = currentProduct.name;
}
saving = true;
$("#stock-form .submit").button("option", "disabled", true);
dobrado.log("Saving stock...", "info");
$.post("/php/request.php",
{ request: "stock",
......@@ -659,6 +684,8 @@ if (!this.dobrado.stock) {
token: dobrado.token },
function(response) {
if (dobrado.checkResponseError(response, "stock edit")) {
saving = false;
$("#stock-form .submit").button("option", "disabled", false);
return;
}
var newItem = true;
......@@ -737,8 +764,11 @@ if (!this.dobrado.stock) {
resetForm();
}
else {
lastProductSaved = name;
showImportData(false);
}
saving = false;
$("#stock-form .submit").button("option", "disabled", false);
});
return false;
}
......@@ -1134,6 +1164,7 @@ if (!this.dobrado.stock) {
}
return;
}
productInfo = "<b>Existing product: " + row.product + "</b>";
if (changes.product) {
productInfo += " has changed";
......@@ -1153,6 +1184,7 @@ if (!this.dobrado.stock) {
}
return;
}
productInfo = "<b>New product: " + row.product + "</b>";
// The category field is not imported so set to empty.
$("#stock-category-input").val("");
......@@ -1170,16 +1202,8 @@ if (!this.dobrado.stock) {
$("#stock-unit-select").val(row.unit);
$("#stock-price-input").val(row.price);
$("#stock-composite-input").prop("checked", false);
// If the product exists, check if the size has changed as this will
// update the base price.
if (changes.exists && changes.size) {
updateSize();
}
// Otherwise always update the wholesale and retail prices anyway because
// they don't get pulled from the import data.
else {
// Update wholesale and retail prices which aren't in import data.
updatePrices();
}
$("#stock-growing-input").val(row.grower);
if (row.available === 1) {
$("#stock-order-available-input").prop("checked", true);
......@@ -1226,16 +1250,8 @@ if (!this.dobrado.stock) {
else {
productInfo = "<b>New product: " + product + "</b>";
}
// If the product exists, check if the size has changed as this will
// update the base price.
if (changes.exists && changes.size) {
updateSize();
}
// Otherwise always update the wholesale and retail prices anyway because
// they don't get pulled from the import data.
else {
// Update wholesale and retail prices as they're not imported.
updatePrices();
}
// currentImport has already been incremented, so need to show one less.
$(".import .info").html("<br>Transactions found in imported data file. " +
"Processing <b>" + (currentImport - 1) + "/" +
......
......@@ -41,8 +41,9 @@ function startSort(event,ui){sortable={maxHeight:$(ui.item).css("max-height"),ov
function stopSort(event,ui){$(ui.item).css("max-height",sortable.maxHeight);$(ui.item).css("overflow",sortable.overflow);$(".sortable").sortable("option","forcePlaceholderSize",false);}
dobrado.save=function(event){if(dobrado.current&&event.editor.checkDirty()){dobrado.log("Saving content...","info");var editable=$(dobrado.current+" .editable");var content={data:event.editor.getData()};if(editable.prop("tagName")==="TEXTAREA"){editable.val(content.data);}
else{editable.html(content.data);}
$.post("/php/content.php",{id:dobrado.current,label:$(dobrado.current).data("label"),content:JSON.stringify(content),url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,"content")){return;}});event.editor.resetDirty();}};dobrado.changePage=function(page){dobrado.log("Changing page...","info");if(/^[a-z0-9\/_-]+$/i.test(page)){var fields=page.match(/^([^/]+)\/?(.*)$/);if(fields&&fields.length===3){if(fields[2]===""){var user=dobrado.readCookie("user");if(user){if(user==="admin"){location.href="/index.php?page="+fields[1];}
else{location.href="/"+user+"/index.php?page="+fields[1];}}}
$.post("/php/content.php",{id:dobrado.current,label:$(dobrado.current).data("label"),content:JSON.stringify(content),url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,"content")){return;}});event.editor.resetDirty();}};dobrado.changePage=function(page){var user=dobrado.readCookie("user");if(!user){dobrado.log("Error: User unknown","error");return;}
dobrado.log("Changing page...","info");if(/^[a-z0-9\/_-]+$/i.test(page)){var fields=page.match(/^([^/]+)\/?(.*)$/);if(fields&&fields.length===3){if(fields[2]===""){if(user==="admin"){location.href="/index.php?page="+fields[1];}
else{location.href="/"+user+"/index.php?page="+fields[1];}}
else{if(fields[1]==="admin"){location.href="/index.php?page="+fields[2];}
else{location.href="/"+fields[1]+"/index.php?page="+fields[2];}}}}
else{dobrado.log("Invalid page name.","error");}};dobrado.ckeditor=function(response){if(dobrado.checkResponseError(response,"ckeditor")){return;}
......@@ -78,7 +79,8 @@ else{location.reload();}}});}
else{$.each(CKEDITOR.instances,function(){this.fire("blur");});}};dobrado.removeModule=function(id,done){var label=$(id).data("label");$.post("/php/remove.php",{id:id,label:label,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,"remove")){return;}
var remove=JSON.parse(response);if(remove.done){$(id).remove();if(done){done();}}});};dobrado.readCookie=function(name){var exp=new RegExp(name+"=([^;]+)");var fields=document.cookie.match(exp);if(fields&&fields.length===2){return fields[1];}
return null;};dobrado.home=function(){var user=dobrado.readCookie("user");if(user){if(user==="admin"){location.href="/";}
else{location.href="/"+user;}}};dobrado.log=function(message,status){$(".control ."+status+" .message").html(message).parent().show();$(".control ."+status).position({of:$(".control"),my:"top",at:"top+5"});if(status==="error"){setTimeout(function(){$(".control .error").hide();},5000);}};dobrado.checkResponseError=function(response,server){if($(".control .info").is(":visible")){setTimeout(function(){$(".control .info").hide();},1000);}
else{location.href="/"+user;}}
else{dobrado.log("Error: User unknown","error");}};dobrado.log=function(message,status){$(".control ."+status+" .message").html(message).parent().show();$(".control ."+status).position({of:$(".control"),my:"top",at:"top+5"});if(status==="error"){setTimeout(function(){$(".control .error").hide();},5000);}};dobrado.checkResponseError=function(response,server){if($(".control .info").is(":visible")){setTimeout(function(){$(".control .info").hide();},1000);}
if(ignoreResponse){return true;}
if(!response){dobrado.log("No response to '"+server+"' call.","error");return true;}
var reply=JSON.parse(response);if(!reply){dobrado.log("Cannot interpret response to '"+server+"' call.","error");return true;}
......
......@@ -231,6 +231,12 @@ if (!this.dobrado) {
};
dobrado.changePage = function(page) {
var user = dobrado.readCookie("user");
if (!user) {
dobrado.log("Error: User unknown", "error");
return;
}
dobrado.log("Changing page...", "info");
if (/^[a-z0-9\/_-]+$/i.test(page)) {
// Look for an optional username before the page name.
......@@ -238,8 +244,6 @@ if (!this.dobrado) {
if (fields && fields.length === 3) {
if (fields[2] === "") {
// Only the page name, must be the user's own page.
var user = dobrado.readCookie("user");
if (user) {
if (user === "admin") {
location.href = "/index.php?page=" + fields[1];
}
......@@ -247,7 +251,6 @@ if (!this.dobrado) {
location.href = "/" + user + "/index.php?page=" + fields[1];
}
}
}
else {
// Otherwise redirect to another user's page.
// Also special case for the 'admin' user.
......@@ -566,6 +569,9 @@ if (!this.dobrado) {
location.href = "/" + user;
}