Added a function to temporariliy suspend availability of products
for service. Also did some refactoring in related areas.
This commit is contained in:
parent
e5790ca013
commit
0923435462
11
database.sql
11
database.sql
@ -89,6 +89,17 @@ create table `loan` (
|
||||
) character set utf8mb4,
|
||||
collate utf8mb4_unicode_ci;
|
||||
|
||||
create table `service` (
|
||||
`id` bigint(20) not null auto_increment,
|
||||
primary key(`id`),
|
||||
`product` bigint(20) not null,
|
||||
constraint `s_f_product`
|
||||
foreign key(`product`) references `product`(`id`),
|
||||
`starttime` bigint(20) not null,
|
||||
`returntime` bigint(20) default null
|
||||
) character set utf8mb4,
|
||||
collate utf8mb4_unicode_ci;
|
||||
|
||||
create table `inventory` (
|
||||
`id` bigint(20) not null auto_increment,
|
||||
primary key(`id`),
|
||||
|
@ -244,6 +244,17 @@
|
||||
</form>
|
||||
¤label¤
|
||||
<div class="clear"></div>
|
||||
<form class="¤hidden¤">
|
||||
<input type="hidden"
|
||||
name="id"
|
||||
value="¤id¤" />
|
||||
<button onClick="JavaScript:discardProduct(event)">
|
||||
Skrota artikel
|
||||
</button>
|
||||
<button onClick="JavaScript:toggleService(event)">
|
||||
¤service¤
|
||||
</button>
|
||||
</form>
|
||||
|
||||
¤¤ product_label ¤¤
|
||||
<div class="qr left">
|
||||
@ -271,16 +282,6 @@
|
||||
</body>
|
||||
</html>
|
||||
|
||||
¤¤ discard_button ¤¤
|
||||
<form>
|
||||
<input type="hidden"
|
||||
name="id"
|
||||
value="¤id¤" />
|
||||
<button onClick="JavaScript:discardProduct(event)">
|
||||
Skrota artikel
|
||||
</button>
|
||||
</form>
|
||||
|
||||
¤¤ info_item ¤¤
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -54,6 +54,8 @@ class Ajax extends Responder {
|
||||
case 'discardproduct':
|
||||
$out = $this->discard_product();
|
||||
break;
|
||||
case 'toggleservice':
|
||||
$out = $this->toggle_service();
|
||||
}
|
||||
print($out->toJson());
|
||||
}
|
||||
@ -67,7 +69,12 @@ class Ajax extends Responder {
|
||||
}
|
||||
|
||||
private function checkout_product() {
|
||||
$user = new User($_POST['user'], 'name');
|
||||
$user = null;
|
||||
try {
|
||||
$user = new User($_POST['user'], 'name');
|
||||
} catch(Exception $e) {
|
||||
return new Failure('Ogiltigt användar-id.');
|
||||
}
|
||||
$product = null;
|
||||
try {
|
||||
$product = new Product($_POST['product'], 'serial');
|
||||
@ -75,7 +82,7 @@ class Ajax extends Responder {
|
||||
return new Failure('Ogiltigt serienummer.');
|
||||
}
|
||||
try {
|
||||
$user->create_loan($product, $_POST['end']);
|
||||
Loan::create_loan($user, $product, $_POST['end']);
|
||||
return new Success($product->get_name() . 'utlånad.');
|
||||
} catch(Exception $e) {
|
||||
return new Failure('Artikeln är redan utlånad.');
|
||||
@ -345,5 +352,16 @@ class Ajax extends Responder {
|
||||
return new Failure('Artikeln är redan skrotad.');
|
||||
}
|
||||
}
|
||||
|
||||
private function toggle_service() {
|
||||
$product = new Product($_POST['id']);
|
||||
try {
|
||||
$product->toggle_service();
|
||||
return new Success('Service-status uppdaterad.');
|
||||
} catch(Exception $e) {
|
||||
return new Failure('Service kan inte registreras '
|
||||
.'på den här artikeln nu.');
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -6,9 +6,51 @@ class Loan {
|
||||
private $starttime = 0;
|
||||
private $endtime = 0;
|
||||
private $returntime = null;
|
||||
|
||||
public static function create_loan($user, $product, $endtime) {
|
||||
$status = $product->get_status();
|
||||
if($status != 'available') {
|
||||
$emsg = '';
|
||||
$prod_id = $product->get_id();
|
||||
switch($status) {
|
||||
case 'on_loan':
|
||||
case 'overdue':
|
||||
$loan_id = $product->get_active_loan()->get_id();
|
||||
$emsg = "Product $prod_id has an active ";
|
||||
$emsg .= "loan (id $loan_id) already.";
|
||||
break;
|
||||
case 'discarded':
|
||||
$emsg = "Product $prod_id has been discarded.";
|
||||
break;
|
||||
case 'service':
|
||||
$service_id = $product->get_active_service()->get_id();
|
||||
$emsg = "Product $prod_id is on service (id $service_id).";
|
||||
break;
|
||||
}
|
||||
throw new Exception($emsg);
|
||||
}
|
||||
$now = time();
|
||||
$insert = prepare('insert into
|
||||
`loan`(`user`, `product`, `starttime`, `endtime`)
|
||||
values (?, ?, ?, ?)');
|
||||
bind($insert, 'iiii',
|
||||
$user->get_id(), $product->get_id(),
|
||||
$now, strtotime($endtime . ' 13:00'));
|
||||
execute($insert);
|
||||
$loan_id = $insert->insert_id;
|
||||
return new Loan($loan_id);
|
||||
}
|
||||
|
||||
public function __construct($id) {
|
||||
$this->id = $id;
|
||||
$search = prepare('select `id` from `loan`
|
||||
where `id`=?');
|
||||
bind($search, 'i', $id);
|
||||
execute($search);
|
||||
$result = result_single($search);
|
||||
if($result === null) {
|
||||
throw new Exception('Loan does not exist.');
|
||||
}
|
||||
$this->id = $result['id'];
|
||||
$this->update_fields();
|
||||
}
|
||||
|
||||
|
@ -135,14 +135,19 @@ abstract class Page extends Responder {
|
||||
'page' => 'products'),
|
||||
$this->fragments['item_link']);
|
||||
$available = 'Tillgänglig';
|
||||
$status = 'available';
|
||||
$discarded = $product->get_discardtime();
|
||||
if($discarded) {
|
||||
$available = 'Skrotad '.$discarded;
|
||||
$status = 'discarded';
|
||||
} else {
|
||||
$loan = $product->get_active_loan();
|
||||
if($loan) {
|
||||
$status = $product->get_status();
|
||||
switch($status) {
|
||||
case 'discarded':
|
||||
$available = 'Skrotad '.$discarded;
|
||||
break;
|
||||
case 'service':
|
||||
$service = $product->get_active_service();
|
||||
$available = 'På service sedan '
|
||||
.$service->get_duration()['start'];
|
||||
break;
|
||||
case 'on_loan':
|
||||
case 'overdue':
|
||||
$loan = $product->get_active_loan();
|
||||
$user = $loan->get_user();
|
||||
$userlink = replace(array('name' => $user->get_displayname(),
|
||||
'id' => $user->get_id(),
|
||||
@ -150,13 +155,11 @@ abstract class Page extends Responder {
|
||||
$this->fragments['item_link']);
|
||||
$available = 'Utlånad till '.$userlink;
|
||||
if($loan->is_overdue()) {
|
||||
$status = 'overdue';
|
||||
$available .= ', försenad';
|
||||
} else {
|
||||
$status = 'on_loan';
|
||||
$available .= ', åter '.$loan->get_duration()['end'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
$rows .= replace(array('available' => $available,
|
||||
'serial' => $product->get_serial(),
|
||||
@ -194,7 +197,6 @@ abstract class Page extends Responder {
|
||||
'name' => $product->get_name(),
|
||||
'page' => 'products'),
|
||||
$this->fragments['item_link']);
|
||||
$available = '';
|
||||
$duration = $loan->get_duration();
|
||||
$status = 'on_loan';
|
||||
if($loan->is_overdue()) {
|
||||
@ -222,41 +224,45 @@ abstract class Page extends Responder {
|
||||
$this->fragments['loan_table']);
|
||||
}
|
||||
|
||||
final protected function build_product_loan_table($loans) {
|
||||
final protected function build_product_history_table($history) {
|
||||
$rows = '';
|
||||
$renew_column_visible = 'hidden';
|
||||
foreach($loans as $loan) {
|
||||
$user = $loan->get_user();
|
||||
$product = $loan->get_product();
|
||||
$userlink = replace(array('id' => $user->get_id(),
|
||||
'name' => $user->get_name(),
|
||||
'page' => 'users'),
|
||||
$this->fragments['item_link']);
|
||||
$available = '';
|
||||
$duration = $loan->get_duration();
|
||||
$status = 'on_loan';
|
||||
if($loan->is_overdue()) {
|
||||
$status = 'overdue';
|
||||
foreach($history as $event) {
|
||||
$duration = $event->get_duration();
|
||||
$product = $event->get_product();
|
||||
$fields = array('item_link' => 'Service',
|
||||
'start_date' => $duration['start'],
|
||||
'end_date' => $duration['end'],
|
||||
'return_date' => $duration['return'],
|
||||
'status' => 'available',
|
||||
'vis_renew' => $renew_column_visible,
|
||||
'vis_renew_button' => 'hidden',
|
||||
'vis_return' => '',
|
||||
'id' => $product->get_id(),
|
||||
'end_new' => '');
|
||||
if($event instanceof Loan) {
|
||||
$user = $event->get_user();
|
||||
$id = $user->get_id();
|
||||
$name = $user->get_name();
|
||||
$fields['item_link'] = replace(array('id' => $id,
|
||||
'name' => $name,
|
||||
'page' => 'users'),
|
||||
$this->fragments['item_link']);
|
||||
$fields['end_new'] = $duration['end_renew'];
|
||||
if($event->is_active()) {
|
||||
$fields['vis_renew_button'] = '';
|
||||
$fields['vis_renew'] = '';
|
||||
$renew_column_visible = '';
|
||||
if($event->is_overdue()) {
|
||||
$fields['status'] = 'overdue';
|
||||
} else {
|
||||
$fields['status'] = 'on_loan';
|
||||
}
|
||||
}
|
||||
} else if ($event instanceof Service) {
|
||||
$fields['status'] = 'service';
|
||||
}
|
||||
$returndate = '';
|
||||
$renew_visible = '';
|
||||
if($duration['return']) {
|
||||
$returndate = $duration['return'];
|
||||
$renew_visible = 'hidden';
|
||||
} else {
|
||||
$renew_column_visible = '';
|
||||
}
|
||||
$rows .= replace(array('item_link' => $userlink,
|
||||
'start_date' => $duration['start'],
|
||||
'end_date' => $duration['end'],
|
||||
'return_date' => $returndate,
|
||||
'status' => $status,
|
||||
'vis_renew' => $renew_column_visible,
|
||||
'vis_renew_button' => $renew_visible,
|
||||
'vis_return' => '',
|
||||
'id' => $product->get_id(),
|
||||
'end_new' => $duration['end_renew']),
|
||||
$this->fragments['loan_row']);
|
||||
$rows .= replace($fields, $this->fragments['loan_row']);
|
||||
}
|
||||
return replace(array('rows' => $rows,
|
||||
'vis_renew' => $renew_column_visible,
|
||||
|
@ -9,13 +9,11 @@ class Product {
|
||||
private $info = array();
|
||||
private $tags = array();
|
||||
|
||||
public static function create_product(
|
||||
$name = '',
|
||||
$invoice = '',
|
||||
$serial = '',
|
||||
$info = array(),
|
||||
$tags = array()
|
||||
) {
|
||||
public static function create_product($name = '',
|
||||
$invoice = '',
|
||||
$serial = '',
|
||||
$info = array(),
|
||||
$tags = array()) {
|
||||
$now = time();
|
||||
begin_trans();
|
||||
try {
|
||||
@ -59,7 +57,7 @@ class Product {
|
||||
execute($search);
|
||||
$result = result_single($search);
|
||||
if($result === null) {
|
||||
throw new Exception('Product does not exist..');
|
||||
throw new Exception('Product does not exist.');
|
||||
}
|
||||
$this->id = $result['id'];
|
||||
$this->update_fields();
|
||||
@ -118,7 +116,7 @@ class Product {
|
||||
case 'tag':
|
||||
$matchvalues = $this->get_tags();
|
||||
case 'status':
|
||||
$matchvalues[] = $this->get_loan_status();
|
||||
$matchvalues[] = $this->get_status();
|
||||
case 'fritext':
|
||||
$matchvalues[] = $this->name;
|
||||
$matchvalues[] = $this->serial;
|
||||
@ -152,6 +150,9 @@ class Product {
|
||||
}
|
||||
|
||||
public function discard() {
|
||||
if($this->get_status() != 'available') {
|
||||
return false;
|
||||
}
|
||||
$now = time();
|
||||
$update = prepare('update `product` set `discardtime`=? where `id`=?');
|
||||
bind($update, 'ii', $now, $this->id);
|
||||
@ -160,6 +161,33 @@ class Product {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toggle_service() {
|
||||
$status = $this->get_status();
|
||||
$now = time();
|
||||
$update = '';
|
||||
if($status == 'service') {
|
||||
return $this->get_active_service()->end();
|
||||
} else if($status == 'available') {
|
||||
Service::create_service($this);
|
||||
return true;
|
||||
}
|
||||
$id = $this->get_id();
|
||||
throw new Exception("The state ($status) of this product (id $id) "
|
||||
."does not allow servicing.");
|
||||
}
|
||||
|
||||
public function get_active_service() {
|
||||
$find = prepare('select `id` from `service`'
|
||||
.'where `returntime` is null and product=?');
|
||||
bind($find, 'i', $this->id);
|
||||
execute($find);
|
||||
$result = result_single($find);
|
||||
if($result === null) {
|
||||
return null;
|
||||
}
|
||||
return new Service($result['id']);
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return $this->name;
|
||||
}
|
||||
@ -276,13 +304,16 @@ class Product {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_loan_status() {
|
||||
public function get_status() {
|
||||
if($this->get_discardtime(false)) {
|
||||
return 'discarded';
|
||||
}
|
||||
if($this->get_active_service()) {
|
||||
return 'service';
|
||||
}
|
||||
$loan = $this->get_active_loan();
|
||||
if(!$loan) {
|
||||
return 'no_loan';
|
||||
return 'available';
|
||||
}
|
||||
if($loan->is_overdue()) {
|
||||
return 'overdue';
|
||||
@ -302,16 +333,23 @@ class Product {
|
||||
return new Loan($result['id']);
|
||||
}
|
||||
|
||||
public function get_loan_history() {
|
||||
$find = prepare('select `id` from `loan`
|
||||
where product=? order by `starttime` desc');
|
||||
bind($find, 'i', $this->id);
|
||||
execute($find);
|
||||
$loans = result_list($find);
|
||||
public function get_history() {
|
||||
$out = array();
|
||||
foreach($loans as $loan) {
|
||||
$out[] = new Loan($loan['id']);
|
||||
foreach(array('loan' => function($id) { return new Loan($id);},
|
||||
'service' => function($id) { return new Service($id);})
|
||||
as $type => $func) {
|
||||
$find = prepare("select `id` from `$type`"
|
||||
.'where `product`=? order by `starttime` desc');
|
||||
bind($find, 'i', $this->id);
|
||||
execute($find);
|
||||
$items = result_list($find);
|
||||
foreach($items as $item) {
|
||||
$out[] = $func($item['id']);
|
||||
}
|
||||
}
|
||||
usort($out, function($a, $b) {
|
||||
return $a->get_duration()['start'] < $b->get_duration()['start'];
|
||||
});
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
@ -78,25 +78,29 @@ class ProductPage extends Page {
|
||||
'serial' => $this->product->get_serial(),
|
||||
'invoice' => $this->product->get_invoice(),
|
||||
'tags' => $tags,
|
||||
'info' => $info);
|
||||
$label = '';
|
||||
'info' => $info,
|
||||
'label' => '',
|
||||
'hidden' => 'hidden',
|
||||
'service' => 'Starta service');
|
||||
if(class_exists('QRcode')) {
|
||||
$label = replace($fields, $this->fragments['product_label']);
|
||||
$fields['label'] = replace($fields,
|
||||
$this->fragments['product_label']);
|
||||
}
|
||||
$fields['label'] = $label;
|
||||
$out = replace($fields, $this->fragments['product_details']);
|
||||
if(!$this->product->get_discardtime()) {
|
||||
$out .= replace(array('id' => $this->product->get_id()),
|
||||
$this->fragments['discard_button']);
|
||||
$fields['hidden'] = '';
|
||||
if($this->product->get_status() == 'service') {
|
||||
$fields['service'] = 'Avsluta service';
|
||||
}
|
||||
}
|
||||
$out .= replace(array('title' => 'Lånehistorik'),
|
||||
$out = replace($fields, $this->fragments['product_details']);
|
||||
$out .= replace(array('title' => 'Artikelhistorik'),
|
||||
$this->fragments['subtitle']);
|
||||
$loan_table = 'Inga lån att visa.';
|
||||
$history = $this->product->get_loan_history();
|
||||
$history_table = 'Ingen historik att visa.';
|
||||
$history = $this->product->get_history();
|
||||
if($history) {
|
||||
$loan_table = $this->build_product_loan_table($history);
|
||||
$history_table = $this->build_product_history_table($history);
|
||||
}
|
||||
$out .= $loan_table;
|
||||
$out .= $history_table;
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ class SearchPage extends Page {
|
||||
case 'ledigt':
|
||||
case 'tillgänglig':
|
||||
case 'tillgängligt':
|
||||
$newitem = 'no_loan';
|
||||
$newitem = 'available';
|
||||
break;
|
||||
case 'sen':
|
||||
case 'sent':
|
||||
@ -95,6 +95,10 @@ class SearchPage extends Page {
|
||||
case 'slängt':
|
||||
$newitem = 'discarded';
|
||||
break;
|
||||
case 'lagning':
|
||||
case 'reparation':
|
||||
$newitem = 'service';
|
||||
break;
|
||||
}
|
||||
$translated[] = $newitem;
|
||||
}
|
||||
|
109
include/Service.php
Normal file
109
include/Service.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
class Service {
|
||||
private $id = 0;
|
||||
private $product = 0;
|
||||
private $starttime = 0;
|
||||
private $returntime = null;
|
||||
|
||||
public static function create_service($product) {
|
||||
$status = $product->get_status();
|
||||
if($status != 'available') {
|
||||
$emsg = '';
|
||||
$prod_id = $product->get_id();
|
||||
switch($status) {
|
||||
case 'on_loan':
|
||||
case 'overdue':
|
||||
$loan_id = $product->get_active_loan()->get_id();
|
||||
$emsg = "Product $prod_id has an active ";
|
||||
$emsg .= "loan (id $loan_id).";
|
||||
break;
|
||||
case 'discarded':
|
||||
$emsg = "Product $prod_id has been discarded.";
|
||||
break;
|
||||
case 'service':
|
||||
$service_id = $product->get_active_service()->get_id();
|
||||
$emsg = "Product $prod_id is on service "
|
||||
."(id $service_id) already.";
|
||||
break;
|
||||
}
|
||||
throw new Exception($emsg);
|
||||
}
|
||||
$now = time();
|
||||
$insert = prepare('insert into
|
||||
`service`(`product`, `starttime`)
|
||||
values (?, ?)');
|
||||
bind($insert, 'ii', $product->get_id(), $now);
|
||||
execute($insert);
|
||||
$service_id = $insert->insert_id;
|
||||
return new Loan($service_id);
|
||||
}
|
||||
|
||||
public function __construct($id) {
|
||||
$search = prepare('select `id` from `service`
|
||||
where `id`=?');
|
||||
bind($search, 'i', $id);
|
||||
execute($search);
|
||||
$result = result_single($search);
|
||||
if($result === null) {
|
||||
throw new Exception('Service does not exist.');
|
||||
}
|
||||
$this->id = $result['id'];
|
||||
$this->update_fields();
|
||||
}
|
||||
|
||||
private function update_fields() {
|
||||
$get = prepare('select * from `service` where `id`=?');
|
||||
bind($get, 'i', $this->id);
|
||||
execute($get);
|
||||
$loan = result_single($get);
|
||||
$this->product = $loan['product'];
|
||||
$this->starttime = $loan['starttime'];
|
||||
$this->returntime = $loan['returntime'];
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function get_user() {
|
||||
return 'Service';
|
||||
}
|
||||
|
||||
public function get_product() {
|
||||
return new Product($this->product);
|
||||
}
|
||||
|
||||
public function get_duration($format = true) {
|
||||
$style = function($time) {
|
||||
return $time;
|
||||
};
|
||||
if($format) {
|
||||
$style = function($time) {
|
||||
if($time) {
|
||||
return gmdate('Y-m-d', $time);
|
||||
}
|
||||
return $time;
|
||||
};
|
||||
}
|
||||
return array('start' => $style($this->starttime),
|
||||
'end' => '',
|
||||
'return' => $style($this->returntime));
|
||||
}
|
||||
|
||||
public function is_active() {
|
||||
if($this->returntime === null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function end() {
|
||||
$now = time();
|
||||
$query = prepare('update `service` set `returntime`=? where `id`=?');
|
||||
bind($query, 'ii', $now, $this->id);
|
||||
execute($query);
|
||||
$this->returntime = $now;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
?>
|
@ -148,29 +148,5 @@ class User {
|
||||
}
|
||||
return $overdue;
|
||||
}
|
||||
|
||||
public function create_loan($product, $endtime) {
|
||||
$find = prepare('select * from `loan`
|
||||
where `product`=? and `returntime` is null');
|
||||
$prod_id = $product->get_id();
|
||||
bind($find, 'i', $prod_id);
|
||||
execute($find);
|
||||
$loan = result_single($find);
|
||||
if($loan !== null) {
|
||||
$loan_id = $loan['id'];
|
||||
throw new Exception(
|
||||
"Product $prod_id has an active loan (id $loan_id) already.");
|
||||
}
|
||||
$now = time();
|
||||
$insert = prepare('insert into
|
||||
`loan`(`user`, `product`, `starttime`, `endtime`)
|
||||
values (?, ?, ?, ?)');
|
||||
bind($insert, 'iiii',
|
||||
$this->id, $prod_id,
|
||||
$now, strtotime($endtime . ' 13:00'));
|
||||
execute($insert);
|
||||
$loan_id = $insert->insert_id;
|
||||
return new Loan($loan_id);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
13
script.js
13
script.js
@ -434,6 +434,19 @@ function discardProduct(event) {
|
||||
ajaxRequest('discardproduct', dataListFromForm(form), render)
|
||||
}
|
||||
|
||||
function toggleService(event) {
|
||||
event.preventDefault()
|
||||
var form = event.currentTarget.parentNode
|
||||
var render = function(result) {
|
||||
if(result.type == 'success') {
|
||||
window.location.reload(false)
|
||||
} else {
|
||||
showResult(result)
|
||||
}
|
||||
}
|
||||
ajaxRequest('toggleservice', dataListFromForm(form), render)
|
||||
}
|
||||
|
||||
function searchInput(event) {
|
||||
if(event.key != "Enter") {
|
||||
return
|
||||
|
@ -84,6 +84,10 @@ td.discarded {
|
||||
background-color: #a0a0a0;
|
||||
}
|
||||
|
||||
td.service {
|
||||
background-color: #e7e08d;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
background-color: #d7e0eb;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user