<h2>Orders and Sales</h2>
-<h3>Creating Orders/Sales</h2>
+The order XML representation looks as follows:
+
+<pre>
+<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>
+</pre>
+
+<table frame="1" border="1">
+<tr><td><b>Item</b></td><td><b>Description</b></td><td><b>Occurrence</b></td></tr>
+
+<tr/>
+<tr><td>Order</td><td>Container for one single order or sale</td><td>1</td></tr>
+<tr><td> id</td><td>ID of the order, if already known.</td><td>0-1</td></tr>
+<tr><td> customer</td><td>ID of the customer. Mandatory.</td><td>1</td></tr>
+<tr><td> seller</td><td>Login of the seller. Automatically filled in.</td><td>0-1</td></tr>
+<tr><td> ordertime</td><td>Time of the order. Automatically filled in.</td><td>0-1</td></tr>
+<tr><td> senttime</td><td>Time at which the order was shipped. Automatically filled in.</td><td>0-1</td></tr>
+<tr><td> totalprice</td><td>Total accumulated price of the order. Automatically filled in.</td><td>0-1</td></tr>
+<tr><td> paid</td><td>Amount that has already been paid. Automatically filled in.</td><td>0-1</td></tr>
+<tr><td> status</td><td>Current status of the order. Automatically filled in. See table below.</td><td>0-1</td></tr>
+
+<tr/>
+<tr><td>Ticket(*)</td><td>data about a single ticket bought in this order</td><td>0-unlimited</td></tr>
+</table>
+(*)At least one of Ticket or Voucher must be used.<p>
+
+If items that are not expected are filled in in a message, they will be ignored.<p>
+
+Order status:<br/>
+<table frame="1" border="1">
+<tr><td><b>Status</b></td><td><b>Description</b></td></tr>
+<tr><td>placed</td><td>The order has been placed, nothing has been sent or paid yet.</td></tr>
+<tr><td>sent</td><td>The order has been shipped.</td></tr>
+<tr><td>cancelled</td><td>The user cancelled an unshipped and unpaid order.</td></tr>
+<tr><td>closed</td><td>The order is final and cannot be altered. (State maybe not needed.)</td></tr>
+</table><p/>
+
+Ticket status:<br/>
+<table frame="1" border="1">
+<tr><td><b>Status</b></td><td><b>Description</b></td></tr>
+<tr><td>bought(1)</td><td>The ticket is valid; whether it is paid for depends on the order status.</td></tr>
+<tr><td>refund(2)</td><td>The ticket has been or needs to be refunded; eg. after the event or order was cancelled.</td></tr>
+<tr><td>used(1)</td><td>The ticket has been used.</td></tr>
+<tr><td>reserved</td><td>The ticket is reserved. This state is currently not used.</td></tr>
+</table>
+(1)bought and used tickets add to the total price of an order; only bought tickets can be used<br/>
+(2)refunded tickets add nothing to the total price of an order, this may make the paid amount higher than the amount due.<p/>
+
+<h3>Checking or Creating Orders/Sales</h3>
+
+The <tt>createorder</tt> transaction creates an open order. The initial state of the order is "placed".<br/>
+The <tt>createsale</tt> 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.<br/>
+The <tt>checkorder</tt> transaction performs all checks and lookups of an order, but does not commit it to the database.<p>
+
+The <tt>create*</tt> transactions fail with an error if a ticket cannot be reserved or if a voucher value is invalid. The <tt>checkorder</tt> 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.<p>
+
+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:
+
+<table frame="1" border="1">
+<tr><td><b>Element</b></td><td><b>Attributes in Request</b></td><td><b>Attributes in Response</b></td></tr>
+<tr><td>Order</td><td>customer</td><td>id(1), customer, seller(3), ordertime(1,4), totalprice(5), paid(1), status(6), senttime(2,4)</td></tr>
+<tr><td>Ticket</td><td>event</td><td>event, id(1), price, status(6)</td></tr>
+<tr><td>Voucher</td><td>value</td><td>value, price, id(1)</td></tr>
+<tr><td>DeliveryAddress</td><td>-</td><td>-</td></tr>
+<tr><td>Comment</td><td>-</td><td>-</td></tr>
+</table>
+(1)field is optional for checks and has no meaning there.<br/>
+(2)the field only exists for sales<br/>
+(3)the seller is automatically taken to be the session user<br/>
+(4)time stamps are automatically filled in with the current server time<br/>
+(5)the total price is the accumulated price of all tickets and vouchers in the order<br/>
+(6)the status fields have special meanings for checks, see below<p>
+
+Order status for checks:
+<table frame="1" border="1">
+<tr><td><b>State</b></td><td><b>Description</b></td></tr>
+<tr><td>ok(1)</td><td>the order can be executed as is</td></tr>
+<tr><td>saleonly(2)</td><td>the order can only be continued as a sale, not as an open order</td></tr>
+<tr><td>orderonly(2)</td><td>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</td></tr>
+<tr><td>fail</td><td>the order contains failed items</td></tr>
+</table>
+(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<br/>
+(2)these states are equivalent to ok if the intended transaction matches and to fail if it does not match<p/>
+
+Ticket status for checks:
+<table frame="1" border="1">
+<tr><td><b>State</b></td><td><b>Description</b></td></tr>
+<tr><td>ok(1)</td><td>the order can be executed as is</td></tr>
+<tr><td>saleonly(2)</td><td>the order can only be continued as a sale, not as an open order</td></tr>
+<tr><td>orderonly(2)</td><td>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</td></tr>
+<tr><td>toolate</td><td>the order is not possible any more</td></tr>
+<tr><td>exhausted</td><td>there are no more tickets for this event</td></tr>
+<tr><td>cancelled</td><td>the event has been cancelled, no order is possible</td>
+<tr><td>invalid</td><td>the event does not exist in the database</td></tr>
+</table>
+(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<br/>
+(2)if two or more tickets contradict on this state the order is returned as failed<p/>
-The <tt>createorder</tt> transaction creates an open order. The <tt>createsale</tt> 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. (???)
+<!-- ***************************************** -->
<h2>Templates</h2>
Templates are used for printouts and documents. There are several types of templates:<p>
#include <QFile>
#include <QCryptographicHash>
#include <QSpinBox>
+#include <QDomDocument>
+#include <QDomElement>
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"));
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;i<cartmodel->rowCount();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;j<amt;j++){
+ QDomElement tc=doc.createElement("Ticket");
+ tc.setAttribute("event",evid);
+ root.appendChild(tc);
+ }
+ }
+ doc.appendChild(root);
+ //send
+ if(req->request(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()
if(index.column()!=0)return 0;
QSpinBox *editor = new QSpinBox(parent);
- editor->setRange(0,1000);
+ editor->setRange(1,1000);
editor->installEventFilter(const_cast<MCartTableDelegate*>(this));
return editor;
}
/**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();
$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");
////////////
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
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);
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;
}
//
/**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*/
//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
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
//
//
+//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
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()
{
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*/
}
+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();
}