<!-- ************************************************************************************
************************************************************************************
************************************************************************************ -->
+<h2>Tickets</h2>
+
+<h3>Getting Tickets</h3>
+
+The <tt>getticket</tt> transaction can be used to get information about a ticket. The request contains only the ticketID, the response looks like:<p>
+
+<pre>
+<Ticket id="ticketid" status="ticketstate" order="orderid" event="eventid" price="priceincent" orderpaystate="status of the order" />
+</pre>
+
+The attribute orderid may be <0 if the ticket is not attached to an order.<p>
+
+<table frame="1" border="1">
+<tr><td><b>Attribute</b></td><td><b>Description</b></td></tr>
+<tr><td>status</td><td>can be any of reserved, bought, used, refund, error (see getorder above for details)</td></tr>
+<tr><td>orderpaystate</td><td>the payment status of the order, it can be "none" if the order is invalid or the ticket is not attached to one, "ok" if the order has been paid correctly, "cancelled" if the order was cancelled, "needpayment" if the order is not fully paid yet, or "needrefund" if too much has been paid</td></tr>
+</table>
+
+<h3>Using Tickets</h3>
+
+The <tt>useticket</tt> transaction marks a ticket as used. The request contains only the ticket ID, the response has status ok and no body if the action succeeded, it has status error and an explanatory text if it failed.<p>
+
+Only tickets that have status "bought" and whose order have payment status "ok" or "needrefund" can be used. The transaction fails for all other tickets.
+
+<!-- ************************************************************************************
+ ************************************************************************************
+ ************************************************************************************ -->
<h2>Templates</h2>
Templates are used for printouts and documents. There are several types of templates:<p>
m_eventid=m_orderid=-1;
m_price=0;
m_status=Invalid;
+ m_paystate=PSUnknown;
+}
+
+MTicket::MTicket(MWebRequest*r,QString t)
+{
+ req=r;
+ m_eventid=m_orderid=-1;
+ m_price=0;
+ m_status=Invalid;
+ m_paystate=PSUnknown;
+ if(!req->request("getticket",t.toAscii()))return;
+ if(req->responseStatus()!=MWebRequest::Ok)return;
+ QDomDocument doc;
+ if(!doc.setContent(req->responseBody()))return;
+ scanXml(doc.documentElement());
}
MTicket::MTicket(MWebRequest*r,const QDomElement&e)
{
req=r;
+ m_paystate=PSUnknown;
+ scanXml(e);
+}
+
+void MTicket::scanXml(const QDomElement&e)
+{
m_eventid=e.attribute("event","-1").toInt();
m_orderid=e.attribute("order","-1").toInt();
m_id=e.attribute("id");
if(st=="cancelled")m_status=CheckFailCancelled;else
if(st=="invalid")m_status=CheckFailEventInvalid;
else m_status=Invalid;
+ st=e.attribute("orderpaystate");
+ if(st=="none")m_paystate=PSNone;else
+ if(st=="cancelled")m_paystate=PSCancelled;else
+ if(st=="ok")m_paystate=PSOk;else
+ if(st=="needpayment")m_paystate=PSNeedPayment;else
+ if(st=="needrefund")m_paystate=PSNeedRefund;
+ else m_paystate=PSUnknown;
}
MTicket::MTicket(const MTicket&t)
m_price=t.m_price;
m_status=t.m_status;
m_id=t.m_id;
+ m_paystate=t.m_paystate;
return *this;
}
}
}
+
+MTicket::PaymentStatus MTicket::paymentStatus()
+{
+ if(m_paystate!=PSUnknown)return m_paystate;
+ if(!isValid())return PSUnknown;
+ if(!req->request("getticket",m_id.toAscii()))return PSUnknown;
+ if(req->responseStatus()!=MWebRequest::Ok)return PSUnknown;
+ QDomDocument doc;
+ if(!doc.setContent(req->responseBody()))return PSUnknown;
+ scanXml(doc.documentElement());
+ return m_paystate;
+}
+
+QString MTicket::markUsed()
+{
+ if(!req->request("useticket",m_id.toAscii()))return QCoreApplication::translate("MTicket","Cannot execute request.");
+ if(req->responseStatus()==MWebRequest::Ok)return "";
+ return QCoreApplication::translate("MTicket",req->responseBody().data());
+}
+
void MTicket::setOrderID(qint32 o)
{
m_orderid=o;
public:
/**creates an invalid ticket*/
MTicket();
+ /**gets the ticket from the database*/
+ MTicket(MWebRequest*,QString);
/**creates a ticket from XML*/
MTicket(MWebRequest*,const QDomElement&);
/**copies a ticket*/
/**returns the ticket status as string*/
QString statusString()const;
+ /**payment state of the order*/
+ enum PaymentStatus{
+ /**yet unknown, need to retrieve it*/
+ PSUnknown,
+ /**no order state (no order)*/
+ PSNone,
+ /**order has been paid ok*/
+ PSOk,
+ /**order is cancelled*/
+ PSCancelled,
+ /**order needs payment*/
+ PSNeedPayment,
+ /**order needs refund*/
+ PSNeedRefund
+ };
+
+ /**returns the payment state of the order (may check database)*/
+ PaymentStatus paymentStatus();
+
+ /**marks the ticket as used in the database; returns empty string on success, error on failure*/
+ QString markUsed();
+
protected:
friend class MOrder;
/**sets the order-ID of the ticket, used by MOrder*/
MWebRequest*req;
qint32 m_eventid,m_orderid,m_price;
TicketStatus m_status;
+ PaymentStatus m_paystate;
QString m_id;
mutable MEvent m_event;
+
+ /**helper: scan XML data*/
+ void scanXml(const QDomElement&);
};
class MOrder
void MOverview::entranceValidate()
{
- //TODO: actually ask the server
+ //check content
+ QString tid=entrancescan->text().trimmed();
+ //avoid spurious events
+ if(tid=="")return;
+ //avoid double scans
+ QDateTime now=QDateTime::currentDateTime();
+ if(tid==lastbarcode && lastbcscan.addSecs(20)>=now){
+ entrancescan->setText("");
+ return;
+ }
+ lastbarcode=tid;
+ lastbcscan=now;
+ //ask the server
+ MTicket tick(req,tid);
+ //decide what to do
QPalette pal=entrancelabel->palette();
QPalette::ColorRole rl=QPalette::Window;
- switch(rand()%3){
- case 0:
- entrancelabel->setText(tr("Ticket Ok"));
- pal.setColor(rl,Qt::green);
- break;
- case 1:
- entrancelabel->setText(tr("Ticket Already Used"));
- pal.setColor(rl,Qt::magenta);
- break;
- case 2:
- entrancelabel->setText(tr("Ticket Not Paid"));
- pal.setColor(rl,Qt::red);
- break;
+ if(!tick.isValid()){
+ entrancelabel->setText(tr("Ticket \"%1\" Not Valid").arg(tid));
+ pal.setColor(rl,Qt::red);
+ }else
+ if(tick.status()==MTicket::Used){
+ entrancelabel->setText(tr("Ticket \"%1\" has already been used").arg(tid));
+ pal.setColor(rl,Qt::magenta);
+ }else
+ if(tick.status()!=MTicket::Bought){
+ entrancelabel->setText(tr("Ticket \"%1\" has not been bought.").arg(tid));
+ pal.setColor(rl,Qt::red);
+ }else
+ if(tick.paymentStatus()==MTicket::PSOk){
+ entrancelabel->setText(tr("Ticket \"%1\" Ok").arg(tid));
+ pal.setColor(rl,Qt::green);
+ tick.markUsed();
+ }else
+ if(tick.paymentStatus()==MTicket::PSNeedRefund){
+ entrancelabel->setText(tr("Ticket \"%1\" Ok; the Order has a refund").arg(tid));
+ pal.setColor(rl,Qt::green);
+ tick.markUsed();
+ }else
+ if(tick.paymentStatus()==MTicket::PSNeedPayment){
+ entrancelabel->setText(tr("Ticket \"%1\" is not paid for!").arg(tid));
+ pal.setColor(rl,Qt::red);
+ }else{
+ entrancelabel->setText(tr("Ticket \"%1\" cannot be accepted, please check the order!").arg(tid));
+ pal.setColor(rl,Qt::red);
}
+
entrancelabel->setPalette(pal);
entrancescan->setFocus(Qt::OtherFocusReason);
entrancescan->setText("");
if(user=="")
setWindowTitle(tr("Change my password"));
else
- setWindowTitle(tr("Reset password of user %1").arg(user));
+ setWindowTitle(tr("Reset password of user \"%1\"").arg(user));
QGridLayout*gl;
setLayout(gl=new QGridLayout);
if(user==""){
#ifndef MAGICSMOKE_OVERVIEW_H
#define MAGICSMOKE_OVERVIEW_H
-#include <QMainWindow>
-#include <QPointer>
+#include <QDateTime>
#include <QDialog>
#include <QItemDelegate>
+#include <QMainWindow>
+#include <QPointer>
#include "customer.h"
QLineEdit*entrancescan;
//cart
MCustomer customer;
+ //barcode cache
+ QString lastbarcode;
+ QDateTime lastbcscan;
};
/**Helper dialog for changing passwords*/
$xml->appendChild($doc);
print($xml->saveXml());
}
+
+ /**returns the current status of the order*/
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**helper function: returns whether the order has outstanding payments/refunds*/
+ public function getPaymentStatus()
+ {
+ global $db;
+ //calculate amount due
+ $totalprice=0;
+ $res=$db->select("ticket","ticketid","orderid=".$db->escapeInt($this->orderid));
+ if(count($res)>0)
+ foreach($res as $tc){
+ $tick=new Ticket($tc["ticketid"]);
+ if($tick->mustBePaid())$totalprice+=$tick->getPrice();
+ }
+ //TODO: add vouchers
+ //compare with what has been paid
+ if($totalprice==$this->amountpaid)return "ok";
+ if($totalprice<$this->amountpaid)return "needrefund";
+ else return "needpayment";
+ }
};
function createOrderXml($xmldata,$action)
}
}
- /**returns whether the ticket must be paid*/
+ /**returns whether the ticket must be paid (or is already paid)*/
public function mustBePaid()
{
return ($this->status & TICKET_MBLOCK) != 0;
return true;
}
+ /**dumps the ticket as XML*/
+ public function dumpXml()
+ {
+ $xml=new DomDocument;
+ $doc=$xml->createElement("Ticket");
+ $doc->setAttribute("id",$this->ticketid);
+ $doc->setAttribute("status",$this->xmlStatus());
+ $doc->setAttribute("order",$this->orderid);
+ $doc->setAttribute("event",$this->eventid);
+ $doc->setAttribute("price",$this->price);
+ $doc->setAttribute("orderpaystate",$this->orderPayStateXml());
+ $xml->appendChild($doc);
+ print($xml->saveXml());
+ }
+
+ /**helper: gets the status of the order for dumpXml*/
+ protected function orderPayStateXml()
+ {
+ if($this->orderid<0)return "none";
+ $ord=new Order($this->orderid);
+ if(!$ord->isValid())return "none";
+ if($ord->getStatus()==ORDER_CLOSED)return "ok";
+ if($ord->getStatus()==ORDER_CANCELLED)return "cancelled";
+ return $ord->getPaymentStatus();
+ }
+ /**tries to mark the ticket as used*/
+ public function markUsedXml()
+ {
+ if(!$this->isValid()){
+ header("X-MagicSmoke-Status: Error");
+ die(tr("The ticket is not valid."));
+ }
+ if($this->status==TICKET_USED){
+ header("X-MagicSmoke-Status: Error");
+ die(tr("The ticket has already been used."));
+ }
+ if($this->status!=TICKET_BOUGHT){
+ header("X-MagicSmoke-Status: Error");
+ die(tr("The ticket has not been bought or is cancelled."));
+ }
+ $ps=$this->orderPayStateXml();
+ if($ps=="needpayment"){
+ header("X-MagicSmoke-Status: Error");
+ die(tr("The ticket has not been paid."));
+ }
+ if($ps!="ok" && $ps!="needrefund"){
+ header("X-MagicSmoke-Status: Error");
+ die(tr("The tickets order is in an invalid state or does not exist."));
+ }
+ $this->status=TICKET_USED;
+ global $db;
+ $db->update("ticket",array("status"=>TICKET_USED),"ticketid=".$db->escapeInt($this->ticketid));
+ }
};
+function getTicketXml($tid)
+{
+ $tick=new Ticket($tid);
+ if($tick->isValid()){
+ header("X-MagicSmoke-Status: Ok");
+ $tick->dumpXml();
+ }else{
+ header("X-MagicSmoke-Status: Error");
+ die(tr("Unable to find this ticket."));
+ }
+}
+
+function useTicketXml($tid)
+{
+ $tick=new Ticket($tid);
+ if($tick->isValid())
+ $tick->markUsedXml();
+ else{
+ header("X-MagicSmoke-Status: Error");
+ die(tr("Unable to find this ticket."));
+ }
+}
+
?>
\ No newline at end of file
tr("getroomdata"),tr("setroomdata"),//room infos
tr("getcustomerlist"),tr("getcustomer"),tr("setcustomer"), //customer info
tr("checkorder"),tr("createorder"),tr("createsale"),tr("getorderlist"),tr("getorder"), //sell/order stuff
+ tr("getticket"),tr("useticket"),//ticket management
tr("gettemplatelist"),tr("gettemplate"),tr("settemplate") //templates
);
/**contains the low-level request name from the client*/
exit();
}
+//get a ticket
+if($SMOKEREQUEST=="getticket"){
+ getTicketXml(trim($REQUESTDATA));
+ exit();
+}
+//mark a ticket as used
+if($SMOKEREQUEST=="useticket"){
+ useTicketXml(trim($REQUESTDATA));
+ exit();
+}
//EOF
header("X-MagicSmoke-Status: Error");