From 194b5a67826c6b44e6806fefa17a7d62fedf059f Mon Sep 17 00:00:00 2001 From: konrad Date: Sat, 15 Mar 2008 21:16:04 +0000 Subject: [PATCH] order works on GUI now git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@117 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- doc/prog_protocol.html | 105 +++++++++++++- src/overview.cpp | 70 ++++++++- src/overview.h | 2 +- www/config.php.template | 2 + www/inc/classes/cart.php | 31 +++- www/inc/classes/event.php | 2 +- www/inc/classes/order.php | 355 ++++++++++++++++++++++++++++++++++++------- www/inc/classes/random.php | 16 ++ www/inc/classes/ticket.php | 149 +++++++++++++++++- www/inc/machine/session.php | 6 + www/machine.php | 10 +- 11 files changed, 669 insertions(+), 79 deletions(-) diff --git a/doc/prog_protocol.html b/doc/prog_protocol.html index 1f520b0..7663575 100644 --- a/doc/prog_protocol.html +++ b/doc/prog_protocol.html @@ -388,10 +388,111 @@ The "mail" attribute is optional - it is only reported for customers that have a

Orders and Sales

-

Creating Orders/Sales

+The order XML representation looks as follows: + +
+<Order id="orderid" customer="customerid" seller="sellerid" ordertime="timestamp" senttime="timestamp"
+    totalprice="amountInCent" paid="amountInCent" status="orderstate">
+  <Ticket event="eventid" id="ticketid" price="priceInCent" status="ticketstate" />
+  <Voucher id="voucherid" price="priceInCent" value="remainingValueInCent" />
+  <DeliveryAddress>deliver address</DeliveryAddress>
+  <Comment>comment...</Comment>
+</Order>
+
+ + + + + + + + + + + + + + + + + +
ItemDescriptionOccurrence
OrderContainer for one single order or sale1
  idID of the order, if already known.0-1
  customerID of the customer. Mandatory.1
  sellerLogin of the seller. Automatically filled in.0-1
  ordertimeTime of the order. Automatically filled in.0-1
  senttimeTime at which the order was shipped. Automatically filled in.0-1
  totalpriceTotal accumulated price of the order. Automatically filled in.0-1
  paidAmount that has already been paid. Automatically filled in.0-1
  statusCurrent status of the order. Automatically filled in. See table below.0-1
Ticket(*)data about a single ticket bought in this order0-unlimited
+(*)At least one of Ticket or Voucher must be used.

+ +If items that are not expected are filled in in a message, they will be ignored.

+ +Order status:
+ + + + + + +
StatusDescription
placedThe order has been placed, nothing has been sent or paid yet.
sentThe order has been shipped.
cancelledThe user cancelled an unshipped and unpaid order.
closedThe order is final and cannot be altered. (State maybe not needed.)

+ +Ticket status:
+ + + + + + +
StatusDescription
bought(1)The ticket is valid; whether it is paid for depends on the order status.
refund(2)The ticket has been or needs to be refunded; eg. after the event or order was cancelled.
used(1)The ticket has been used.
reservedThe ticket is reserved. This state is currently not used.
+(1)bought and used tickets add to the total price of an order; only bought tickets can be used
+(2)refunded tickets add nothing to the total price of an order, this may make the paid amount higher than the amount due.

+ +

Checking or Creating Orders/Sales

+ +The createorder transaction creates an open order. The initial state of the order is "placed".
+The createsale transaction creates an order that is marked as delivered and paid. The initial state of the created order is "sent" and the amount paid is equal to the total price.
+The checkorder transaction performs all checks and lookups of an order, but does not commit it to the database.

+ +The create* transactions fail with an error if a ticket cannot be reserved or if a voucher value is invalid. The checkorder transaction uses special state values to signify failure. It sets the price of a voucher to the nearest legal voucher value if the value is not allowed.

+ +The request is an order object without most fields filled in, the response is a copy of that object with all relevant fields filled in: + + + + + + + + +
ElementAttributes in RequestAttributes in Response
Ordercustomerid(1), customer, seller(3), ordertime(1,4), totalprice(5), paid(1), status(6), senttime(2,4)
Ticketeventevent, id(1), price, status(6)
Vouchervaluevalue, price, id(1)
DeliveryAddress--
Comment--
+(1)field is optional for checks and has no meaning there.
+(2)the field only exists for sales
+(3)the seller is automatically taken to be the session user
+(4)time stamps are automatically filled in with the current server time
+(5)the total price is the accumulated price of all tickets and vouchers in the order
+(6)the status fields have special meanings for checks, see below

+ +Order status for checks: + + + + + + +
StateDescription
ok(1)the order can be executed as is
saleonly(2)the order can only be continued as a sale, not as an open order
orderonly(2)the order can only be continued as an open order, not as a sale; this state also shows that there is a problem with the configuration
failthe order contains failed items
+(1)Attention: the return code "ok" means the order would have succeeded at the time of the check, it may still fail when it is actually placed
+(2)these states are equivalent to ok if the intended transaction matches and to fail if it does not match

+ +Ticket status for checks: + + + + + + + + + +
StateDescription
ok(1)the order can be executed as is
saleonly(2)the order can only be continued as a sale, not as an open order
orderonly(2)the order can only be continued as an open order, not as a sale; this state also shows that there is a problem with the configuration
toolatethe order is not possible any more
exhaustedthere are no more tickets for this event
cancelledthe event has been cancelled, no order is possible
invalidthe event does not exist in the database
+(1)Attention: the return code "ok" means the order would have succeeded at the time of the check, it may still fail when it is actually placed
+(2)if two or more tickets contradict on this state the order is returned as failed

-The createorder transaction creates an open order. The createsale transactions creates an order that is marked as delivered and paid. The request is an order object, the response is a copy of that object with prices filled in. (???) +

Templates

Templates are used for printouts and documents. There are several types of templates:

diff --git a/src/overview.cpp b/src/overview.cpp index b2364ae..4283f53 100644 --- a/src/overview.cpp +++ b/src/overview.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include MOverview::MOverview(MWebRequest*mw,QString pk) { @@ -59,10 +61,10 @@ MOverview::MOverview(MWebRequest*mw,QString pk) m->addAction(tr("&Show all customers"),this,SLOT(customerMgmt())); m=mb->addMenu(tr("C&art")); - m->addAction(tr("Add &Ticket")); - m->addAction(tr("Add &Voucher")); - m->addAction(tr("&Remove Item")); - m->addAction(tr("&Abort Shopping")); + m->addAction(tr("Add &Ticket"),this,SLOT(cartAddTicket())); + m->addAction(tr("Add &Voucher"),this,SLOT(cartAddVoucher()))->setEnabled(false); + m->addAction(tr("&Remove Item"),this,SLOT(cartRemoveItem())); + m->addAction(tr("&Abort Shopping"),this,SLOT(initCart())); m->addSeparator(); m->addAction(tr("&Show all orders")); @@ -674,14 +676,66 @@ void MOverview::cartRemoveItem() cartmodel->removeRow(idx.row()); } -void MOverview::cartOrder() +void MOverview::cartOrder(QString otype) { - //TODO: implement + //sanity checks + if(cartmodel->rowCount()<1){ + QMessageBox::warning(this,tr("Error"),tr("There is nothing in the order. Ignoring it.")); + return; + } + if(!customer.isValid()){ + QMessageBox::warning(this,tr("Error"),tr("Please chose a customer first!")); + return; + } + /////////////// + //create order + QDomDocument doc; + QDomElement root=doc.createElement("Order"); + root.setAttribute("customer",customer.customerID()); + //is there a delivery address + QString s=cartaddr->toPlainText().trimmed(); + if(s!=""){ + QDomElement da=doc.createElement("DeliveryAddress"); + da.appendChild(doc.createTextNode(s)); + root.appendChild(da); + } + //is there a comment? + s=cartcomment->toPlainText().trimmed(); + if(s!=""){ + QDomElement cc=doc.createElement("Comment"); + cc.appendChild(doc.createTextNode(s)); + root.appendChild(cc); + } + //scan tickets + // TODO: scan vouchers + for(int i=0;irowCount();i++){ + int amt=cartmodel->data(cartmodel->index(i,0)).toInt(); + int evid=cartmodel->data(cartmodel->index(i,0),Qt::UserRole).toInt(); + for(int j=0;jrequest(otype,doc.toByteArray())==false){ + QMessageBox::warning(this,tr("Error"),tr("The request failed.")); + return; + } + if(req->responseStatus()!=MWebRequest::Ok){ + QMessageBox::warning(this,tr("Error"),tr("A problem occurred during the order: %1").arg(tr(req->responseBody()))); + return; + } + //parse result + QDomDocument rdoc; + rdoc.setContent(req->responseBody()); + //TODO: do something with it } void MOverview::cartSell() { - //TODO: implement + cartOrder("createsale"); } void MOverview::customerMgmt() @@ -754,7 +808,7 @@ QWidget *MCartTableDelegate::createEditor(QWidget *parent, if(index.column()!=0)return 0; QSpinBox *editor = new QSpinBox(parent); - editor->setRange(0,1000); + editor->setRange(1,1000); editor->installEventFilter(const_cast(this)); return editor; } diff --git a/src/overview.h b/src/overview.h index c92c5fc..c407fd7 100644 --- a/src/overview.h +++ b/src/overview.h @@ -95,7 +95,7 @@ class MOverview:public QMainWindow /**remove item from the cart*/ void cartRemoveItem(); /**send the order to the server*/ - void cartOrder(); + void cartOrder(QString otype="createorder"); /**send the order to the server as sold*/ void cartSell(); diff --git a/www/config.php.template b/www/config.php.template index b9f7e43..4142f64 100644 --- a/www/config.php.template +++ b/www/config.php.template @@ -22,6 +22,8 @@ $db->setDbName("smoke"); $db->setPrefix("smoke_"); //set this to one of the supported MySQL storage engines for DB creation $db->setStorageEngine("InnoDB"); +//set default character set +$db->setCharacterSet("utf8"); //////////// diff --git a/www/inc/classes/cart.php b/www/inc/classes/cart.php index cec8e8d..df217e4 100644 --- a/www/inc/classes/cart.php +++ b/www/inc/classes/cart.php @@ -68,6 +68,8 @@ define("CE_EVENTNOTICKETS",11); define("CE_EVENTUNKNOWN",12); /**this error is returned if the event is already over or tickets cannot be purchased anymore*/ define("CE_EVENTOVER",13); +/**this error is returned if an invalid voucher value is ordered*/ +define("CE_INVALIDVOUCHER",20); /**instantiated by Cart::orderCheck to report errors*/ class CartError @@ -212,22 +214,39 @@ class Cart public function orderCheck() { global $db; - //TODO: extend to differentiate online, shop and direct sale + //NOTE: only covers online order $ret=array(); //go through events global $db; $res=$db->select("cart_ticket","*","cartid=".$db->escapeString($this->cartid)); + $orderstop=($db->getConfig("OrderStop")+0)*3600; if(count($res)>0) foreach($res as $k=>$tc){ $evt=new Event($tc["eventid"]); - //TODO: add more checks (event over, cancelled, etc.pp.) + //check that tickets can be sold + if(!$evt->exists()) + $ret[]=new CartError(CE_EVENTUNKNOWN,$tc["eventid"]); + else if($evt->availableTicketAmount()<$tc["amount"]) $ret[]=new CartError(CE_EVENTNOTICKETS,$tc["eventid"]); + else + if(($evt->getStartTime()-$orderstop)<=time()) + $ret[]=new CartError(CE_EVENTOVER,$tc["eventid"]); + else + if($evt->isCancelled()) + $ret[]=new CartError(CE_EVENTCANCELLED,$tc["eventid"]); + else + $itemcnt++; + } + //check voucher values + $validvouchers=explode(" ",$db->getConfig("ValidVouchers")); + $res=$db->select("cart_voucher", "cvid,value", "cartid=".$db->escapeString($this->cartid)); + foreach($res as $k=>$vc){ + if(in_array("".$vc["value"],$validvouchers)) + $itmcnt++; + else + $ret[]=new CartError(CE_INVALIDVOUCHER); } - //vouchers are ok by default, just check amount - $itmcnt=count($res); - $res=$db->select("cart_voucher", "cvid", "cartid=".$db->escapeString($this->cartid)); - $itmcnt+=count($res); //check that we have something to order if($itmcnt<=0) $ret[]=new CartError(CE_NOITEMS); diff --git a/www/inc/classes/event.php b/www/inc/classes/event.php index a694347..482202a 100644 --- a/www/inc/classes/event.php +++ b/www/inc/classes/event.php @@ -141,7 +141,7 @@ class Event reset($res); if(count($res)>0) foreach($res as $tk){ - if(!($tk["status"]&TICKET_CANCELLED))$amt++; + if(!($tk["status"]&TICKET_MBLOCK))$amt++; } return $this->capacity - $amt; } diff --git a/www/inc/classes/order.php b/www/inc/classes/order.php index be8c896..6fed4e0 100644 --- a/www/inc/classes/order.php +++ b/www/inc/classes/order.php @@ -12,52 +12,60 @@ // /**an order has been placed, this flag is set when the order is filled and finalized*/ -define("ORDER_PLACED",1); +define("ORDER_PLACED",0); /**the order has been sent out (it must be placed first; direct sales are automatically sent)*/ -define("ORDER_SENT",2); +define("ORDER_SENT",1); /**the order has been cancelled by the user (this is only possible as long as no money has been paid and nothing has been sent yet)*/ -define("ORDER_CANCELLED",4); -/**the order is closed (optional: this flag means no further payment/cancellation/etc. is possible)*/ -define("ORDER_CLOSED",8); +define("ORDER_CANCELLED",2); +/**the order has been finalized; no more changes possible*/ +define("ORDER_CLOSED",10); /**this class represents an order in the database*/ class Order { - private $orderid; + //cache + private $orderid=false; private $status=false; private $customerid=false; + private $deliveryaddress=""; + private $comment=""; + private $seller=false; + private $amountpaid=0; + private $ordertime=false; + private $senttime=false; + //to be submitted + private $newtickets; + private $newvouchers; /**instantiates an existing order with the given orderid or creates a new one if orderid===false*/ public function __construct($orderid=false) { global $db; - if($orderid===false){ - //create a new one - $odr=array( - //set to default - "soldby"=>"_online", - "status" => 0, - "ordertime" => time() - ); - $this->orderid=$db->insert("order",$odr); - }else{ + if($orderid!==false){ //get it from DB $res=$db->select("order","*","orderid=".$db->escapeInt($orderid)); if(count($res)==0){ - $this->orderid=false; return; } $this->orderid=$res[0]["orderid"]; $this->status=$res[0]["status"]; $this->customerid=$res[0]["customerid"]; + $this->deliveryaddress=$res[0]["deliveryaddress"]; + $this->comment=$res[0]["comments"]; + $this->seller=$res[0]["soldby"]; + $this->amountpaid=$res[0]["amountpaid"]; + $this->ordertime=$res[0]["ordertime"]; + $this->senttime=$res[0]["senttime"]; } + $this->newtickets=array(); + $this->newvouchers=array(); } - /**returns whether the order can still be changed*/ + /**returns whether the order can still be changed; this does not affect the modify routines*/ public function canChange() { - return $this->isValid() && $this->status == 0; + return $this->status == false; } /**returns whether the order is a valid DB object*/ @@ -74,71 +82,314 @@ class Order //check myself if(!$this->canChange())return false; //get tickets - $db->beginTransaction(); $tick=$cart->getTickets(); if(count($tick)>0) foreach($tick as $k=>$tc){ - $this->addTickets($tc->getEventId(),$tc->getAmount()); + $eid=$tc->getEventId(); + $amt=$tc->getAmount(); $tc->changeAmount(0); + if(isset($this->newtickets[$eid])) + $this->newtickets[$eid]+=$amt; + else + $this->newtickets[$eid]=$amt; } //TODO: get vouchers - //done - $db->commitTransaction(); return true; } - /**adds some tickets to the order, returns ticketid or false if change is not possible*/ - public function addTickets($eventid,$amount) + /**used by XML functions: add a single ticket for an event*/ + public function addTicket($eid) { - if(!$this->canChange() || $amount <= 0)return false; - global $db; - //get event - $event=new Event($eventid); - //check that there are tickets available - if($event->availableTicketAmount()<$amount)return false; - //create ticket - $tc=array("eventid" => $eventid, - "price" => $event->getDefaultPrice(), - "status" => 0, - "orderid" => $this->orderid - ); - $ret=array(); - for($i=0;$i<$amount;$i++)$ret[]=$db->insert("ticket",$tc); - return $ret; + if(isset($this->newtickets[$eid])) + $this->newtickets[$eid]+=1; + else + $this->newtickets[$eid]=1; } - /**sets the customer of this order; returns 1 on success, false or 0 on failure*/ + /**sets the customer of this order; returns true on success, false on failure*/ public function setCustomer($cust) { global $db; if(!$this->canChange() || !$cust->isValid())return false; - return $db->update("order",array("customerid"=>$cust->getID()),"orderid=".$db->escapeInt($this->orderid)); + $this->customerid=$cust->getID(); + return true; + } + + /**sets the customer of this order; returns true on success, false on failure*/ + public function setCustomerid($cust) + { + return $this->setCustomer(new Customer($cust)); } /**places/finalizes the order; returns false on failure, true on success or if the order already was finalized()*/ - public function placeOrder() + public function placeOrder($isSale=false) { - if(!$this->canChange())return; - global $db; + //sanity check + if(!$this->canChange())return false; + if((count($this->newtickets)+count($this->newvouchers))==0)return false; + global $db,$session; $db->beginTransaction(); - //get orderstatus and correct it - $res=$db->select("order","status","orderid=".$db->escapeInt($this->orderid)); - if(count($res)==0){ - $this->orderid=false; + if(!$this->validateOrder()){ $db->rollbackTransaction(); return false; } - $db->update("order",array("status"=>ORDER_PLACED),"orderid=".$db->escapeInt($this->orderid)); + //create order $this->status=ORDER_PLACED; + if(isset($session))$usr=$session->getUser(); + else $usr=false; + $this->seller=$usr; + $this->ordertime=time(); + $this->amountpaid=0; + $this->orderid=$db->insert("order",array("customerid"=>$this->customerid,"soldby"=>$usr,"deliveryaddress"=>$this->deliveryaddress,"status"=>$this->status,"ordertime"=>$this->ordertime,"comments"=>$this->comment,"amountpaid"=>0)); + //orderid ok? + if($this->orderid===false){ + $db->rollbackTransaction(); + return false; + } + //insert tickets + $totalprice=0; + foreach($this->newtickets as $evid=>$amount){ + for($i=0;$i<$amount;$i++){ + $tick=new Ticket; + $tick->setEventId($evid); + $tick->addToOrder($this->orderid); + $totalprice+=$tick->getPrice(); + //TODO: check return code of addToOrder + } + } + //TODO: insert vouchers + //update amountpaid for sales + if($isSale){ + $db->update("order",array("amountpaid"=>$totalprice,"status"=>ORDER_SENT),"orderid=".$db->escapeInt($this->orderid)); + $this->status=ORDER_SENT; + $this->amountpaid=$totalprice; + } //end $db->commitTransaction(); return true; } + + /**validates the order against the database; returns whether it can be opened as an order; prints an order object fit for checkorder if $dumpxml is true*/ + public function validateOrder($dumpxml=false) + { + global $db; + $ret=true; + $price=0; + $ostat="ok"; + $xml=new DomDocument; + $ord=$xml->createElement("Order"); + //check customer + $res=$db->select("customer","customerid","customerid=".$db->escapeInt($this->customerid)); + if(count($res)<1){ + if($dumpxml===false)return false; + $ostat="fail"; + $ret=false; + $ord->setAttribute("customer","-1"); + }else + $ord->setAttribute("customer",$this->customerid); + //check tickets + $orderstop=($db->getConfig("OrderStop")+0)*3600; + $salestop=($db->getConfig("SaleStop")+0)*3600; + $curtime=time(); + foreach($this->newtickets as $evid => $amount){ + + $evt=new Event($evid); + if(!$evt->exists()){ + if($dumpxml===false)return false; + //create only one ticket and make it sound negative + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status","invalid"); + $ord->appendChild($ev); + $ret=false; + $ostat="fail"; + continue; + } + $stime=$evt->getStartTime(); + $estat="ok"; + if(($stime-$orderstop)<=$curtime){ + if($dumpxml===false)return false; + $estat="saleonly"; + $ret=false; + } + if(($stime-$salestop)<=$curtime) + if($estat=="ok")$estat="orderonly"; + else $estat="toolate"; + if($estat!="ok"){ + //create only one ticket and make it sound negative + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status",$estat); + $ord->appendChild($ev); + if($ostat=="ok")$ostat=$estat; + else if($ostat!=$estat)$ostat="fail"; + $ret=false; + continue; + } + if($evt->isCancelled()){ + if($dumpxml===false)return false; + //create only one ticket and make it sound negative + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status","cancelled"); + $ord->appendChild($ev); + $ret=false; + continue; + } + $avail=$evt->availableTicketAmount(); + if($avail<$amount){ + if($dumpxml===false)return false; + //create a few good ones + for($i=0;$i<$avail;$i++){ + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status","ok"); + $ev->setAttribute("price",$evt->getDefaultPrice()); + $ord->appendChild($ev); + } + //create only one bad ticket + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status","exhausted"); + $ord->appendChild($ev); + $ret=false; + continue; + } + //finally create good ones + for($i=0;$i<$amount;$i++){ + $ev=$xml->createElement("Ticket"); + $ev->setAttribute("event",$evid); + $ev->setAttribute("status","ok"); + $ev->setAttribute("price",$evt->getDefaultPrice()); + $ord->appendChild($ev); + } + } + + //TODO: check vouchers + + //add other data + if($dumpxml){ + $ord->appendChild($xml->createElement("DeliveryAddress",$this->deliveryaddress)); + $ord->appendChild($xml->createElement("Comment",$this->comment)); + $ord->setAttribute("status",$ostat); + $xml->appendChild($ord); + print($xml->saveXml()); + } + return $ret; + } + + /**returns the ID of this order or false if it is not in the database yet*/ + public function getOrderId() + { + return $this->orderid; + } + + /**dumps the whole order as XML*/ + public function dumpXml() + { + $xml=new DomDocument; + $doc=$xml->createElement("Order"); + $doc->setAttribute("id",$this->orderid); + $doc->setAttribute("customer",$this->customerid); + $doc->setAttribute("seller",$this->seller); + $doc->setAttribute("ordertime",$this->ordertime); + $doc->setAttribute("paid",$this->amountpaid); + switch($this->status){ + case ORDER_PLACED: + $doc->setAttribute("status","placed"); + break; + case ORDER_SENT: + $doc->setAttribute("status","sent"); + break; + case ORDER_CANCELLED: + $doc->setAttribute("status","cancelled"); + break; + case ORDER_CLOSED: + $doc->setAttribute("status","closed"); + break; + default: + $doc->setAttribute("status","error"); + break; + } + $doc->setAttribute("senttime",$this->senttime); + //add Tickets + $totalprice=0; + global $db; + $res=$db->select("ticket","ticketid","orderid=".$db->escapeInt($this->orderid)); + if(count($res)>0) + foreach($res as $tc){ + $tick=new Ticket($tc["ticketid"]); + $tx=$xml->createElement("Ticket"); + $tx->setAttribute("event",$tick->getEventId()); + $tx->setAttribute("id",$tick->getTicketID()); + $tx->setAttribute("price",$tick->getPrice()); + $tx->setAttribute("status",$tick->xmlStatus()); + if($tick->mustBePaid())$totalprice+=$tick->getPrice(); + $doc->appendChild($tx); + } + //TODO: add vouchers + + //add sum + $doc->setAttribute("totalprice",$totalprice); + + //add static fields + $doc->appendChild($xml->createElement("DeliveryAddress",$this->deliveryaddress)); + $doc->appendChild($xml->createElement("Comment",$this->comment)); + + //dump + $xml->appendChild($doc); + print($xml->saveXml()); + } }; -function createOrderXml($xmldata,$ispaid) +function createOrderXml($xmldata,$action) { - //parse XML data + //parse XML data and fill order object + $order=new Order; + $xml=new DomDocument; + $xml->loadXml($xmldata); + $doc=$xml->documentElement; + $cust=$doc->getAttribute("customer")+0; + $order->setCustomerId($cust); + //get tickets + foreach($doc->getElementsByTagName("Ticket") as $tc){ + $order->addTicket($tc->getAttribute("event")+0); + } + //TODO: get vouchers + + //check action + switch($action){ + case "check": + // check order + $order->validateOrder(true); + header("X-MagicSmoke-Status: Ok"); + break; + case "order": + // create order +// $order->validateOrder(true); + if($order->placeOrder()){ + header("X-MagicSmoke-Status: Ok"); + $order->dumpXml(); + }else{ + header("X-MagicSmoke-Status: Error"); + die(tr("Cannot place order, sorry.")); + } + break; + case "sell": + //create order + if($order->placeOrder(true)){ + header("X-MagicSmoke-Status: Ok"); + //finalize sale + $order->dumpXml(); + }else{ + header("X-MagicSmoke-Status: Error"); + die(tr("Cannot place order, sorry.")); + } + break; + default: + header("X-MagicSmoke-Status: Error"); + die(tr("Internal Error: unknown action.")); + } } ?> \ No newline at end of file diff --git a/www/inc/classes/random.php b/www/inc/classes/random.php index 9b19645..ff154af 100644 --- a/www/inc/classes/random.php +++ b/www/inc/classes/random.php @@ -50,4 +50,20 @@ function getSalt() return getRandom(16*4); } +/**return a new Code-39 capable ID; length is the amount of characters*/ +function getCode39ID($length) +{ + $c39="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $rnd=getRandom($length*8); + $ret=""; + for($i=0;$i<$length;$i++){ + //cut random + $r="0x".substr($rnd,$i*2,2); + $r=($r+0)&31; + //append char + $ret.=substr($c39,$r,1); + } + return $ret; +} + ?> \ No newline at end of file diff --git a/www/inc/classes/ticket.php b/www/inc/classes/ticket.php index ff3a28c..322943a 100644 --- a/www/inc/classes/ticket.php +++ b/www/inc/classes/ticket.php @@ -11,16 +11,153 @@ // // +//masks +/**mask: ticket is blocked (can be used or is used)*/ +define("TICKET_MBLOCK",0x100); + /**ticket has been reserved by a seller*/ -define("TICKET_RESERVED",1); +define("TICKET_RESERVED",0x101); /**ticket is part of an order or has been sold independently*/ -define("TICKET_SOLD",2); +define("TICKET_BOUGHT",0x102); /**ticket has been used*/ -define("TICKET_USED",4); -/**the ticket has been paid*/ -define("TICKET_PAID",8); +define("TICKET_USED",0x103); /**ticket has been cancelled by some entity*/ -define("TICKET_CANCELLED",16); +define("TICKET_CANCELLED",0x4); +/**ticket has been refunded*/ +define("TICKET_REFUND",0x4); + +class Ticket +{ + private $ticketid=false; + private $eventid=false; + private $price=false; + private $status=false; + private $reservedby=false; + private $reservetimeout=false; + private $orderid=false; + + private static $NumTicketChars=false; + + /**generates a ticket, if $ticketid is false it creates an empty ticket, if it is a string it attempts to find it in the database*/ + public function __construct($ticketid=false) + { + global $db; + if(self::$NumTicketChars===false){ + self::$NumTicketChars=$db->getConfig("TicketIDChars")+0; + if(self::$NumTicketChars<=5)self::$NumTicketChars=10; + } + if($ticketid!==false){ + $res=$db->select("ticket","*","ticketid=".$db->escapeString(strtoupper($ticketid))); + if(count($res)<1)return; + $this->ticketid=strtoupper($ticketid); + $this->eventid=$res[0]["eventid"]; + $this->price=$res[0]["price"]; + $this->status=$res[0]["status"]; + $this->reservedby=$res[0]["reservedby"]; + $this->reservetimeout=$res[0]["reservetimeout"]; + $this->orderid=$res[0]["orderid"]; + } + } + + /**returns whether this is a valid DB object*/ + public function isValid() + { + return $this->ticketid!==false; + } + + /**returns the ID of the ticket*/ + public function getTicketId() + { + return $this->ticketid; + } + + /**returns the ID of the event*/ + public function getEventId() + { + return $this->eventid; + } + + /**returns the price of the ticket*/ + public function getPrice() + { + return $this->price; + } + + /**returns the ticket status*/ + public function getStatus() + { + return $this->status; + } + + /**returns the ticket status for XML output*/ + public function xmlStatus() + { + switch($this->status){ + case TICKET_RESERVED: + return "reserved"; + case TICKET_BOUGHT: + return "bought"; + case TICKET_USED: + return "used"; + case TICKET_CANCELLED: + return "refund"; + default: + return "error"; + } + } + + /**returns whether the ticket must be paid*/ + public function mustBePaid() + { + return ($this->status & TICKET_MBLOCK) != 0; + } + + /**sets the event and copies the price from it; returns true on success*/ + public function setEventId($e) + { + global $db; + //find event + $res=$db->select("event","defaultprice","eventid=".$db->escapeInt($e)); + if(count($res)<1)return false; + $this->eventid=$e+0; + $this->price=$res[0]["defaultprice"]; + } + + /**sets the event and copies the price from it; returns true on success*/ + public function setEvent($e) + { + return $this->setEventID($e->getEventId()); + } + + /**creates the ticket in the database and adds it to the order; expects orderid as argument; returns false if it fails; it may fail if the event has not been set*/ + public function addToOrder($o) + { + global $db; + //sanity checks + if($this->ticketid!==false)return false; + if($this->eventid===false)return false; + //generate ticket ID + $db->beginTransaction(); + do{ + $tid=getCode39ID(self::$NumTicketChars); + $res=$db->select("ticket","ticketid","ticketid=".$db->escapeString($tid)); + if(count($res)==0)break; + }while(true); + //create entry + $res=$db->insert("ticket",array("ticketid"=>$tid,"eventid"=>$this->eventid, "price"=>$this->price,"status"=>TICKET_BOUGHT,"orderid"=>$o)); + if($res===false){ + $db->rollbackTransaction(); + return false; + } + $db->commitTransaction(); + $this->ticketid=$tid; + $this->status=TICKET_BOUGHT; + $this->orderid=$o; + return true; + } + + +}; ?> \ No newline at end of file diff --git a/www/inc/machine/session.php b/www/inc/machine/session.php index f509ba8..d9ebd82 100644 --- a/www/inc/machine/session.php +++ b/www/inc/machine/session.php @@ -71,6 +71,12 @@ class Session return $this->sessid!=""; } + /**returns the user name of the session*/ + public function getUser() + { + return $this->user; + } + /**returns true if the session is actually authenticated*/ public function isAuthenticated() { diff --git a/www/machine.php b/www/machine.php index 89f672d..e759177 100644 --- a/www/machine.php +++ b/www/machine.php @@ -23,7 +23,7 @@ $ALLOWEDREQUESTS=array( tr("geteventlist"),tr("geteventdata"),tr("seteventdata"), //event infos tr("getroomdata"),tr("setroomdata"),//room infos tr("getcustomerlist"),tr("getcustomer"),tr("setcustomer"), //customer info - tr("createorder"),tr("createsale"), //sell/order stuff + tr("checkorder"),tr("createorder"),tr("createsale"), //sell/order stuff tr("gettemplatelist"),tr("gettemplate"),tr("settemplate") //templates ); /**contains the low-level request name from the client*/ @@ -255,12 +255,16 @@ if($SMOKEREQUEST=="setcustomer"){ } +if($SMOKEREQUEST=="checkorder"){ + createOrderXml($REQUESTDATA,"check"); + exit(); +} if($SMOKEREQUEST=="createorder"){ - createOrderXml($REQUESTDATA,false); + createOrderXml($REQUESTDATA,"order"); exit(); } if($SMOKEREQUEST=="createsale"){ - createOrderXml($REQUESTDATA,true); + createOrderXml($REQUESTDATA,"sell"); exit(); } -- 1.7.2.5