<li>Templates:<ul>
<li><a href="template.html">Web Template Design</a></li>
<li><a href="prog_odttemplate.html">ODT Template Design</a></li>
- <li><a href="prog_tickettemplate.html">Ticket Template Design</a></li>
+ <li><a href="prog_tickettemplate.html">Ticket and Voucher Template Design</a></li>
</ul></li>
<li><a href="build_prog.html">Building the Program</a></li>
<li><a href="install_prog.html">Installing the Program</a></li>
<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" />
+ <Voucher id="voucherid" price="priceInCent" value="remainingValueInCent" used="0|1" status=""/>
<Shipping price="priceInCent" type="shippingtypeID">Shipping Comment</Shipping>
<DeliveryAddress>deliver address</DeliveryAddress>
<Comment>comment...</Comment>
<tr><td> id</td><td>ID of the voucher</td><td>1</td></tr>
<tr><td> price</td><td>Price of the voucher (adds to price of the order)</td><td>1</td></tr>
<tr><td> value</td><td>Value (in cent) of the voucher (does not add to the order, but refers to how much the voucher is worth)</td><td>1</td></tr>
+<tr><td> status</td><td>only in validation responses: result of the validation</td><td>0-1</td></tr>
+<tr><td> used</td><td>Flag whether the voucher has been used to pay for anything (assumed as 0 if not present)</td><td>0-1</td></tr>
<tr/>
<tr><td>Shipping</td><td>information about the kind of shipping used and how much it costs; if text is used it is a copy of the text from the shipping table information - it cannot be changed here</td><td>0-1</td></tr>
<tr><td> price</td><td>Price of the shipping option</td><td>1</td></tr>
<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, price(7)</td><td>event, id(1), price, status(6)</td></tr>
-<tr><td>Voucher</td><td>value, price(7)</td><td>value, price, id(1)</td></tr>
+<tr><td>Voucher</td><td>value, price(7)</td><td>value, price, id(1), status(11)</td></tr>
<tr><td>Shipping</td><td>type(8), price(9)</td><td>type(10), price, [text](10)</td></tr>
<tr><td>DeliveryAddress</td><td>[text]</td><td>[text]</td></tr>
<tr><td>Comment</td><td>[text]</td><td>[text]</td></tr>
(7)the price field will only be honoured in the request, if the user also has the privilege to use the changeticketprice transaction<br/>
(8)the shipping types that are allowed depend on whether the user has the _anyshipping privilege<br/>
(9)price is optional, if set it is ignored if the user does not have the _repriceshipping privilege<br/>
-(10)the type in the response may be -1 if an error occurred in this case the text contains the error message, otherwise it contains a copy of the shipping option text<p>
+(10)the type in the response may be -1 if an error occurred in this case the text contains the error message, otherwise it contains a copy of the shipping option text<br/>
+(11)in validation responses if the voucher contains a non-empty state it is not deemed valid by the server, the state must be one of the status constants below<p>
Order status for checks:
<table frame="1" border="1">
(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/>
+Voucher status for checks:
+<table frame="1" border="1">
+<tr><td><b>State</b></td><td><b>Description</b></td></tr>
+<tr><td>[empty]</td><td>the order can be executed as is</td></tr>
+<tr><td>invalidvalue</td><td>the user is not allowed to use this value for the voucher</td></tr>
+<tr><td>invalidprice</td><td>for this user the price must equal the value (or be ommitted)</td></tr>
+</table>
+
<h3>Getting an Overview List of Orders</h3>
The <tt>getorderlist</tt> transaction can be used to get a list of all currently stored orders. The request does not contain any data, the response looks like:<p>
<h3>Cancelling an Order</h3>
-The <tt>cancelorder</tt> transaction is used to mark an order as cancelled. It also marks all tickets inside the order as cancelled and voids any vouchers in it. This transaction is only legal for orders in the "placed" state.
+The <tt>cancelorder</tt> transaction is used to mark an order as cancelled. It also marks all tickets inside the order as cancelled and voids any vouchers in it. This transaction is only legal for orders in the "placed" state. It will fail if any tickets or vouchers have already been used.
<h3>Searching for an Order</h3>
The response contains the updated order object or just an error message.
-<h3>Creating/Changing Shipping Options</h3>
+<h3>Creating/Changing/Deleting Shipping Options</h3>
The <tt>setshipping</tt> creates or changes a shipping option. The request looks like:
<pre>
-<ShippingOption id="shippingOptionID" price="priceInCent" web="0|1" anyUser="0|1">
+<ShippingOption type="shippingOptionID" price="priceInCent" web="0|1" anyUser="0|1">
Text that describes the option
</ShippingOption>
</pre>
-If the <tt>id</tt> attribute is missing the server creates a new option. Otherwise it changes an existing one. The <tt>web</tt> attribute tells whether the option is available to customers using the web interface. The <tt>anyUser</tt> attribute tells whether the option can be used by anyone who can create an order/sale (=1) or only by users with the "_anyshipping" privilege (=0).<p>
+If the <tt>type</tt> attribute is missing the server creates a new option. Otherwise it changes an existing one. The <tt>web</tt> attribute tells whether the option is available to customers using the web interface. The <tt>anyUser</tt> attribute tells whether the option can be used by anyone who can create an order/sale (=1) or only by users with the "_anyshipping" privilege (=0).<p>
-The response is empty or contains an error message.
+The response contains the new shipping option id or contains an error message.<p>
+
+The <tt>deleteshipping</tt> transaction can be used to delete a shipping option. The request contains just the ID of the option, the response is empty.
<h3>Listing Shipping Options</h3>
<pre>
<ShippingList>
- <ShippingOption id="shippingOptionID" price="priceInCent" web="0|1" anyUser="0|1">
+ <ShippingOption type="shippingOptionID" price="priceInCent" web="0|1" anyUser="0|1">
Text that describes the option
</ShippingOption>
...
<!-- ************************************************************************************
************************************************************************************
************************************************************************************ -->
+<h2>Vouchers</h2>
+
+Rules for vouchers:
+<ul>
+<li>a voucher with price and value zero is cancelled (the used flag is ignored in this case)
+<li>any non-cancelled voucher that is unused can be cancelled
+<li>any non-cancelled voucher can be emptied
+<li>a voucher with non-zero value can be used
+</ul>
+
+<h3>Getting Valid Voucher Prices</h3>
+
+The <tt>getvoucherprices</tt> transaction can be used to retrieve allowed voucher prices. The request is empty. The response contains the allowed prices in cents, separated by spaces - it is empty if no voucher prices are configured.
+
+<h3>Using a Voucher</h3>
+
+The <tt>usevoucher</tt> transaction can be used to use a voucher for payment. The request contains the voucher-ID on the first line and the order to be paid on the second line. The response contains the amount that remains on the voucher on success or contains an error message if an error occurred.
+
+<h3>Returning/Cancelling a Voucher</h3>
+
+The <tt>cancelvoucher</tt> transaction can be used to cancel a voucher. The request contains just the voucher-ID. The response is empty or contains an error message.<p>
+
+For this transaction to succeed the voucher must not have been used. The price and the value will be reset to zero. If the voucher is already cancelled the transaction will report success.
+
+<h3>Destroying a Voucher</h3>
+
+The <tt>emptyvoucher</tt> transaction can be used to void any remaining value of the voucher. The request contains just the voucher-ID. The response is empty or contains an error message.<p>
+
+This transaction simulates buying nothing for a lot of money: it sets the voucher to used and resets the remaining value to zero. The voucher will still have a price that needs to be paid and it cannot be returned/reimbursed afterwards.<p>
+
+Only privileged users should be able to do this (if anybody at all).
+
+<h3>Getting a Voucher</h3>
+
+The <tt>getvoucher</tt> transaction can be used to retrieve information about a specific voucher. The request just contains the voucher ID, the response contains the XML representation of it (see the Voucher tag in the Order XML object for details).
+
+<!-- ************************************************************************************
+ ************************************************************************************
+ ************************************************************************************ -->
<h2>Templates</h2>
Templates are used for printouts and documents. There are several types of templates:<p>
<html>
-<title>Magic Smoke Ticket Templates</title>
+<title>Magic Smoke Label Templates</title>
<body>
-<h1>Magic Smoke Ticket Templates</h1>
+<h1>Magic Smoke Label (Ticket/Voucher) Templates</h1>
+
+Both tickets and vouchers are handled as labels by MagicSmoke.<p>
The ticket template file must be called <tt>ticket.xtt</tt>.<p>
-Templates for Tickets are ZIP archive files that contain a description of the ticket layout as XML and all necessary pictures and optionally font files. The XML description must link all other resources in the archive file to make them visible to the system.<p>
+The voucher template file must be called <tt>voucher.xtt</tt>.<p>
+
+Templates for labels are ZIP archive files that contain a description of the label layout as XML and all necessary pictures and optionally font files. The XML description must link all other resources in the archive file to make them visible to the system.<p>
The description file must be called <tt>template.xml</tt> and must be in the root directory of the archive.
<h2>XML format</h2>
-The <tt>template.xml</tt> file describes the painting operations that form the ticket. The operations are executed in the order that appears in the template file - that means an element that appears later in the file can erase an earlier element if it occupies the same position. The XML format looks like:<p>
+The <tt>template.xml</tt> file describes the painting operations that form the label. The operations are executed in the order that appears in the template file - that means an element that appears later in the file can erase an earlier element if it occupies the same position. The XML format looks like:<p>
<pre>
-<TicketTemplate unit="mm|in|px" size="11 22">
+<LabelTemplate unit="mm|in|px" size="11 22">
<LoadFont file="nameInsideArchive.ttf"/>
<Picture file="nameInsideArchive.png" size="11 22" offset="33 44" smooth="1"/>
<Text font="fontFamilyName" fontsize="sizeInPt" offset="11 22" size="33 44" align="left|right|center"
valign="top|bottom|center" >some string with @VARIABLES@ to print</Text>
<Barcode offset="11 22" size="33 44"/>
...
-</TicketTemplate>
+</LabelTemplate>
</pre>
The elements are:
<table frame="1" border="1">
<tr><td><b>Element</b></td><td><b>Description</b></td></tr>
-<tr><td>TicketTemplate</td><td>this is the document element, it describes the complete ticket</td></tr>
-<tr><td>LoadFont</td><td>loads a font file (only TTF is supported) that is stored in the template ZIP file into the internal font database - it is removed from it after the ticket is rendered</td></tr>
-<tr><td>Picture</td><td>paints a picture file that is stored in the template ZIP onto the ticket; a number of formats are supported, but it is recommended to use PNG; if size is present it is scaled first - it is recommended to scale, since different printers can have different resolutions and hence the picture will have different sizes; the default scaling algorithm is smooth</td></tr>
-<tr><td>Text</td><td>describes text that is rendered on the ticket</td></tr>
-<tr><td>Barcode</td><td>generates a code-39 barcode from the ticket-ID and scales it onto the ticket</td></tr>
+<tr><td>LabelTemplate</td><td>this is the document element, it describes the complete label (actually the current implementation ignores the name of this element)</td></tr>
+<tr><td>LoadFont</td><td>loads a font file (only TTF is supported) that is stored in the template ZIP file into the internal font database - it is removed from it after the label is rendered</td></tr>
+<tr><td>Picture</td><td>paints a picture file that is stored in the template ZIP onto the label; a number of formats are supported, but it is recommended to use PNG; if size is present it is scaled first - it is recommended to scale, since different printers can have different resolutions and hence the picture will have different sizes; the default scaling algorithm is smooth</td></tr>
+<tr><td>Text</td><td>describes text that is rendered on the label</td></tr>
+<tr><td>Barcode</td><td>generates a code-39 barcode from the BARCODE variable and scales it onto the label</td></tr>
</table><p>
The attributes are
<table frame="1" border="1">
<tr><td><b>Attribute</b></td><td><b>Description</b></td></tr>
<tr><td>unit</td><td>describes in what unit sizes and offsets are described in the template, possible values are "mm" (Millimeter), "in" (Inches); the default is "mm". All of them are allowed to use fractions.</td></tr>
-<tr><td>size</td><td>describes the size the element is scaled to (or in the case of the complete ticket: its total size), it is two positive numbers separated by a space: "width height"</td></tr>
+<tr><td>size</td><td>describes the size the element is scaled to (or in the case of the complete label: its total size), it is two positive numbers separated by a space: "width height"</td></tr>
<tr><td>offset</td><td>describes the position of the element as "X Y" coordinates. They describe the distance from the upper left corner.</td></tr>
<tr><td>file</td><td>a relative file name within the template ZIP file</td></tr>
<tr><td>smooth</td><td>for Pictures: describes whether the scaling should be done using edged (0) or smooth (1) scaling</td></tr>
<tr><td>valign</td><td>vertical alignment of the text: "top" puts the text below its offset, "bottom" places the text above of its offset, "center" puts the vertical center of the text on the offset</td></tr>
</table><p>
-Variables for Text elements are enclosed in "@" signs (eg. @VARNAME@). The following variables exist:
+Variables for Text elements are enclosed in "@" signs (eg. @VARNAME@).
+
+<h3>Ticket Variables</h3>
+
+The following variables exist for tickets:
<table frame="1" border="1">
<tr><td><b>Variable</b></td><td><b>Description</b></td></tr>
+<tr><td>BARCODE</td><td>an alias for TICKETID</td></tr>
<tr><td>TICKETID</td><td>the ID of the ticket</td></tr>
<tr><td>PRICE</td><td>the price of the ticket</td></tr>
<tr><td>ROOM</td><td>the room of the tickets event</td></tr>
<tr><td>ARTIST</td><td>the artist for this event</td></tr>
</table>
+<h3>Voucher Variables</h3>
+
+The following variables exist for vouchers:
+<table frame="1" border="1">
+<tr><td><b>Variable</b></td><td><b>Description</b></td></tr>
+<tr><td>BARCODE</td><td>an alias for VOUCHERID</td></tr>
+<tr><td>VOUCHERID</td><td>the ID of the voucher</td></tr>
+<tr><td>PRICE</td><td>the price of the voucher</td></tr>
+<tr><td>VALUE</td><td>the remaining value of the voucher (the system does not store the initial value)</td></tr>
+</table>
+
</html>
\ No newline at end of file
//
#include "eventsummary.h"
-#include "webrequest.h"
+#include "misc.h"
#include "odtrender.h"
+#include "webrequest.h"
#include <QBoxLayout>
#include <QDomDocument>
#include <QTableView>
#include <QTextBrowser>
-inline QString htmlize(QString str)
-{
- QString out;
- for(int i=0;i<str.size();i++){
- QChar c=str[i];
- ushort ci=c.unicode();
- if(c=='<')out+="<";else
- if(c=='>')out+=">";else
- if(c=='&')out+="&";else
- if(c=='\n')out+="<br/>\n";else
- if(c.isSpace()||(c.unicode()>=32&&c.unicode()<=0x7f))out+=c;
- else out+="&#"+QString::number(ci)+";";
- }
- return out;
-}
-
-
MEventSummary::MEventSummary(QWidget*par,MWebRequest*rq,int eid)
:QDialog(par)
{
--- /dev/null
+//
+// C++ Implementation: misc
+//
+// Description:
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "misc.h"
+
+#include <QCoreApplication>
+
+QString htmlize(QString str)
+{
+ QString out;
+ for(int i=0;i<str.size();i++){
+ QChar c=str[i];
+ ushort ci=c.unicode();
+ if(c=='<')out+="<";else
+ if(c=='>')out+=">";else
+ if(c=='&')out+="&";else
+ if(c=='\n')out+="<br/>\n";else
+ if(c.isSpace()||(c.unicode()>=32&&c.unicode()<=0x7f))out+=c;
+ else out+="&#"+QString::number(ci)+";";
+ }
+ return out;
+}
+
+QString cent2str(int c)
+{
+ QString ret=QCoreApplication::translate("misc","%1.%2","price with decimal dot");
+ return ret.arg(c/100).arg(c%100,2,10,QChar('0'));
+}
+
+int str2cent(QString s)
+{
+ QString p=s.replace(QCoreApplication::translate("misc",".","decimal dot in price"),".");
+ double c=s.toDouble()*100;
+ return int(c);
+}
+
+QRegExp priceRegExp()
+{
+ return QRegExp(QCoreApplication::translate("misc","[0-9]+\\.[0-9]{2}","regexp for price"));
+}
--- /dev/null
+//
+// C++ Interface: misc
+//
+// Description: miscellaneous helper functions
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_MISC_H
+#define MAGICSMOKE_MISC_H
+
+#include <QString>
+#include <QRegExp>
+
+/**converts special HTML characters into harmless &-codes, so the text can be included*/
+QString htmlize(QString str);
+
+/**converts a cent value into a localized string*/
+QString cent2str(int c);
+
+/**converts a localized string back into a cent value (must not contain spaces or extra dots)*/
+int str2cent(QString s);
+
+/**return a localized regular expression that validates prices*/
+QRegExp priceRegExp();
+
+#endif
//
//
+#include "misc.h"
#include "order.h"
#include "webrequest.h"
m_seller=o.m_seller;
m_deladdress=o.m_deladdress;
m_comment=o.m_comment;
+ m_vouchers=o.m_vouchers;
+ m_shipping=o.m_shipping;
return *this;
}
QString MOrder::totalPriceString(int off)const
{
- return cent2string(m_price+off);
+ return cent2str(m_price+off);
}
int MOrder::amountPaid()const
QString MOrder::amountPaidString(int off)const
{
- return cent2string(m_paid+off);
-}
-
-//static
-QString MOrder::cent2string(int c)
-{
- return QString::number(c/100)+QCoreApplication::translate("MOrder",".","decimal dot")+QString().sprintf("%02d",c%100);
+ return cent2str(m_paid+off);
}
bool MOrder::needsPayment()const
QString MOrder::amountToPayStr(int off)const
{
- return cent2string(amountToPay()+off);
+ return cent2str(amountToPay()+off);
}
QString MOrder::amountToRefundStr(int off)const
{
- return cent2string(amountToRefund()+off);
+ return cent2str(amountToRefund()+off);
}
bool MOrder::isSent()const
m_deladdress="";
m_comment="";
m_tickets.clear();
+ m_vouchers.clear();
+ m_shipping=MShipping();
//Basics
bool b;
m_orderid=e.attribute("id","-1").toInt(&b);
m_tickets.append(tick);
}
}
- //TODO: scan vouchers
+ nl=e.elementsByTagName("Voucher");
+ for(int i=0;i<nl.size();i++){
+ QDomElement el=nl.at(i).toElement();
+ if(el.isNull())continue;
+ MVoucher vou(req,el);
+ if(vou.isValid()){
+ vou.setOrderID(m_orderid);
+ m_vouchers.append(vou);
+ }
+ }
+ nl=e.elementsByTagName("Shipping");
+ if(nl.size()<1)m_shipping=MShipping();
+ else m_shipping=MShipping(req,nl.at(0).toElement());
}
void MOrder::makeComplete()
ntc.append(m_tickets[i]);
}
m_tickets=ntc;
- //TODO: vouchers
- //TODO: shipping info?
+ //vouchers
+ QList<MVoucher>nvc;
+ for(int i=0;i<m_vouchers.size();i++)
+ if(m_vouchers[i].xmlState()=="")
+ nvc.append(m_vouchers[i]);
+ m_vouchers=nvc;
+ //shipping info?
+ if(m_shipping.id()<0)m_shipping=MShipping();
}
/**************
return QT_TRANSLATE_NOOP("MOrder","This ticket is not part of this order.");
}
+QList<MVoucher> MOrder::vouchers(){return m_vouchers;}
+
+MShipping MOrder::shipping(){return m_shipping;}
+
+QString MOrder::sendShipping(MShipping s)
+{
+ if(!req)return QCoreApplication::translate("MOrder","Cannot query DB, don't know it.");
+ //construct XML
+ QDomDocument doc;
+ QDomElement el=doc.createElement("OrderChangeShipping");
+ el.setAttribute("orderid",m_orderid);
+ if(s.isValid()){
+ el.setAttribute("type",s.id());
+ el.setAttribute("price",s.price());
+ }
+ doc.appendChild(el);
+ //send
+ if(!req->request("orderchangeshipping",doc.toByteArray()))
+ return QCoreApplication::translate("MOrder","Cannot update shipping: error while sending.");
+ if(req->responseStatus()!=MWebRequest::Ok)
+ return QCoreApplication::translate("php::",req->responseBody());
+ QDomDocument rsp;
+ rsp.setContent(req->responseBody().trimmed());
+ parseXml(rsp.documentElement());
+ return "";
+}
+
+QString MOrder::voucherReturn(QString tid)
+{
+ for(int i=0;i<m_vouchers.size();i++){
+ if(m_vouchers[i].voucherID()==tid){
+ int op=m_vouchers[i].price();
+ QString r=m_vouchers[i].voucherReturn();
+ int np=m_vouchers[i].price();
+ m_price+=np-op;
+ return r;
+ }
+ }
+ return QT_TRANSLATE_NOOP("MOrder","This voucher is not part of this order.");
+}
+
MOrder MOrder::createOrder(QString type)
{
///////////////
root.appendChild(cc);
}
//scan tickets
- // TODO: scan vouchers
for(int i=0;i<m_tickets.size();i++){
m_tickets[i].xmlForOrder(doc,root);
}
+ //scan vouchers
+ for(int i=0;i<m_vouchers.size();i++){
+ m_vouchers[i].xmlForOrder(doc,root);
+ }
+ //shipping
+ if(m_shipping.isValid()){
+ QDomElement si=doc.createElement("Shipping");
+ si.setAttribute("price",m_shipping.price());
+ si.setAttribute("type",m_shipping.id());
+ root.appendChild(si);
+ }
+ //finalize
doc.appendChild(root);
//send
if(req->request(type,doc.toByteArray())==false){
if(m_status==Bought || m_status==Used)return m_price;
else return 0;
}
+
+
+/******************************************************************************
+ * Voucher
+ ******************************************************************************/
+
+MVoucher::MVoucher()
+{
+ req=0;
+ m_price=m_value=0;
+ m_orderid=-1;
+ isused=false;
+}
+
+MVoucher::MVoucher(MWebRequest*r,const QDomElement&e)
+{
+ req=r;
+ parseXml(e);
+}
+
+MVoucher::MVoucher(MWebRequest*r,QString v)
+{
+ req=0;
+ m_price=m_value=0;
+ m_orderid=-1;
+ isused=false;
+ if(!r->request("getvoucher",v.toUtf8()))return;
+ if(r->responseStatus()!=MWebRequest::Ok)return;
+ QDomDocument doc;
+ if(!doc.setContent(r->responseBody().trimmed()))return;
+ parseXml(doc.documentElement());
+ req=r;
+}
+
+void MVoucher::parseXml(const QDomElement&e)
+{
+ m_id=e.attribute("id");
+ m_price=e.attribute("price","0").toInt();
+ m_value=e.attribute("value","0").toInt();
+ isused=e.attribute("used","0").toInt()!=0;
+ xmlstate=e.text().trimmed();
+ m_orderid=-1;
+}
+
+MVoucher::MVoucher(const MVoucher&v)
+{
+ operator=(v);
+}
+
+MVoucher& MVoucher::operator=(const MVoucher&v)
+{
+ req=v.req;
+ m_id=v.m_id;
+ m_price=v.m_price;
+ m_value=v.m_value;
+ isused=v.isused;
+ m_orderid=v.m_orderid;
+ xmlstate=v.xmlstate;
+ return *this;
+}
+
+QString MVoucher::voucherID()const{return m_id;}
+
+int MVoucher::price()const{return m_price;}
+QString MVoucher::priceString()const{return cent2str(m_price);}
+int MVoucher::value()const{return m_value;}
+QString MVoucher::valueString()const{return cent2str(m_value);}
+
+bool MVoucher::isValid()const{return req!=0 && m_id!="";}
+
+bool MVoucher::isUsed()const{return isused;}
+
+bool MVoucher::isCancelled()const{return m_price==0 && m_value==0;}
+
+bool MVoucher::isEmpty()const{return m_value==0;}
+
+QString MVoucher::xmlState()const{return xmlstate;}
+
+QString MVoucher::statusString()const
+{
+ if(!req)return QCoreApplication::translate("MVoucher","invalid");
+ if(xmlstate!="")return QCoreApplication::translate("php::",xmlstate.toAscii());
+ if(isCancelled())return QCoreApplication::translate("MVoucher","cancelled");
+ if(isEmpty())return QCoreApplication::translate("MVoucher","empty");
+ if(isUsed())return QCoreApplication::translate("MVoucher","used");
+ else return QCoreApplication::translate("MVoucher","unused");
+}
+
+void MVoucher::setOrderID(int o){m_orderid=o;}
+
+int MVoucher::orderID()const{return m_orderid;}
+
+void MVoucher::xmlForOrder(QDomDocument&doc,QDomElement&root)
+{
+ QDomElement vx=doc.createElement("Voucher");
+ vx.setAttribute("value",m_value);
+ vx.setAttribute("price",m_price);
+ root.appendChild(vx);
+}
+
+QString MVoucher::voucherReturn()
+{
+ if(!req)return QT_TRANSLATE_NOOP("MVoucher","Voucher is not stored, can't return it.");
+ if(!req->request("cancelvoucher",m_id.toUtf8()))
+ return QT_TRANSLATE_NOOP("MVoucher","Failed to execute request");
+ if(req->responseStatus()!=MWebRequest::Ok)
+ return QString::fromUtf8(req->responseBody());
+ m_value=m_price=0;
+ return "";
+}
#ifndef MAGICSMOKE_ORDER_H
#define MAGICSMOKE_ORDER_H
-#include <QString>
-#include <QDateTime>
#include <QDate>
+#include <QDateTime>
+#include <QString>
#include "customer.h"
-
#include "event.h"
+#include "shipping.h"
class MWebRequest;
class QDomDocument;
void scanXml(const QDomElement&);
};
+/**this class represents a voucher*/
+class MVoucher
+{
+ public:
+ /**create empty/invalid voucher*/
+ MVoucher();
+ /**create voucher from XML*/
+ MVoucher(MWebRequest*,const QDomElement&);
+ /**retrieve voucher from DB*/
+ MVoucher(MWebRequest*,QString);
+ /**copy voucher*/
+ MVoucher(const MVoucher&);
+
+ /**copy voucher*/
+ MVoucher& operator=(const MVoucher&);
+
+ /**returns the ID of the voucher*/
+ QString voucherID()const;
+
+ /**returns the price in cent of the voucher (what it costs)*/
+ int price()const;
+ /**returns the price of the voucher as string*/
+ QString priceString()const;
+ /**returns the remaining value in cent of the voucher (what it is worth)*/
+ int value()const;
+ /**returns the remaining value of the voucher as string*/
+ QString valueString()const;
+
+ /**returns whether this is a valid voucher object*/
+ bool isValid()const;
+
+ /**returns whether the voucher has already been used*/
+ bool isUsed()const;
+
+ /**returns whether the voucher is cancelled*/
+ bool isCancelled()const;
+
+ /**returns whether the voucher is empty, ie. its remaining value is zero*/
+ bool isEmpty()const;
+
+ /**for order validation: returns the validation result of the voucher (empty string = ok)*/
+ QString xmlState()const;
+
+ /**returns a status string for the voucher for displaying*/
+ QString statusString()const;
+
+ /**Returns the ID of the order this voucher belongs to (only when retrieved with an order)*/
+ int orderID()const;
+
+ /**attempts to return the voucher; returns empty string on success, error message on failure*/
+ QString voucherReturn();
+
+ protected:
+ friend class MOrder;
+ /**sets the order-ID of the ticket, used by MOrder*/
+ void setOrderID(qint32);
+ /**used by createOrder to generate XML elements for each ticket*/
+ void xmlForOrder(QDomDocument&,QDomElement&);
+
+ private:
+ MWebRequest*req;
+ QString m_id;
+ qint32 m_price,m_value,m_orderid;
+ bool isused;
+ QString xmlstate;
+
+ //helper: parses XML
+ void parseXml(const QDomElement&);
+};
+
+/**this class represents a complete order*/
class MOrder
{
public:
- /**create invalid host*/
+ /**create invalid order*/
MOrder();
/**create order by id*/
MOrder(MWebRequest*,qint32);
/**used to cancel/return a ticket from this order; returns empty string on success, error message on failure*/
QString ticketReturn(QString);
+ /**used to cancel/return a voucher from this order; returns empty string on success, error message on failure*/
+ QString voucherReturn(QString);
+
+ /**returns the list of vouchers of this order*/
+ QList<MVoucher> vouchers();
+
+ /**returns the shipping method of this order*/
+ MShipping shipping();
+
+ /**sends a new shipping method to the DB and updates the order object; returns empty string on success, error string otherwise*/
+ QString sendShipping(MShipping);
+
/**create a new order in the DB; returns it*/
MOrder createOrder(QString type="createorder");
QString m_seller,m_deladdress,m_comment;
bool m_complete;
QList<MTicket> m_tickets;
+ QList<MVoucher> m_vouchers;
+ MShipping m_shipping;
/**internal: requests the order from the database*/
void getFromDB(qint32);
/**internal: parse XML*/
void parseXml(const QDomElement&);
- /**helper: converts a cent value into a localized string*/
- static QString cent2string(int);
-
/**internal: makes sure the order is completely retrieved*/
void makeComplete();
};
#include <QApplication>
#include <QBoxLayout>
#include <QComboBox>
+#include <QDoubleSpinBox>
#include <QFileDialog>
#include <QGridLayout>
#include <QInputDialog>
m->addAction(tr("&Mark Order as Shipped..."),this,SLOT(shipOrder()))
->setEnabled(req->hasRole("ordershipped"));
m->addSeparator();
- m->addAction(tr("Ch&ange Ticket-Price..."),this,SLOT(changeTicket()))
+ m->addAction(tr("Ch&ange Item-Price..."),this,SLOT(changeItem()))
->setEnabled(req->hasRole("changeticketprice"));
- m->addAction(tr("&Return Ticket..."),this,SLOT(ticketReturn()))
+ m->addAction(tr("&Return Item..."),this,SLOT(itemReturn()))
->setEnabled(req->hasRole("ticketreturn"));
m->addAction(tr("Change Commen&t..."),this,SLOT(changeComment()))
->setEnabled(req->hasRole("setordercomment"));
+ m->addAction(tr("Change Sh&ipping Method..."),this,SLOT(changeShipping()))
+ ->setEnabled(req->hasRole("changeordershipping"));
m->addSeparator();
m->addAction(tr("&Close"),this,SLOT(close()));
m=mb->addMenu(tr("&Payment"));
m->addAction(tr("Save Bill &as file..."),this,SLOT(saveBill()));
m->addSeparator();
m->addAction(tr("Print &Tickets..."),this,SLOT(printTickets()));
- m->addAction(tr("Print &Current Ticket..."),this,SLOT(printCurrentTicket()));
- m->addAction(tr("&View Tickets..."),this,SLOT(ticketView()));
+ m->addAction(tr("Print V&ouchers..."),this,SLOT(printVouchers()));
+ m->addAction(tr("Print &Current Item..."),this,SLOT(printCurrentItem()));
+ m->addAction(tr("&View Items..."),this,SLOT(itemView()));
QWidget*w;
setCentralWidget(w=new QWidget);
gl->addWidget(m_paid=new QLabel(m_order.amountPaidString()),rw,1);
gl->addWidget(new QLabel(tr("Order State:")),++rw,0);
gl->addWidget(m_state=new QLabel(m_order.orderStatusString()),rw,1);
+ gl->addWidget(new QLabel(tr("Shipping Method:")),++rw,0);
+ gl->addWidget(m_shipmeth=new QLabel(m_order.shipping().description()),rw,1);
+ gl->addWidget(new QLabel(tr("Shipping Costs:")),++rw,0);
+ gl->addWidget(m_shipprice=new QLabel(m_order.shipping().priceString()),rw,1);
gl->addWidget(new QLabel(tr("Order Comment:")),++rw,0);
gl->addWidget(m_comment=lab=new QLabel(m_order.comment()),rw,1);
lab->setWordWrap(true);
QSize sz=size();
vl->addSpacing(10);
- vl->addWidget(m_tickettable=new QTableView,10);
- m_tickettable->setModel(m_ticketmodel=new QStandardItemModel(this));
- m_tickettable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ vl->addWidget(m_table=new QTableView,10);
+ m_table->setModel(m_model=new QStandardItemModel(this));
+ m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
updateTable();
//make sure everything is visible
- QSize vsz=m_tickettable->maximumViewportSize();
+ QSize vsz=m_table->maximumViewportSize();
vsz.setWidth(vsz.width()+40);
if(sz.width()<vsz.width())sz.setWidth(vsz.width());
sz.setHeight(sz.height()+vsz.height()+40);
req->dataDir();
}
+static const int ITEM_TICKET=1;
+static const int ITEM_VOUCHER=2;
+
void MOrderWindow::updateTable()
{
QList<MTicket> tickets=m_order.tickets();
QList<MEvent> events;
if(tickets.size()>0)
events=req->getAllEvents();
- m_ticketmodel->clear();
- m_ticketmodel->setHorizontalHeaderLabels(QStringList()<<tr("Ticket ID")<<tr("Event")<<tr("Start Time")<<tr("Status")<<tr("Price"));
- m_ticketmodel->insertRows(0,tickets.size());
+ QList<MVoucher> vouchers=m_order.vouchers();
+ m_model->clear();
+ m_model->setHorizontalHeaderLabels(QStringList()<<tr("Item ID")<<tr("Description")<<tr("Start Time")<<tr("Status")<<tr("Price"));
+ m_model->insertRows(0,tickets.size()+vouchers.size());
for(int i=0;i<tickets.size();i++){
- m_ticketmodel->setData(m_ticketmodel->index(i,0),tickets[i].ticketID());
- m_ticketmodel->setData(m_ticketmodel->index(i,3),tickets[i].statusString());
- m_ticketmodel->setData(m_ticketmodel->index(i,4),tickets[i].priceString());
+ m_model->setData(m_model->index(i,0),tickets[i].ticketID());
+ m_model->setData(m_model->index(i,0),ITEM_TICKET,Qt::UserRole);
+ m_model->setData(m_model->index(i,3),tickets[i].statusString());
+ m_model->setData(m_model->index(i,4),tickets[i].priceString());
//find event
MEvent ev;int eid=tickets[i].eventID();
for(int j=0;j<events.size();j++)
if(events[j].eventId()==eid)
ev=events[j];
//render remainder
- m_ticketmodel->setData(m_ticketmodel->index(i,1),ev.title());
- m_ticketmodel->setData(m_ticketmodel->index(i,2),ev.startTimeString());
+ m_model->setData(m_model->index(i,1),ev.title());
+ m_model->setData(m_model->index(i,2),ev.startTimeString());
+ }
+ int off=tickets.size();
+ for(int i=0;i<vouchers.size();i++){
+ m_model->setData(m_model->index(i+off,0),ITEM_VOUCHER,Qt::UserRole);
+ m_model->setData(m_model->index(i+off,0),vouchers[i].voucherID());
+ m_model->setData(m_model->index(i+off,1),tr("Voucher (current value: %1)").arg(vouchers[i].valueString()));
+ m_model->setData(m_model->index(i+off,3),vouchers[i].statusString());
+ m_model->setData(m_model->index(i+off,4),vouchers[i].priceString());
}
- m_tickettable->resizeColumnsToContents();
+ m_table->resizeColumnsToContents();
}
void MOrderWindow::setChanged()
return m_changed;
}
-void MOrderWindow::ticketView()
+void MOrderWindow::itemView()
{
QList<MTicket>tickets=m_order.tickets();
- if(tickets.size()<1)return;
- MTicketView tv(this,tickets);
+ QList<MVoucher>vouchers=m_order.vouchers();
+ if(tickets.size()<1 && vouchers.size()<1)return;
+ MOrderItemView tv(this,req,tickets,vouchers);
tv.exec();
}
-void MOrderWindow::printCurrentTicket()
+void MOrderWindow::printCurrentItem()
{
- //get current ticketid
- QModelIndexList lst=m_tickettable->selectionModel()->selectedIndexes();
+ //get current ticketid/voucherid
+ QModelIndexList lst=m_table->selectionModel()->selectedIndexes();
if(lst.size()<1)return;
- QModelIndex idx=m_ticketmodel->index(lst[0].row(),0);
- QString id=m_ticketmodel->data(idx).toString();
+ QModelIndex idx=m_model->index(lst[0].row(),0);
+ QString id=m_model->data(idx).toString();
if(id=="")return;
- //find ticket
- QList<MTicket>tickets=m_order.tickets();
- MTicket tick;
- for(int i=0;i<tickets.size();i++)
- if(tickets[i].ticketID()==id)
- tick=tickets[i];
- if(!tick.isValid())return;
- //print
- printTickets(QList<MTicket>()<<tick);
+ int type=m_model->data(idx,Qt::UserRole).toInt();
+ //find ticket/voucher
+ if(type==ITEM_TICKET){
+ QList<MTicket>ticks;
+ QList<MTicket>tickets=m_order.tickets();
+ for(int i=0;i<tickets.size();i++)
+ if(tickets[i].ticketID()==id && tickets[i].isValid())
+ ticks<<tickets[i];
+ printTickets(ticks);
+ }else
+ if(type==ITEM_VOUCHER){
+ QList<MVoucher>vouchs;
+ QList<MVoucher>vouchers=m_order.vouchers();
+ for(int i=0;i<vouchers.size();i++)
+ if(vouchers[i].voucherID()==id && vouchers[i].isValid())
+ vouchs<<vouchers[i];
+ printVouchers(vouchs);
+ }
+
}
void MOrderWindow::printTickets()
printTickets(m_order.tickets());
}
-void MOrderWindow::printTickets(QList<MTicket> tickets)
+void MOrderWindow::printVouchers()
+{
+ printVouchers(m_order.vouchers());
+}
+
+void MOrderWindow::printTickets(QList<MTicket> ticketsin)
{
+ //reduce ticket list to usable ones
+ QList<MTicket> tickets;
+ for(int i=0;i<ticketsin.size();i++){
+ if(ticketsin[i].status()==MTicket::Bought)
+ tickets.append(ticketsin[i]);
+ }
//sanity check
- if(tickets.size()<1)return;
+ if(tickets.size()<1){
+ QMessageBox::warning(this,tr("Warning"),tr("There are no tickets left to print."));
+ return;
+ }
//get template
QString tf=req->getTemplate("ticket.xtt");
if(tf==""){
if(pd.exec()!=QDialog::Accepted)return;
//label arrangement
MTicketRenderer render(tf);
- MLabelDialog ld(this,&printer,tickets.size(),render.ticketSize(printer));
+ MLabelDialog ld(this,&printer,tickets.size(),render.labelSize(printer));
if(ld.exec()!=QDialog::Accepted)
return;
//print
}
}
+void MOrderWindow::printVouchers(QList<MVoucher> vouchersin)
+{
+ //reduce voucher list to usable ones
+ QList<MVoucher>vouchers;
+ for(int i=0;i<vouchersin.size();i++){
+ if(vouchersin[i].isValid() && vouchersin[i].value()>0 && vouchersin[i].xmlState()=="")
+ vouchers.append(vouchersin[i]);
+ }
+ //sanity check
+ if(vouchers.size()<1){
+ QMessageBox::warning(this,tr("Warning"),tr("There are no vouchers left to print."));
+ return;
+ }
+ //get template
+ QString tf=req->getTemplate("voucher.xtt");
+ if(tf==""){
+ QMessageBox::warning(this,tr("Warning"),tr("Unable to get template file (voucher.xtt). Giving up."));
+ return;
+ }
+ //get printer settings
+ QPrinter printer;
+ QPrintDialog pd(&printer,this);
+ if(pd.exec()!=QDialog::Accepted)return;
+ //label arrangement
+ MVoucherRenderer render(tf);
+ MLabelDialog ld(this,&printer,vouchers.size(),render.labelSize(printer));
+ if(ld.exec()!=QDialog::Accepted)
+ return;
+ //print
+ QPainter painter(&printer);
+ for(int i=0;i<vouchers.size();i++){
+ QPointF p=ld.labelOffset(i);
+ if(ld.labelNeedsPageTurn(i)){
+ printer.newPage();
+ }
+ render.render(vouchers[i],printer,&painter,p);
+ }
+}
+
void MOrderWindow::printBill()
{
//get template
if(vn=="COMMENT")value=m_order.comment();else
if(vn=="AMOUNTTOPAY")value=m_order.amountToPayStr(av);else
if(vn=="AMOUNTTOREFUND")value=m_order.amountToRefundStr(av);else
- if(vn=="TICKETS")value=QString::number(m_ticketmodel->rowCount()+av);
+ if(vn=="TICKETS")value=QString::number(m_model->rowCount()+av);
if(vn=="ADDRESSLINES")value=QString::number(m_order.customer().address().split("\n").size()+av);
}
void MOrderWindow::getLoopIterations(QString loopname,int&iterations)
{
- if(loopname=="TICKETS")iterations=m_ticketmodel->rowCount();
+ if(loopname=="TICKETS")iterations=m_model->rowCount();
if(loopname=="ADDRESSLINES")iterations=m_order.customer().address().split("\n").size();
}
void MOrderWindow::getLoopVariable(QString loopname,int it,QString vn,int av,QString&value)
{
if(loopname=="TICKETS"){
- if(it<0 || it>=m_ticketmodel->rowCount())return;
+ if(it<0 || it>=m_model->rowCount())return;
QList<MTicket>tickets=m_order.tickets();
bool ok;
double rt=QInputDialog::getDouble(this,tr("Enter Payment"),tr("Please enter the amount that has been paid:"),m_order.amountToPay()/100.0,0,m_order.amountToPay()/100.0,2,&ok);
if(!ok)return;
- int pay=rt*100.0;
+ int pay=int(rt*100.0);
if(pay<=0)return;
//submit
QByteArray rq=QByteArray::number(m_order.orderID())+" "+QByteArray::number(pay);
bool ok;
double rt=QInputDialog::getDouble(this,tr("Enter Refund"),tr("Please enter the amount that will be refunded:"),m_order.amountToRefund()/100.0,0,m_order.amountToRefund()/100.0,2,&ok);
if(!ok)return;
- int pay=rt*100.0;
+ int pay=int(rt*100.0);
if(pay<=0)return;
//submit
QByteArray rq=QByteArray::number(m_order.orderID())+" "+QByteArray::number(pay);
m_paid->setText(m_order.amountPaidString());
}
-void MOrderWindow::changeTicket()
+void MOrderWindow::changeItem()
{
if(!m_order.isValid())return;
//get ticket selection
- QModelIndexList lst=m_tickettable->selectionModel()->selectedIndexes();
+ QModelIndexList lst=m_table->selectionModel()->selectedIndexes();
if(lst.size()<1)return;
- QModelIndex idx=m_ticketmodel->index(lst[0].row(),0);
- QString id=m_ticketmodel->data(idx).toString();
+ QModelIndex idx=m_model->index(lst[0].row(),0);
+ QString id=m_model->data(idx).toString();
if(id=="")return;
- //find ticket
- QList<MTicket>tickets=m_order.tickets();
- MTicket tick;
- for(int i=0;i<tickets.size();i++)
- if(tickets[i].ticketID()==id)
- tick=tickets[i];
- if(!tick.isValid())return;
- //get value
- bool ok;
- double rt=QInputDialog::getDouble(this,tr("Enter Refund"),tr("Please enter the amount that will be refunded:"),tick.price()/100.0,0,1000000,2,&ok);
- if(!ok)return;
- int pay=rt*100.0;
- if(pay<0)return;
- //submit
- m_order.updateTicketPrice(tick.ticketID(),pay);
+ int type=m_model->data(idx,Qt::UserRole).toInt();
+ if(type==ITEM_TICKET){
+ //find ticket
+ QList<MTicket>tickets=m_order.tickets();
+ MTicket tick;
+ for(int i=0;i<tickets.size();i++)
+ if(tickets[i].ticketID()==id)
+ tick=tickets[i];
+ if(!tick.isValid())return;
+ //get value
+ bool ok;
+ double rt=QInputDialog::getDouble(this,tr("Enter Price"),tr("Please enter the new price for the ticket:"),tick.price()/100.0,0,1000000,2,&ok);
+ if(!ok)return;
+ int pay=int(rt*100.0);
+ if(pay<0)return;
+ //submit
+ m_order.updateTicketPrice(tick.ticketID(),pay);
+ }else{
+ QMessageBox::warning(this,tr("Warning"),tr("Cannot change this item type."));
+ return;
+ }
m_total->setText(m_order.totalPriceString());
updateTable();
}
-void MOrderWindow::ticketReturn()
+void MOrderWindow::itemReturn()
{
if(!m_order.isValid())return;
//get ticket selection
- QModelIndexList lst=m_tickettable->selectionModel()->selectedIndexes();
+ QModelIndexList lst=m_table->selectionModel()->selectedIndexes();
if(lst.size()<1)return;
- QModelIndex idx=m_ticketmodel->index(lst[0].row(),0);
- QString id=m_ticketmodel->data(idx).toString();
+ QModelIndex idx=m_model->index(lst[0].row(),0);
+ QString id=m_model->data(idx).toString();
if(id=="")return;
- //find ticket
- QList<MTicket>tickets=m_order.tickets();
- MTicket tick;
- for(int i=0;i<tickets.size();i++)
- if(tickets[i].ticketID()==id)
- tick=tickets[i];
- if(!tick.isValid())return;
- //check state
- if(tick.status()!=MTicket::Bought && tick.status()!=MTicket::Reserved){
- QMessageBox::warning(this,tr("Warning"),tr("This ticket cannot be returned, it has already been used or is in the wrong state."));
- return;
- }
- //ask nicely
- if(QMessageBox::question(this,tr("Return Ticket"),tr("Do you really want to return this ticket?"),QMessageBox::Yes|QMessageBox::No)!=QMessageBox::Yes)return;
- //submit
- QString r=m_order.ticketReturn(tick.ticketID());
- m_total->setText(m_order.totalPriceString());
- updateTable();
- if(r!="")QMessageBox::warning(this,tr("Warning"),trUtf8(r.toUtf8()));
+ int type=m_model->data(idx,Qt::UserRole).toInt();
+ if(type==ITEM_TICKET){
+ //find ticket
+ QList<MTicket>tickets=m_order.tickets();
+ MTicket tick;
+ for(int i=0;i<tickets.size();i++)
+ if(tickets[i].ticketID()==id)
+ tick=tickets[i];
+ if(!tick.isValid())return;
+ //check state
+ if(tick.status()!=MTicket::Bought && tick.status()!=MTicket::Reserved){
+ QMessageBox::warning(this,tr("Warning"),tr("This ticket cannot be returned, it has already been used or is in the wrong state."));
+ return;
+ }
+ //ask nicely
+ if(QMessageBox::question(this,tr("Return Ticket"),tr("Do you really want to return this ticket?"),QMessageBox::Yes|QMessageBox::No)!=QMessageBox::Yes)return;
+ //submit
+ QString r=m_order.ticketReturn(tick.ticketID());
+ m_total->setText(m_order.totalPriceString());
+ updateTable();
+ if(r!="")QMessageBox::warning(this,tr("Warning"),trUtf8(r.toUtf8()));
+ }else
+ if(type==ITEM_VOUCHER){
+ //find ticket
+ QList<MVoucher>vouchers=m_order.vouchers();
+ MVoucher vou;
+ for(int i=0;i<vouchers.size();i++)
+ if(vouchers[i].voucherID()==id)
+ vou=vouchers[i];
+ if(!vou.isValid())return;
+ //check state
+ if(vou.isUsed()){
+ QMessageBox::warning(this,tr("Warning"),tr("This voucher cannot be returned, it has already been used."));
+ return;
+ }
+ //ask nicely
+ if(QMessageBox::question(this,tr("Return Voucher"),tr("Do you really want to return this voucher?"),QMessageBox::Yes|QMessageBox::No)!=QMessageBox::Yes)return;
+ //submit
+ QString r=m_order.voucherReturn(vou.voucherID());
+ m_total->setText(m_order.totalPriceString());
+ updateTable();
+ if(r!="")QMessageBox::warning(this,tr("Warning"),trUtf8(r.toUtf8()));
+ }else
+ QMessageBox::warning(this,tr("Warning"),tr("Cannot return this item type."));
}
void MOrderWindow::cancelOrder()
m_comment->setText(cmt);
}
+void MOrderWindow::changeShipping()
+{
+ //create editor dialog
+ MShippingChange d(this,req,m_order.shipping());
+ //get status
+ if(d.exec()!=QDialog::Accepted)return;
+ //send to server
+ m_order.sendShipping(d.selection());
+ //reset display
+ m_shipmeth->setText(m_order.shipping().description());
+ m_shipprice->setText(m_order.shipping().priceString());
+ m_total->setText(m_order.totalPriceString());
+}
+
/*************************************************************************************/
-MTicketView::MTicketView(QWidget*w,QList<MTicket>t)
- :QDialog(w),tickets(t)
+MOrderItemView::MOrderItemView(QWidget*w,MWebRequest*r,QList<MTicket>t,QList<MVoucher>v)
+ :QDialog(w),tickets(t),vouchers(v)
{
setWindowTitle(tr("Preview Tickets"));
+ req=r;
QVBoxLayout*vl;
setLayout(vl=new QVBoxLayout);
vl->addWidget(cb=new QComboBox,0);
cb->setEditable(false);
for(int i=0;i<tickets.size();i++)
- cb->addItem(tickets[i].ticketID());
+ cb->addItem(tr("Ticket: ")+tickets[i].ticketID());
+ for(int i=0;i<vouchers.size();i++)
+ cb->addItem(tr("Voucher: ")+vouchers[i].voucherID());
vl->addWidget(disp=new QLabel,10);
- //TODO: get the real template!
- render=new MTicketRenderer("../examples/ticket.xtt");
- changeTicket(0);
- connect(cb,SIGNAL(currentIndexChanged(int)),this,SLOT(changeTicket(int)));
+ //get the templates
+ trender=new MTicketRenderer(req->getTemplate("ticket.xtt"));
+ vrender=new MVoucherRenderer(req->getTemplate("voucher.xtt"));
+ changeItem(0);
+ connect(cb,SIGNAL(currentIndexChanged(int)),this,SLOT(changeItem(int)));
}
-MTicketView::~MTicketView()
+MOrderItemView::~MOrderItemView()
{
- delete render;
+ delete trender;
+ delete vrender;
}
-void MTicketView::changeTicket(int idx)
+void MOrderItemView::changeItem(int idx)
{
+ //ticket or voucher?
+ if(idx<tickets.size()){
+ QSizeF sz=trender->labelSize(*disp);
+ QPixmap tick(sz.toSize());
+ tick.fill();
+ if(!trender->render(tickets[idx],tick))
+ qDebug("unable to render");
+ disp->setPixmap(tick);
+ }else{
+ QSizeF sz=vrender->labelSize(*disp);
+ QPixmap tick(sz.toSize());
+ tick.fill();
+ if(!vrender->render(vouchers[idx-tickets.size()],tick))
+ qDebug("unable to render");
+ disp->setPixmap(tick);
+ }
+}
+
+
+/*************************************************************************************/
+
+MShippingChange::MShippingChange(QWidget*pa,MWebRequest*r,MShipping s)
+ :QDialog(pa)
+{
+ req=r;
+ all=req->getAllShipping();
+ setWindowTitle(tr("Change Shipping Method"));
+
+ int cid=-1;
+ if(s.isValid())cid=s.id();
+
+ QGridLayout*gl;
+ setLayout(gl=new QGridLayout);
+ gl->addWidget(new QLabel(tr("Method:")),0,0);
+ gl->addWidget(opt=new QComboBox,0,1);
+ gl->addWidget(new QLabel(tr("Price:")),1,0);
+ gl->addWidget(prc=new QDoubleSpinBox,1,1);
+ gl->setRowMinimumHeight(2,15);
+ gl->setRowStretch(2,1);
+ QHBoxLayout*hl;
+ gl->addLayout(hl=new QHBoxLayout,3,0,1,2);
+ hl->addStretch(10);
+ QPushButton*p;
+ hl->addWidget(p=new QPushButton(tr("Ok")));
+ connect(p,SIGNAL(clicked()),this,SLOT(accept()));
+ hl->addWidget(p=new QPushButton(tr("Cancel")));
+ connect(p,SIGNAL(clicked()),this,SLOT(reject()));
- QSizeF sz=render->ticketSize(*disp);
- QPixmap tick(sz.toSize());
- tick.fill();
- if(!render->render(tickets[idx],tick))
- qDebug("unable to render");
- disp->setPixmap(tick);
+ prc->setRange(0.,1.0e9);//hmm, even in Yen this should be big enough
+ prc->setDecimals(2);
+ prc->setValue(s.price()/100.);
+ prc->setEnabled(req->hasRole("orderchangeshipping"));
+
+ opt->addItem(tr("(None)","shipping method"));
+ int scid=0;
+ for(int i=0;i<all.size();i++){
+ opt->addItem(all[i].description());
+ if(all[i].id()==cid)
+ scid=i+1;
+ }
+ opt->setCurrentIndex(scid);
+ connect(opt,SIGNAL(currentIndexChanged(int)),this,SLOT(changeItem(int)));
+}
+
+MShipping MShippingChange::selection()const
+{
+ int cid=opt->currentIndex();
+ if(cid==0)return MShipping();
+ MShipping cp=all[cid-1];
+ cp.setPrice(price());
+ return cp;
+}
+
+int MShippingChange::price()const
+{
+ return int(prc->value()*100.);
+}
+
+void MShippingChange::changeItem(int cid)
+{
+ if(cid==0)prc->setValue(0);
+ else prc->setValue(double(all[cid-1].price())/100.0);
}
void updateTable();
/**internal: show the tickets as graphics*/
- void ticketView();
+ void itemView();
/**internal: print the currently selected ticket*/
- void printCurrentTicket();
+ void printCurrentItem();
/**internal: print all tickets*/
void printTickets();
/**internal helper: print list of tickets*/
void printTickets(QList<MTicket>);
+ /**internal: print all vouchers*/
+ void printVouchers();
+ /**internal helper: print list of vouchers*/
+ void printVouchers(QList<MVoucher>);
/**print a bill*/
void printBill();
/**generate a refund*/
void refund();
- /**change a ticket price*/
- void changeTicket();
- /**return a ticket*/
- void ticketReturn();
+ /**change a ticket/voucher price*/
+ void changeItem();
+ /**return a ticket/voucher*/
+ void itemReturn();
/**change the comment on the order*/
void changeComment();
+ /**change the shipping option*/
+ void changeShipping();
+
/**cancel the order*/
void cancelOrder();
/**mark as shipped*/
MWebRequest*req;
MOrder m_order;
bool m_changed;
- QLabel *m_orderid,*m_orderdate,*m_sentdate,*m_state,*m_paid,*m_total,*m_comment;
- QTableView *m_tickettable;
- QStandardItemModel *m_ticketmodel;
+ QLabel *m_orderid,*m_orderdate,*m_sentdate,*m_state,*m_paid,*m_total,*m_comment,
+ *m_shipmeth,*m_shipprice;
+ QTableView *m_table;
+ QStandardItemModel *m_model;
};
class MTicketRenderer;
+class MVoucherRenderer;
-/**helper class: displays tickets*/
-class MTicketView:public QDialog
+/**helper class: displays tickets and vouchers*/
+class MOrderItemView:public QDialog
{
Q_OBJECT
public:
- MTicketView(QWidget*,QList<MTicket>);
- ~MTicketView();
+ MOrderItemView(QWidget*,MWebRequest*,QList<MTicket>,QList<MVoucher>);
+ ~MOrderItemView();
private slots:
- void changeTicket(int);
+ void changeItem(int);
private:
+ MWebRequest*req;
QList<MTicket> tickets;
+ QList<MVoucher> vouchers;
QLabel*disp;
- MTicketRenderer*render;
+ MTicketRenderer*trender;
+ MVoucherRenderer*vrender;
+};
+
+class QDoubleSpinBox;
+class QComboBox;
+
+/**helper class: allows to change the shipping option*/
+class MShippingChange:public QDialog
+{
+ Q_OBJECT
+ public:
+ /**creates the dialog*/
+ MShippingChange(QWidget*,MWebRequest*,MShipping);
+
+ /**returns the selected shipping option, including corrected price*/
+ MShipping selection()const;
+ /**returns the entered price in cent*/
+ int price()const;
+
+ private slots:
+ /**internal: updates price when new option is selected*/
+ void changeItem(int);
+ private:
+ MWebRequest*req;
+ QList<MShipping> all;
+ QComboBox*opt;
+ QDoubleSpinBox*prc;
};
#endif
//
//
-#include "overview.h"
-#include "webrequest.h"
-#include "eventedit.h"
#include "checkdlg.h"
+#include "eventedit.h"
#include "eventsummary.h"
+#include "misc.h"
#include "orderwin.h"
+#include "overview.h"
+#include "webrequest.h"
#include <QApplication>
#include <QBoxLayout>
m=mb->addMenu(tr("C&art"));
m->addAction(tr("Add &Ticket"),this,SLOT(cartAddTicket()));
- m->addAction(tr("Add &Voucher"),this,SLOT(cartAddVoucher()))->setEnabled(false);
+ m->addAction(tr("Add &Voucher"),this,SLOT(cartAddVoucher()));
m->addAction(tr("&Remove Item"),this,SLOT(cartRemoveItem()));
m->addAction(tr("&Abort Shopping"),this,SLOT(initCart()));
+ m->addSeparator();
+ m->addAction(tr("&Update Shipping Options"),this,SLOT(updateShipping()));
m=mb->addMenu(tr("&Misc"));
- m->addAction(tr("&Return ticket..."),this,SLOT(ticketReturn()));
+ m->addAction(tr("Return &ticket..."),this,SLOT(ticketReturn()));
+ m->addAction(tr("Return &voucher..."),this,SLOT(voucherReturn()));
+ m->addSeparator();
+ m->addAction(tr("Edit &Shipping Options..."),this,SLOT(editShipping()));
m=mb->addMenu(tr("C&onfigure"));
m->addAction(tr("&Auto-Refresh settings..."),this,SLOT(setRefresh()));
connect(p,SIGNAL(clicked()),this,SLOT(cartAddTicket()));
hl2->addWidget(p=new QPushButton(tr("Add Voucher")),0);
connect(p,SIGNAL(clicked()),this,SLOT(cartAddVoucher()));
- p->setEnabled(false);
hl2->addWidget(p=new QPushButton(tr("Remove Item")),0);
connect(p,SIGNAL(clicked()),this,SLOT(cartRemoveItem()));
QFrame*frm;
vl2->addWidget(frm=new QFrame,0);
frm->setFrameShape(QFrame::HLine);
vl2->addSpacing(10);
+ vl2->addWidget(new QLabel(tr("Shipping Method:")),0);
+ vl2->addWidget(cartship=new QComboBox,0);
+ cartship->setEditable(false);
vl2->addWidget(new QLabel(tr("Delivery Address:")),0);
vl2->addWidget(cartaddr=new QTextEdit);
vl2->addSpacing(10);
}else{
initCart();
}
+ updateShipping();
if(req->hasRole("getorderlist")){
updateOrders();
}else{
}
}
+void MOverview::updateShipping()
+{
+ cartship->clear();
+ cartship->addItem(tr("(No Shipping)"),-1);
+ QList<MShipping>ship=req->getAllShipping();
+ for(int i=0;i<ship.size();i++)
+ cartship->addItem(ship[i].description(),ship[i].id());
+}
+
void MOverview::updateUsers()
{
QList<MUser>usl=req->getAllUsers();
}
-void MOverview::eventOrderTicket()
-{
- //get selected event
- int id;
- QModelIndexList ilst=eventtable->selectionModel()->selectedIndexes();
- if(ilst.size()<1)return;
- QModelIndex idx=eventmodel->index(ilst[0].row(),0);
- id=eventmodel->data(idx,Qt::UserRole).toInt();
- if(id<0)return;
- //activate order tab
- tab->setCurrentWidget(carttab);
- //insert event into table
- int cr=cartmodel->rowCount();
- cartmodel->insertRows(cr,1);
- cartmodel->setData(cartmodel->index(cr,0),1);
- cartmodel->setData(cartmodel->index(cr,0),id,Qt::UserRole);
- cartmodel->setData(cartmodel->index(cr,1),eventmodel->data(eventmodel->index(ilst[0].row(),1)));
- cartmodel->setData(cartmodel->index(cr,2),eventmodel->data(eventmodel->index(ilst[0].row(),0)));
- carttable->resizeColumnsToContents();
-}
-
void MOverview::initCart()
{
//clear cart
//clear address/comment
cartaddr->setPlainText("");
cartcomment->setPlainText("");
+ //clear shipping
+ cartship->setCurrentIndex(0);
}
void MOverview::setCustomer()
cartcustomer->setText(customer.getNameAddress());
}
+static const int CART_TICKET=1;
+static const int CART_VOUCHER=2;
+
+static const int CART_IDROLE=Qt::UserRole;//ticket id
+static const int CART_PRICEROLE=Qt::UserRole;//voucher price
+static const int CART_VALUEROLE=Qt::UserRole+1;//voucher value
+static const int CART_TYPEROLE=Qt::UserRole+2;//which is it? ticket or voucher?
+
void MOverview::cartAddTicket()
{
//create ticket selection dialog
int cr=cartmodel->rowCount();
cartmodel->insertRows(cr,1);
cartmodel->setData(cartmodel->index(cr,0),1);
- cartmodel->setData(cartmodel->index(cr,0),id,Qt::UserRole);
+ cartmodel->setData(cartmodel->index(cr,0),id,CART_IDROLE);
+ cartmodel->setData(cartmodel->index(cr,0),CART_TICKET,CART_TYPEROLE);
cartmodel->setData(cartmodel->index(cr,1),eventmodel->data(eventmodel->index(idx.row(),1)));
cartmodel->setData(cartmodel->index(cr,2),eventmodel->data(eventmodel->index(idx.row(),0)));
carttable->resizeColumnsToContents();
}
}
+void MOverview::eventOrderTicket()
+{
+ //get selected event
+ int id;
+ QModelIndexList ilst=eventtable->selectionModel()->selectedIndexes();
+ if(ilst.size()<1)return;
+ QModelIndex idx=eventmodel->index(ilst[0].row(),0);
+ id=eventmodel->data(idx,Qt::UserRole).toInt();
+ if(id<0)return;
+ //activate order tab
+ tab->setCurrentWidget(carttab);
+ //insert event into table
+ int cr=cartmodel->rowCount();
+ cartmodel->insertRows(cr,1);
+ cartmodel->setData(cartmodel->index(cr,0),1);
+ cartmodel->setData(cartmodel->index(cr,0),id,CART_IDROLE);
+ cartmodel->setData(cartmodel->index(cr,0),CART_TICKET,CART_TYPEROLE);
+ cartmodel->setData(cartmodel->index(cr,1),eventmodel->data(eventmodel->index(ilst[0].row(),1)));
+ cartmodel->setData(cartmodel->index(cr,2),eventmodel->data(eventmodel->index(ilst[0].row(),0)));
+ carttable->resizeColumnsToContents();
+}
+
+void MOverview::cartAddVoucher()
+{
+ //create voucher selection dialog
+ QDialog dlg;
+ dlg.setWindowTitle(tr("Select Voucher"));
+ QComboBox*cp,*cv;
+ QHBoxLayout*hl;
+ QGridLayout*gl;
+ QStandardItemModel mdl;
+ QRegExpValidator regv(priceRegExp(),this);
+ QList<int>prc=req->getVoucherPrices();
+ mdl.insertRows(0,prc.size());
+ mdl.insertColumns(0,1);
+ for(int i=0;i<prc.size();i++)mdl.setData(mdl.index(i,0),cent2str(prc[i]));
+ dlg.setLayout(gl=new QGridLayout);
+ gl->addWidget(new QLabel(tr("Select voucher price and value:")),0,0,1,2);
+ gl->addWidget(new QLabel(tr("Price:")),1,0);
+ gl->addWidget(cp=new QComboBox,1,1);
+ cp->setModel(&mdl);
+ cp->setEnabled(req->hasRole("_anypricevoucher"));
+ cp->setEditable(true);
+ cp->setValidator(®v);
+ gl->addWidget(new QLabel(tr("Value:")),2,0);
+ gl->addWidget(cv=new QComboBox,2,1);
+ cv->setModel(&mdl);
+ cv->setEditable(req->hasRole("_anyvoucher"));
+ cv->setValidator(®v);
+ gl->setRowMinimumHeight(3,15);
+ gl->setColumnStretch(0,0);
+ gl->setColumnStretch(1,1);
+ gl->setRowStretch(3,1);
+ gl->addLayout(hl=new QHBoxLayout,4,0,1,2);
+ hl->addStretch(10);
+ QPushButton*p;
+ hl->addWidget(p=new QPushButton(tr("Ok")),0);
+ connect(p,SIGNAL(clicked()),&dlg,SLOT(accept()));
+ hl->addWidget(p=new QPushButton(tr("Cancel")),0);
+ connect(p,SIGNAL(clicked()),&dlg,SLOT(reject()));
+ //wait for it
+ if(dlg.exec()==QDialog::Accepted){
+ //get selection
+ int price,value;
+ value=str2cent(cv->currentText());
+ if(req->hasRole("_anypricevoucher"))
+ price=str2cent(cp->currentText());
+ else
+ price=value;
+ //copy to cart
+ int cr=cartmodel->rowCount();
+ cartmodel->insertRows(cr,1);
+ cartmodel->setData(cartmodel->index(cr,0),1);
+ cartmodel->setData(cartmodel->index(cr,0),price,CART_PRICEROLE);
+ cartmodel->setData(cartmodel->index(cr,0),value,CART_VALUEROLE);
+ cartmodel->setData(cartmodel->index(cr,0),CART_VOUCHER,CART_TYPEROLE);
+ cartmodel->setData(cartmodel->index(cr,1),tr("Voucher (price: %1, value %2)").arg(cent2str(price)).arg(cent2str(value)));
+ carttable->resizeColumnsToContents();
+ }
+}
+
void MOverview::cartRemoveItem()
{
//get selection
cc.appendChild(doc.createTextNode(s));
root.appendChild(cc);
}
- //scan tickets
- // TODO: scan vouchers
+ //scan tickets & 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);
+ QModelIndex idx=cartmodel->index(i,0);
+ int tp=cartmodel->data(idx,CART_TYPEROLE).toInt();
+ if(tp==CART_TICKET){
+ int amt=cartmodel->data(idx).toInt();
+ int evid=cartmodel->data(idx,CART_IDROLE).toInt();
+ for(int j=0;j<amt;j++){
+ QDomElement tc=doc.createElement("Ticket");
+ tc.setAttribute("event",evid);
+ root.appendChild(tc);
+ }
+ }else
+ if(tp==CART_VOUCHER){
+ int amt=cartmodel->data(idx).toInt();
+ int price=cartmodel->data(idx,CART_PRICEROLE).toInt();
+ int value=cartmodel->data(idx,CART_VALUEROLE).toInt();
+ for(int j=0;j<amt;j++){
+ QDomElement tc=doc.createElement("Voucher");
+ tc.setAttribute("price",price);
+ tc.setAttribute("value",value);
+ root.appendChild(tc);
+ }
+
}
}
+ //set shipping info
+ if(cartship->currentIndex()>0){
+ QDomElement si=doc.createElement("Shipping");
+ si.setAttribute("type",cartship->itemData(cartship->currentIndex()).toInt());
+ root.appendChild(si);
+ }
+ //finalize
doc.appendChild(root);
//send
if(req->request("checkorder",doc.toByteArray())==false){
mcl.exec();
}
+void MOverview::editShipping()
+{
+ MShippingEditor se(req,this);
+ se.exec();
+ updateShipping();
+}
+
void MOverview::tabChanged()
{
QWidget*w=tab->currentWidget();
if(r!="")QMessageBox::warning(this,tr("Warning"),trUtf8(r.toUtf8()));
}
+void MOverview::voucherReturn()
+{
+ //get ticket
+ bool ok;
+ QString tid=QInputDialog::getText(this,tr("Return Voucher"),tr("Please enter the voucher ID to return:"),QLineEdit::Normal,"",&ok);
+ if(!ok || tid=="")return;
+ MVoucher vouc(req,tid);
+ if(!vouc.isValid()){
+ QMessageBox::warning(this,tr("Warning"),tr("This is not a valid voucher."));
+ return;
+ }
+ //check state
+ if(vouc.isUsed()){
+ QMessageBox::warning(this,tr("Warning"),tr("This voucher cannot be returned, it has already been used."));
+ return;
+ }
+ //submit
+ QString r=vouc.voucherReturn();
+ if(r!="")QMessageBox::warning(this,tr("Warning"),trUtf8(r.toUtf8()));
+}
+
void MOverview::refreshData()
{
QSettings set;
updateUsers();
if(set.value("refreshHosts",false).toBool() && req->hasRole("gethosts"))
updateHosts();
+ if(set.value("refreshShipping",false).toBool() && req->hasRole("getshipping"))
+ updateShipping();
}
void MOverview::setRefresh()
hl->addWidget(rate=new QSpinBox,0);
rate->setRange(1,999);
rate->setValue(set.value("refresh",300).toInt()/60);
- QCheckBox *rev,*rus,*rho;
+ QCheckBox *rev,*rus,*rho,*rsh;
vl->addWidget(rev=new QCheckBox(tr("refresh &event list")));
rev->setChecked(set.value("refreshEvents",false).toBool());
vl->addWidget(rus=new QCheckBox(tr("refresh &user list")));
rus->setChecked(set.value("refreshUsers",false).toBool());
vl->addWidget(rho=new QCheckBox(tr("refresh &host list")));
rho->setChecked(set.value("refreshHosts",false).toBool());
+ vl->addWidget(rsh=new QCheckBox(tr("refresh &shipping list")));
+ rho->setChecked(set.value("refreshShipping",false).toBool());
vl->addSpacing(15);
vl->addStretch(10);
vl->addLayout(hl=new QHBoxLayout);
set.setValue("refreshEvents",rev->isChecked());
set.setValue("refreshUsers",rus->isChecked());
set.setValue("refreshHosts",rho->isChecked());
+ set.setValue("refreshShipping",rsh->isChecked());
set.setValue("refresh",rate->value()*60);
//reset timer
rtimer.stop();
/**cancel the event*/
void eventCancel();
+ /**update shipping info*/
+ void updateShipping();
+
/**get all orders, update list*/
void updateOrders();
/**open order detail window*/
/**add a ticket to the cart*/
void cartAddTicket();
/**add a voucher to the cart*/
- //void cartAddVoucher();
+ void cartAddVoucher();
/**remove item from the cart*/
void cartRemoveItem();
/**check the order on the server*/
/**manage customers*/
void customerMgmt();
+ /**edit shipping options*/
+ void editShipping();
+
/**generic check which tab is active*/
void tabChanged();
/**return a ticket*/
void ticketReturn();
+ /**return a voucher*/
+ void voucherReturn();
/**find an order by ticket*/
void orderByTicket();
/**find/select orders by event*/
QPushButton*thishostbutton;
QLabel*cartcustomer,*entrancelabel;
QTextEdit *cartaddr,*cartcomment;
- QComboBox*ordermode;
+ QComboBox*ordermode,*cartship;
QLineEdit*entrancescan;
//event list
QAction*showoldevents;
--- /dev/null
+//
+// C++ Implementation: shipping
+//
+// Description:
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "shipping.h"
+#include "webrequest.h"
+
+#include <QApplication>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QStandardItemModel>
+#include <QTableView>
+#include <QPushButton>
+#include <QMessageBox>
+#include <QInputDialog>
+#include <QBoxLayout>
+
+MShipping::MShipping(MWebRequest*r)
+{
+ req=r;
+ m_id=-1;
+ m_price=0;
+ m_web=m_any=false;
+}
+
+MShipping::MShipping(MWebRequest*r,const QDomElement&e)
+{
+ req=r;
+ bool b;
+ m_id=e.attribute("type","-1").toInt(&b);
+ if(!b)m_id=-1;
+ m_price=e.attribute("price","0").toInt();
+ m_web=e.attribute("web","0")=="1";
+ m_any=e.attribute("anyUser","0")=="1";
+ m_descr=e.text();
+}
+MShipping::MShipping(const MShipping&s)
+{
+ req=s.req;
+ m_id=s.m_id;
+ m_price=s.m_price;
+ m_web=s.m_web;
+ m_any=s.m_any;
+ m_descr=s.m_descr;
+}
+MShipping& MShipping::operator=(const MShipping&s)
+{
+ req=s.req;
+ m_id=s.m_id;
+ m_price=s.m_price;
+ m_web=s.m_web;
+ m_any=s.m_any;
+ m_descr=s.m_descr;
+ return *this;
+}
+
+bool MShipping::isValid()const
+{
+ return req && m_id>=0;
+}
+
+int MShipping::id()const{return m_id;}
+int MShipping::price()const{return m_price;}
+
+QString MShipping::priceString()const
+{
+ return QString::number(m_price/100)+QCoreApplication::translate("MShipping",".","decimal dot")+QString().sprintf("%02d",m_price%100);
+}
+
+bool MShipping::webUsable()const{return m_web;}
+bool MShipping::anyUserCanUse()const{return m_any;}
+
+bool MShipping::canUse()const
+{
+ if(m_any)return true;
+ if(!req)return false;
+ return req->hasRole("_anyshipping");
+}
+
+QString MShipping::description()const{return m_descr;}
+
+void MShipping::setPrice(int p)
+{
+ if(p>=0)m_price=p;
+}
+
+void MShipping::setWebUsable(bool b){m_web=b;}
+
+void MShipping::setAnyUserCanUse(bool b){m_any=b;}
+
+void MShipping::setDescription(QString d){m_descr=d;}
+
+bool MShipping::save(bool asnew)
+{
+ if(!req)return false;
+ //create XML
+ QDomDocument doc;
+ QDomElement el=doc.createElement("ShippingOption");
+ if(isValid()&&!asnew)el.setAttribute("type",m_id);
+ el.setAttribute("price",m_price);
+ el.setAttribute("web",m_web?"1":"0");
+ el.setAttribute("anyUser",m_any?"1":"0");
+ el.appendChild(doc.createTextNode(m_descr));
+ doc.appendChild(el);
+ //execute request
+ if(!req->request("setshipping",doc.toByteArray()))return false;
+ if(req->responseStatus()!=MWebRequest::Ok)return false;
+ //get result
+ bool b;
+ int id=QString::fromAscii(req->responseBody()).trimmed().toInt(&b);
+ if(b && id>=0)m_id=id;
+ return true;
+}
+
+/******************************************************************************************/
+
+MShippingEditor::MShippingEditor(MWebRequest*r,QWidget*par)
+ :QDialog(par)
+{
+ req=r;
+ all=req->getAllShipping();
+ setWindowTitle(tr("Edit Shipping Options"));
+
+ QHBoxLayout*hl;
+ QVBoxLayout*vl,*vl2;
+ setLayout(vl=new QVBoxLayout);
+ vl->addLayout(hl=new QHBoxLayout,1);
+ hl->addWidget(table=new QTableView,1);
+ table->setModel(model=new QStandardItemModel(this));
+ table->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ updateTable();
+ hl->addLayout(vl2=new QVBoxLayout,0);
+ QPushButton*p;
+ vl2->addWidget(p=new QPushButton(tr("Change Description")));
+ connect(p,SIGNAL(clicked()),this,SLOT(changeDescription()));
+ vl2->addWidget(p=new QPushButton(tr("Change Price")));
+ connect(p,SIGNAL(clicked()),this,SLOT(changePrice()));
+ vl2->addWidget(p=new QPushButton(tr("Change Availability")));
+ connect(p,SIGNAL(clicked()),this,SLOT(changeAvail()));
+ vl2->addSpacing(20);
+ vl2->addWidget(p=new QPushButton(tr("Add Option")));
+ connect(p,SIGNAL(clicked()),this,SLOT(addNew()));
+ vl2->addWidget(p=new QPushButton(tr("Delete Option")));
+ connect(p,SIGNAL(clicked()),this,SLOT(deleteShip()));
+ vl2->addStretch(1);
+
+ vl->addSpacing(15);
+ vl->addLayout(hl=new QHBoxLayout,0);
+ hl->addStretch(10);
+ hl->addWidget(p=new QPushButton(tr("Ok")));
+ connect(p,SIGNAL(clicked()),this,SLOT(accept()));
+ hl->addWidget(p=new QPushButton(tr("Cancel")));
+ connect(p,SIGNAL(clicked()),this,SLOT(reject()));
+}
+void MShippingEditor::updateTable()
+{
+ model->clear();
+ model->insertColumns(0,5);
+ model->insertRows(0,all.size());
+ model->setHorizontalHeaderLabels(QStringList()<<tr("ID")<<tr("Description")<<tr("Price")<<tr("Web")<<tr("Any User"));
+ for(int i=0;i<all.size();i++){
+ model->setData(model->index(i,0),all[i].id());
+ model->setData(model->index(i,1),all[i].description());
+ model->setData(model->index(i,2),all[i].priceString());
+ model->setData(model->index(i,3),all[i].webUsable()?tr("Yes"):tr("No"));
+ model->setData(model->index(i,4),all[i].anyUserCanUse()?tr("Yes"):tr("No"));
+ }
+ table->resizeColumnsToContents();
+}
+
+void MShippingEditor::changeDescription()
+{
+ //find item
+ QModelIndexList lst=table->selectionModel()->selectedIndexes();
+ if(lst.size()<1)return;
+ QModelIndex idx=lst[0];
+ //get shipping
+ MShipping s=all[idx.row()];
+ //get new value
+ QString r=QInputDialog::getText(this,tr("Shipping Option Description"),tr("Please select a new description for this shipping option:"),QLineEdit::Normal,s.description());
+ if(r=="")return;
+ s.setDescription(r);
+ if(!s.save()){
+ QMessageBox::warning(this,tr("Warning"),tr("Could not store the changes."));
+ return;
+ }
+ all[idx.row()]=s;
+ updateTable();
+}
+
+void MShippingEditor::changePrice()
+{
+ //find item
+ QModelIndexList lst=table->selectionModel()->selectedIndexes();
+ if(lst.size()<1)return;
+ QModelIndex idx=lst[0];
+ //get shipping
+ MShipping s=all[idx.row()];
+ //get new value
+ bool b;
+ double r=QInputDialog::getDouble(this,tr("Shipping Option Price"),tr("Please select a new price for this shipping option:"),s.price()/100.,0,1e9,2,&b);
+ if(!b)return;
+ s.setPrice(int(r*100.));
+ if(!s.save()){
+ QMessageBox::warning(this,tr("Warning"),tr("Could not store the changes."));
+ return;
+ }
+ all[idx.row()]=s;
+ updateTable();
+}
+void MShippingEditor::changeAvail()
+{
+ //find item
+ QModelIndexList lst=table->selectionModel()->selectedIndexes();
+ if(lst.size()<1)return;
+ QModelIndex idx=lst[0];
+ //get shipping
+ MShipping s=all[idx.row()];
+ //get new value
+ bool b;
+ QStringList opt;
+ opt<<tr("None")<<tr("Web Interface")<<tr("Any User")<<tr("Any User + Web Interface");
+ int cav=(s.webUsable()?1:0)|(s.anyUserCanUse()?2:0);
+ QString r=QInputDialog::getItem(this,tr("Shipping Option Availability"),tr("Please select a new availability for this shipping option:"),opt,cav,false,&b);
+ if(!b)return;
+ for(int i=0;i<opt.size();i++)if(r==opt[i])cav=i;
+ s.setWebUsable(cav&1);
+ s.setAnyUserCanUse(cav&2);
+ if(!s.save()){
+ QMessageBox::warning(this,tr("Warning"),tr("Could not store the changes."));
+ return;
+ }
+ all[idx.row()]=s;
+ updateTable();
+}
+void MShippingEditor::addNew()
+{
+ //get data
+ //TODO: use a single dialog
+ QString dsc=QInputDialog::getText(this,tr("Shipping Option Description"),tr("Please select a new description for this new shipping option:"));
+ if(dsc=="")return;
+ bool b;
+ double prc=QInputDialog::getDouble(this,tr("Shipping Option Price"),tr("Please select a new price for this new shipping option:"),0.,0,1e9,2,&b);
+ if(!b)return;
+ QStringList opt;
+ opt<<tr("None")<<tr("Web Interface")<<tr("Any User")<<tr("Any User + Web Interface");
+ QString avl=QInputDialog::getItem(this,tr("Shipping Option Availability"),tr("Please select a new availability for this new shipping option:"),opt,3,false,&b);
+ if(!b)return;
+ //create the option
+ MShipping s(req);
+ s.setDescription(dsc);
+ s.setPrice(int(prc*100.));
+ int cav=4;
+ for(int i=0;i<opt.size();i++)if(avl==opt[i])cav=i;
+ s.setWebUsable(cav&1);
+ s.setAnyUserCanUse(cav&2);
+ if(!s.save()){
+ QMessageBox::warning(this,tr("Warning"),tr("Could not create the new option."));
+ return;
+ }
+ all.append(s);
+ updateTable();
+}
+
+void MShippingEditor::deleteShip()
+{
+ //find item
+ QModelIndexList lst=table->selectionModel()->selectedIndexes();
+ if(lst.size()<1)return;
+ QModelIndex idx=lst[0];
+ //get shipping
+ int id=all[idx.row()].id();
+ if(!req->request("deleteshipping",QString::number(id).toAscii())){
+ QMessageBox::warning(this,tr("Warning"),tr("Unable to delete this option."));
+ return;
+ }
+ if(req->responseStatus()!=MWebRequest::Ok){
+ QMessageBox::warning(this,tr("Warning"),tr("Unable to delete this option."));
+ return;
+ }
+ all.removeAt(idx.row());
+ updateTable();
+}
--- /dev/null
+//
+// C++ Interface: shipping
+//
+// Description:
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_SHIPPING_H
+#define MAGICSMOKE_SHIPPING_H
+
+#include <QString>
+#include <QDialog>
+
+class QDomElement;
+class MWebRequest;
+
+/**this class represents a shipping type*/
+class MShipping
+{
+ public:
+ /**creates an invalid (empty) shipping type; if the MWebRequest pointer is given the object can later be saved to the DB*/
+ MShipping(MWebRequest*r=0);
+ /**creates a shipping type from XML*/
+ MShipping(MWebRequest*,const QDomElement&);
+ /**copies a shipping type*/
+ MShipping(const MShipping&);
+ /**copies a shipping type*/
+ MShipping& operator=(const MShipping&);
+
+ /**returns whether the shipping type is valid*/
+ bool isValid()const;
+
+ /**returns the ID of the shipping type*/
+ int id()const;
+
+ /**returns the price (cent) of the type*/
+ int price()const;
+
+ /**returns the price as string*/
+ QString priceString()const;
+
+ /**returns whether the type is usable via web interface*/
+ bool webUsable()const;
+
+ /**returns whether the type is usable by any user*/
+ bool anyUserCanUse()const;
+
+ /**returns whether the currently open session/user can use it*/
+ bool canUse()const;
+
+ /**returns the description of the type*/
+ QString description()const;
+
+ /**changes the price (does not write to the DB, use save())*/
+ void setPrice(int);
+
+ /**changes whether web users can use it (does not write to the DB, use save())*/
+ void setWebUsable(bool);
+
+ /**change whether any user can use it (does not write to the DB, use save())*/
+ void setAnyUserCanUse(bool);
+
+ /**change the description (does not write to the DB, use save())*/
+ void setDescription(QString);
+
+ /**save data to the database (if the object did not come from the DB or if asnew is true it will be created); returns true on success*/
+ bool save(bool asnew=false);
+ private:
+ MWebRequest*req;
+ int m_id,m_price;
+ bool m_web,m_any;
+ QString m_descr;
+};
+
+class QStandardItemModel;
+class QTableView;
+
+class MShippingEditor:public QDialog
+{
+ Q_OBJECT
+ public:
+ MShippingEditor(MWebRequest*,QWidget*);
+
+ private slots:
+ void changeDescription();
+ void changePrice();
+ void changeAvail();
+ void addNew();
+ void deleteShip();
+ void updateTable();
+
+ private:
+ MWebRequest*req;
+ QList<MShipping>all;
+ QStandardItemModel*model;
+ QTableView*table;
+};
+
+#endif
CONFIG += qt thread
QT += xml network
-MSVERSION = "0.1 alpha"
win32-* {
#RC-File containing the icon:
SOURCES = \
main.cpp \
debug.cpp \
+ misc.cpp \
keygen.cpp \
mainwindow.cpp \
hmac.cpp \
user.cpp \
host.cpp \
order.cpp \
+ shipping.cpp \
customer.cpp \
checkdlg.cpp \
eventsummary.cpp \
odtrender.cpp \
ticketrender.cpp \
orderwin.cpp \
- labeldlg.cpp
+ labeldlg.cpp \
+ version.cpp
HEADERS = \
keygen.h \
user.h \
host.h \
order.h \
+ shipping.h \
customer.h \
checkdlg.h \
eventsummary.h \
odtrender.h \
ticketrender.h \
orderwin.h \
- labeldlg.h
+ labeldlg.h \
+ misc.h
#some PHP files are listed in this file to scan them for translatable items
#use genphpscan.sh to regenerate it.
#include "order.h"
-class MTicketRendererPrivate
+class MLabelRendererPrivate
{
protected:
- friend class MTicketRenderer;
+ friend class MLabelRenderer;
- MTicketRendererPrivate(QString file,MTicketRenderer*p);
- ~MTicketRendererPrivate();
+ MLabelRendererPrivate(QString file,MLabelRenderer*p);
+ ~MLabelRendererPrivate();
//part of the constructor
void prepare(QUnZip&);
- //called by MTicketRenderer
- bool render(const MTicket&,QPaintDevice&,QPainter*,QPointF);
+ //called by MLabelRenderer
+ bool render(const MLabel&,QPaintDevice&,QPainter*,QPointF);
- //called by MTicketRenderer
- QSizeF ticketSize(const QPaintDevice&);
+ //called by MLabelRenderer
+ QSizeF labelSize(const QPaintDevice&);
private:
- MTicketRenderer*parent;
+ MLabelRenderer*parent;
//internal font-IDs
QList<int> fdb;
//image cache:
QDomDocument txml;
//contains the unit of measurement
QString unit;
- //contains the size of the ticket in "unit"
+ //contains the size of the Label in "unit"
QSizeF tsize;
//after constructor: contains whether this object is correctly initialized
bool canrender;
//does the actual rendering work
- void render(QDomElement&,const MTicket&,QPaintDevice&,QPainter*,QPointF);
+ void render(QDomElement&,const MLabel&,QPaintDevice&,QPainter*,QPointF);
//renders a single line
- QString renderLine(const MTicket&,QString);
-
- //gets variable content from the ticket
- QString getVariable(const MTicket&,QString);
+ QString renderLine(const MLabel&,QString);
//parses element to extract offset
QPointF getoffset(QDomElement&,bool);
QSizeF tonatural(const QPaintDevice&,QSizeF);
};
-MTicketRenderer::MTicketRenderer(QString file)
+MLabelRenderer::MLabelRenderer(QString file)
{
- d=new MTicketRendererPrivate(file,this);
+ d=new MLabelRendererPrivate(file,this);
}
-MTicketRendererPrivate::MTicketRendererPrivate(QString file,MTicketRenderer*p)
+MLabelRendererPrivate::MLabelRendererPrivate(QString file,MLabelRenderer*p)
{
parent=p;
canrender=false;
prepare(temp);
temp.close();
tfile.close();
- qDebug("Ticket Renderer initialized: %s",canrender?"ready!":"error, can't render.");
+ qDebug("Label Renderer initialized: %s",canrender?"ready!":"error, can't render.");
}
-void MTicketRendererPrivate::prepare(QUnZip&temp)
+void MLabelRendererPrivate::prepare(QUnZip&temp)
{
//make sure this is a valid ZIP file
if(!temp.locateFile("template.xml")){
- qDebug("Ticket renderer: can't find template.xml");
+ qDebug("Label renderer: can't find template.xml");
canrender=false;
return;
}
buffer.open(QBuffer::ReadWrite);
temp.getCurrentFile(buffer);
if(!txml.setContent(buffer.data())){
- qDebug("Ticket renderer: can't parse template.xml - XML fault.");
+ qDebug("Label renderer: can't parse template.xml - XML fault.");
canrender=false;
return;
}
QDomElement doc=txml.documentElement();
unit=doc.attribute("unit","mm");
if(unit!="mm"&&unit!="in"){
- qDebug("Ticket renderer: illegal unit in template.xml.");
+ qDebug("Label renderer: illegal unit in template.xml.");
canrender=false;
return;
}
if(el.isNull())continue;
QString fn=el.attribute("file");
if(fn==""){
- qDebug("Ticket renderer warning: Font element without file attribute. Line %i Column %i.",el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer warning: Font element without file attribute. Line %i Column %i.",el.lineNumber(),el.columnNumber());
continue;
}
if(fndb.contains(fn)){
- qDebug("Ticket renderer warning: Font file %s was loaded more than once. Line %i Column %i.",fn.toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer warning: Font file %s was loaded more than once. Line %i Column %i.",fn.toAscii().data(),el.lineNumber(),el.columnNumber());
continue;
}
fndb.append(fn);
if(!temp.locateFile(fn)){
- qDebug("Ticket renderer warning: Font element references unknown file. Line %i Column %i.",el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer warning: Font element references unknown file. Line %i Column %i.",el.lineNumber(),el.columnNumber());
continue;
}
QBuffer buffer;
temp.getCurrentFile(buffer);
int fid=QFontDatabase::addApplicationFontFromData(buffer.data());
if(fid<0){
- qDebug("Ticket renderer warning: Font could not be loaded: %s",fn.toAscii().data());
+ qDebug("Label renderer warning: Font could not be loaded: %s",fn.toAscii().data());
continue;
}
fdb.append(fid);
if(el.isNull())continue;
QString fn=el.attribute("file");
if(fn==""){
- qDebug("Ticket renderer error: Picture element without file attribute. Line %i Column %i.",el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Picture element without file attribute. Line %i Column %i.",el.lineNumber(),el.columnNumber());
//failed pictures are fatal
canrender=false;
return;
}
if(idb.contains(fn))continue;
if(!temp.locateFile(fn)){
- qDebug("Ticket renderer error: Picture element references unknown file. Line %i Column %i.",el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Picture element references unknown file. Line %i Column %i.",el.lineNumber(),el.columnNumber());
//failed pictures are fatal
canrender=false;
return;
temp.getCurrentFile(buffer);
QImage img;
if(!img.loadFromData(buffer.data())){
- qDebug("Ticket renderer error: Picture file %s could not be interpreted.",fn.toAscii().data());
+ qDebug("Label renderer error: Picture file %s could not be interpreted.",fn.toAscii().data());
//failed pictures are fatal
canrender=false;
return;
}
}
-MTicketRenderer::~MTicketRenderer()
+MLabelRenderer::~MLabelRenderer()
{
delete d;
d=0;
}
-MTicketRendererPrivate::~MTicketRendererPrivate()
+MLabelRendererPrivate::~MLabelRendererPrivate()
{
//delete fonts
for(int i=0;i<fdb.size();i++)
QFontDatabase::removeApplicationFont(fdb[i]);
}
-bool MTicketRenderer::render(const MTicket&ticket,QPaintDevice&pdev,QPainter*painter,QPointF offset)
+bool MLabelRenderer::render(const MLabel&ticket,QPaintDevice&pdev,QPainter*painter,QPointF offset)
{
return d->render(ticket,pdev,painter,offset);
}
-bool MTicketRendererPrivate::render(const MTicket&ticket,QPaintDevice&pdev,QPainter*painter,QPointF offset)
+bool MLabelRendererPrivate::render(const MLabel&ticket,QPaintDevice&pdev,QPainter*painter,QPointF offset)
{
//sanity check
if(!canrender){
- qDebug("Ticket Renderer: render called, but can't render.");
+ qDebug("Label Renderer: render called, but can't render.");
return false;
}
//actually render
return canrender;
}
-QPointF MTicketRendererPrivate::getoffset(QDomElement&el,bool isfatal)
+QPointF MLabelRendererPrivate::getoffset(QDomElement&el,bool isfatal)
{
QStringList off=el.attribute("offset").split(" ");
if(off.size()!=2){
if(isfatal){
- qDebug("Ticket renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QPointF();
ret.setX(off[0].toDouble(&b));
if(!b){
if(isfatal){
- qDebug("Ticket renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QPointF();
ret.setY(off[1].toDouble(&b));
if(!b){
if(isfatal){
- qDebug("Ticket renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal offset in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QPointF();
return ret;
}
-QSizeF MTicketRendererPrivate::getsize(QDomElement&el,bool isfatal)
+QSizeF MLabelRendererPrivate::getsize(QDomElement&el,bool isfatal)
{
QStringList lst=el.attribute("size").split(" ");
if(lst.size()!=2){
if(isfatal){
- qDebug("Ticket renderer error: Illegal size (%i items) in %s at line %i column %i.",lst.size(),el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal size (%i items) in %s at line %i column %i.",lst.size(),el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QSizeF();
ret.setWidth(lst[0].toDouble(&b));
if(!b){
if(isfatal){
- qDebug("Ticket renderer error: Illegal size (invalid width) in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal size (invalid width) in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QSizeF();
ret.setHeight(lst[1].toDouble(&b));
if(!b){
if(isfatal){
- qDebug("Ticket renderer error: Illegal size (invalid height) in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: Illegal size (invalid height) in %s at line %i column %i.",el.tagName().toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
}
return QSizeF();
return ret;
}
-QPointF MTicketRendererPrivate::tonatural(const QPaintDevice&dev,QPointF p)
+QPointF MLabelRendererPrivate::tonatural(const QPaintDevice&dev,QPointF p)
{
double fac;
if(unit=="mm")fac=25.4;
return p;
}
-QSizeF MTicketRendererPrivate::tonatural(const QPaintDevice&dev,QSizeF s)
+QSizeF MLabelRendererPrivate::tonatural(const QPaintDevice&dev,QSizeF s)
{
double fac;
if(unit=="mm")fac=25.4;
return s;
}
-void MTicketRendererPrivate::render(QDomElement&sup, const MTicket&tick, QPaintDevice&pdev, QPainter*painter, QPointF noff)
+void MLabelRendererPrivate::render(QDomElement&sup, const MLabel&tick, QPaintDevice&pdev, QPainter*painter, QPointF noff)
{
//initialize painter
QPainter *paint;
if(!canrender){
return;
}
- QString cd=getVariable(tick,"TICKETID");
+ QString cd=tick.getVariable("BARCODE");
//paint
//TODO: find a way to switch off antialiasing
QRectF rect(tonatural(pdev,off)+noff,tonatural(pdev,sz));
paint->drawImage(rect,code39(cd));
}else{
- qDebug("Ticket renderer error: unknown element %s in ticket template at line %i column %i.",enm.toAscii().data(),el.lineNumber(),el.columnNumber());
+ qDebug("Label renderer error: unknown element %s in label template at line %i column %i.",enm.toAscii().data(),el.lineNumber(),el.columnNumber());
canrender=false;
return;
}
delete paint;
}
-QString MTicketRendererPrivate::renderLine(const MTicket&tick,QString line)
+QString MLabelRendererPrivate::renderLine(const MLabel&tick,QString line)
{
QString ret,vname;
bool isvar=false;
if(vname=="")ret+="@";
else{
//this is a variable, get value
- ret+=getVariable(tick,vname);
+ ret+=tick.getVariable(vname);
}
//reset mode
isvar=false;
return ret;
}
-QString MTicketRendererPrivate::getVariable(const MTicket&tick,QString var)
+QSizeF MLabelRenderer::labelSize(const QPaintDevice&dev)
+{
+ return d->labelSize(dev);
+}
+
+QSizeF MLabelRendererPrivate::labelSize(const QPaintDevice&dev)
+{
+ return tonatural(dev,tsize);
+}
+
+MLabel::MLabel(){}
+MLabel::~MLabel(){}
+
+class MTicketLabel:public MLabel
+{
+ public:
+ MTicketLabel(const MTicket&t):tick(t){}
+ QString getVariable(QString var)const;
+ private:
+ MTicket tick;
+};
+
+QString MTicketLabel::getVariable(QString var)const
{
- if(var=="TICKETID")return tick.ticketID();
+ if(var=="TICKETID"||var=="BARCODE")return tick.ticketID();
if(var=="PRICE")return tick.priceString();
if(var=="DATETIME")return tick.event().startTimeString();
if(var=="ROOM")return tick.event().room();
return "";
}
-QSizeF MTicketRenderer::ticketSize(const QPaintDevice&dev)
+MTicketRenderer::MTicketRenderer(QString f):MLabelRenderer(f){}
+bool MTicketRenderer::render(const MTicket&label,QPaintDevice&pdev,QPainter*painter,QPointF offset)
{
- return d->ticketSize(dev);
+ return MLabelRenderer::render(MTicketLabel(label),pdev,painter,offset);
}
-QSizeF MTicketRendererPrivate::ticketSize(const QPaintDevice&dev)
+class MVoucherLabel:public MLabel
{
- return tonatural(dev,tsize);
+ public:
+ MVoucherLabel(const MVoucher&t):vouc(t){}
+ QString getVariable(QString var)const;
+ private:
+ MVoucher vouc;
+};
+
+QString MVoucherLabel::getVariable(QString var)const
+{
+ if(var=="VOUCHERID"||var=="BARCODE")return vouc.voucherID();
+ if(var=="PRICE")return vouc.priceString();
+ if(var=="VALUE")return vouc.valueString();
+ return "";
+}
+
+MVoucherRenderer::MVoucherRenderer(QString f):MLabelRenderer(f){}
+bool MVoucherRenderer::render(const MVoucher&label,QPaintDevice&pdev,QPainter*painter,QPointF offset)
+{
+ return MLabelRenderer::render(MVoucherLabel(label),pdev,painter,offset);
}
#include <QPointF>
#include <QSizeF>
-class MTicketRendererPrivate;
+class MLabelRendererPrivate;
class MTicket;
+class MVoucher;
class QPaintDevice;
class QPainter;
-/**abstract base class for all ODT rendering classes*/
-class MTicketRenderer
+/**base class that describes labels*/
+class MLabel
+{
+ public:
+ /**constructs the label*/
+ MLabel();
+ /**deconstructs the label*/
+ virtual ~MLabel();
+
+ /**abstract: overwrite this to return data for a label*/
+ virtual QString getVariable(QString)const=0;
+};
+
+/**base class for all label rendering classes*/
+class MLabelRenderer
{
public:
/**instantiates a renderer loaded from template file*/
- MTicketRenderer(QString file);
+ MLabelRenderer(QString file);
/**deletes the renderer*/
- virtual ~MTicketRenderer();
+ virtual ~MLabelRenderer();
/**renders the ticket; returns whether the rendering was successful*/
- virtual bool render(const MTicket&ticket,QPaintDevice&pdev,QPainter*painter=0,QPointF offset=QPointF());
+ virtual bool render(const MLabel&label,QPaintDevice&pdev,QPainter*painter=0,QPointF offset=QPointF());
/**returns the size of the ticket in the coordinates of the specified paint device (used for preview)*/
- virtual QSizeF ticketSize(const QPaintDevice&);
+ virtual QSizeF labelSize(const QPaintDevice&);
protected:
- friend class MTicketRendererPrivate;
+ friend class MLabelRendererPrivate;
private:
- MTicketRendererPrivate*d;
+ MLabelRendererPrivate*d;
+};
+
+/**convenience class: renders vouchers directly*/
+class MVoucherRenderer:public MLabelRenderer
+{
+ public:
+ MVoucherRenderer(QString f);
+ bool render(const MVoucher&label,QPaintDevice&pdev,QPainter*painter=0,QPointF offset=QPointF());
};
+/**convenience class: renders vouchers directly*/
+class MTicketRenderer:public MLabelRenderer
+{
+ public:
+ MTicketRenderer(QString f);
+ bool render(const MTicket&label,QPaintDevice&pdev,QPainter*painter=0,QPointF offset=QPointF());
+};
+
+
#endif
--- /dev/null
+//
+// C++ Implementation: version information
+//
+// Description: processes version.inc to form some meaningful C++ code
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "version.h"
+
+#define defversion(x,y) const char VERSION_##x[] = #y;
+#include "../www/inc/machine/version.inc"
+#undef defversion
--- /dev/null
+
+#define defversion(x,y) extern const char VERSION_##x[];
+#include "../www/inc/machine/version.inc"
+#undef defversion
//
//
-#include "webrequest.h"
-#include "keygen.h"
#include "hmac.h"
+#include "keygen.h"
#include "main.h"
+#include "version.h"
+#include "webrequest.h"
#include <QDir>
#include <QDomDocument>
return ret;
}
+QList<MShipping> MWebRequest::getAllShipping()
+{
+ if(!hasRole("getshipping"))return QList<MShipping>();
+ errstr="";
+ if(!request("getshipping",""))return QList<MShipping>();
+ if(responseStatus()!=Ok)return QList<MShipping>();
+ //parse return document
+ QDomDocument doc;
+ QString msg;int ln,cl;
+ if(!doc.setContent(rspdata,&msg,&ln,&cl)){
+ errstr=tr("Error parsing ShippingList XML data (line %1 column %2): %3").arg(ln).arg(cl).arg(msg);
+ return QList<MShipping>();
+ }
+ QDomElement root=doc.documentElement();
+ QDomNodeList nl=root.elementsByTagName("ShippingOption");
+ QList<MShipping>ret;
+ for(int i=0;i<nl.size();i++){
+ QDomElement el=nl.at(i).toElement();
+ if(el.isNull())continue;
+ MShipping mo(this,el);
+ if(mo.isValid())ret.append(mo);
+ }
+ return ret;
+}
+
+QList<int>MWebRequest::getVoucherPrices()
+{
+ if(!hasRole("getvoucherprices"))return QList<int>();
+ errstr="";
+ if(!request("getvoucherprices",""))return QList<int>();
+ if(responseStatus()!=Ok)return QList<int>();
+ //parse return document
+ QStringList lst=QString::fromAscii(responseBody()).split(" ");
+ QList<int>ret;
+ for(int i=0;i<lst.size();i++){
+ bool b;
+ int r=lst[i].trimmed().toInt(&b);
+ if(b && !ret.contains(r))ret.append(r);
+ }
+ return ret;
+}
+
QList<MOrder> MWebRequest::getOrdersByEvents(QList<int>evids)
{
errstr="";
#ifndef MAGICSMOKE_WEBREQUEST_H
#define MAGICSMOKE_WEBREQUEST_H
-#include <QObject>
-#include <QString>
#include <QByteArray>
-#include <QPointer>
+#include <QDateTime>
#include <QHttp>
-#include <QUrl>
#include <QList>
-#include <QDateTime>
+#include <QObject>
+#include <QPointer>
+#include <QString>
+#include <QUrl>
-#include "room.h"
+#include "customer.h"
#include "event.h"
-#include "user.h"
#include "host.h"
-#include "customer.h"
#include "order.h"
+#include "room.h"
+#include "shipping.h"
+#include "user.h"
/**abstraction of requests to the web server, handles sessions and all data transfer*/
class MWebRequest:public QObject
/**returns a list of all orders*/
QList<MOrder>getAllOrders();
+ /**returns a list of all available shipping methods*/
+ QList<MShipping>getAllShipping();
+
+ /**returns valid voucher prices in cents*/
+ QList<int>getVoucherPrices();
+
/**returns a list of all orders that order from one of the specified events*/
QList<MOrder>getOrdersByEvents(QList<int>);
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",2);
-/**the order has been finalized; no more changes possible*/
-define("ORDER_CLOSED",10);
+/**the order has been finalized; no more changes possible; TODO: define exactly what this means*/
+define("ORDER_CLOSED",0x80);
/**order validation: output XML*/
define("VALIDATEORDER_XML",1);
private $amountpaid=0;
private $ordertime=false;
private $senttime=false;
+ private $shippingcosts=0;
+ private $shippingtype=false;
//to be submitted
private $newtickets;
private $newticketamounts;
$this->amountpaid=$res[0]["amountpaid"];
$this->ordertime=$res[0]["ordertime"];
$this->senttime=$res[0]["senttime"];
+ if(!$db->isNull($res[0]["shippingtype"])){
+ $this->shippingcosts=$res[0]["shippingcosts"]+0;
+ $this->shippingtype=$res[0]["shippingtype"];
+ }
}
$this->newtickets=array();
$this->newticketamounts=array();
$this->newtickets[$eid][]=$price;
}
+ /**used by XML functions: add a voucher (if value is omitted it equals price); returns true on success*/
+ public function addVoucher($value,$price=false)
+ {
+ if(!is_numeric($value))return false;
+ if($price===false)$price=$value;
+ if(!is_numeric($price))return false;
+ if($price<0 || $value<=0)return false;
+ $this->newvouchers[]=array("price"=>$price,"value"=>$value);
+ return true;
+ }
+
/**sets the customer of this order; returns true on success, false on failure*/
public function setCustomer($cust)
{
return $this->setCustomer(new Customer($cust));
}
+ /**sets the shipping type/price of the order; if price is omitted, it is taken from the DB; if type is false it means no shipping involved; returns true on success*/
+ public function setShipping($stype,$sprice=false)
+ {
+ if(!$this->canChange())return false;
+ //check for no shipping
+ if($stype===false){
+ $this->shippingtype=false;
+ $this->shippingcosts=0;
+ }
+ //get DB data, check that type exists
+ global $db;
+ $res=$db->select("shipping","cost","shipid=".$db->escapeInt($stype));
+ if($res===false || count($res)<1)return false;
+ //check price
+ if($sprice===false)$sprice=$res[0]["cost"];
+ //remember
+ $this->shippingtype=$stype+0;
+ $this->shippingcosts=$sprice+0;
+ return true;
+ }
+
/**places/finalizes the order; returns false on failure, true on success or if the order already was finalized()*/
public function placeOrder($isSale=false)
{
return false;
}
// print(2);
- //create order
+ //create order, incl shipping
$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));
+ $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,"shippingtype"=>$this->shippingtype,"shippingcosts"=>$this->shippingcosts));
// print(3);
//orderid ok?
if($this->orderid===false){
return false;
}
//insert tickets
- $totalprice=0;
+ $totalprice=$this->shippingcosts;
foreach($this->newtickets as $evid=>$tcs){
$amount=count($tcs);
for($i=0;$i<$amount;$i++){
//TODO: check return code of addToOrder
}
}
- //TODO: insert vouchers
+ //insert vouchers
+ foreach($this->newvouchers as $vc){
+ $vouc=new Voucher;
+ $vouc->addToOrder($this->orderid,$vc["price"],$vc["value"]);
+ $totalprice+=$vc["price"];
+ //TODO: check return code of addToOrder
+ }
//update amountpaid for sales
if($isSale){
$db->update("order",array("amountpaid"=>$totalprice,"status"=>ORDER_SENT),"orderid=".$db->escapeInt($this->orderid));
}
}
- //TODO: check vouchers
+ //check vouchers
+ global $session;
+ $cananyvval=$session->canExecute("_anyvoucher");
+ $cananyvprc=$session->canExecute("_anypricevoucher");
+ $vvals=array();
+ foreach(explode(" ",$db->getConfig("ValidVouchers")) as $v)$vvals[]=$v+0;
+ foreach($this->newvouchers as $vc){
+ $vx=$xml->createElement("Voucher");
+ $vx->setAttribute("price",$vc["price"]);
+ $vx->setAttribute("value",$vc["value"]);
+ $vx->setAttribute("id",$ftid++);
+ //check for valid value
+ if(!$cananyvval && !in_array($vvals)){
+ $vx->setAttribute("status",tr("invalidvalue","voucher state"));
+ $ostat="fail";
+ $ret=false;
+ }else
+ //check for value==price
+ if(!$cananyvprc && $vc["price"]!=$vc["value"]){
+ $vx->setAttribute("status",tr("invalidprice","voucher state"));
+ $ostat="fail";
+ $ret=false;
+ }else
+ $totalprice+=$vc["price"];
+ //dump it
+ $ord->appendChild($vx);
+ }
- //TODO: check shipping
+ //check shipping
+ if($this->shippingtype!==false){
+ $cananyship=$session->canExecute("_anyshipping");
+ $cananysprc=$session->canExecute("_repriceshipping");
+ //check shipping type exists
+ $res=$db->select("shipping","*","shipid=".$db->escapeInt($this->shippingtype));
+ $sp=$xml->createElement("Shipping");
+ if($res!==false && count($res)>0){
+ //check user has right to use this
+ if(!$res[0]["canallusers"] && !$cananyship){
+ $sp->setAttribute("type","-1");
+ $sp->setAttribute("price",0);
+ $sp->appendChild($xml->createTextNode(tr("Shipping type not available to user.")));
+ $ostat="fail";
+ $ret=false;
+ }else{
+ //correct price
+ if(!$cananysprc)
+ $this->shippingcosts=$res[0]["cost"];
+ //create target
+ $sp->setAttribute("type",$this->shippingtype);
+ $sp->setAttribute("price",$this->shippingcosts);
+ $sp->appendChild($xml->createTextNode($res[0]["description"]));
+ //add to sum
+ $totalprice+=$this->shippingcosts;
+ }
+ }else{
+ $sp->setAttribute("type","-1");
+ $sp->setAttribute("price",0);
+ $sp->appendChild($xml->createTextNode(tr("Illegal shipping type.")));
+ $ostat="fail";
+ $ret=false;
+ }
+ $ord->appendChild($sp);
+ }
//add other data and dump XML
if($dumpxml){
$totalprice=0;
global $db;
$res=$db->select("ticket","ticketid","orderid=".$db->escapeInt($this->orderid));
- if(count($res)>0)
+ if($res!==false && count($res)>0)
foreach($res as $tc){
$tick=new Ticket($tc["ticketid"]);
$tx=$xml->createElement("Ticket");
if($tick->mustBePaid())$totalprice+=$tick->getPrice();
$doc->appendChild($tx);
}
- //TODO: add vouchers
+ //add vouchers
+ $res=$db->select("voucher","voucherid,price,value,isused","orderid=".$db->escapeInt($this->orderid));
+ if($res!==false && count($res)>0)
+ foreach($res as $vc){
+ $vx=$xml->createElement("Voucher");
+ $vx->setAttribute("id",$vc["voucherid"]);
+ $vx->setAttribute("price",$vc["price"]);
+ $vx->setAttribute("value",$vc["value"]);
+ $vx->setAttribute("used",$vc["isused"]?"1":"0");
+ $totalprice+=$vc["price"];
+ $doc->appendChild($vx);
+ }
+
+ //add shipping
+ if($this->shippingtype !== false){
+ $sx=$xml->createElement("Shipping");
+ $sx->setAttribute("price",$this->shippingcosts);
+ $sx->setAttribute("type",$this->shippingtype);
+ $res=$db->select("shipping","*","shipid=".$db->escapeInt($this->shippingtype));
+ if($res!==false && count($res)>0){
+ $sx->appendChild($xml->createTextNode($res[0]["description"]));
+ }
+ $doc->appendChild($sx);
+ if($this->status==ORDER_PLACED || $this->status==ORDER_SENT)
+ $totalprice+=$this->shippingcosts;
+ }
//add sum
$doc->setAttribute("totalprice",$totalprice);
return $this->status;
}
- /**helper function: returns whether the order has outstanding payments/refunds*/
- public function getPaymentStatus()
+ /**helper function: returns the amount due to be paid; returns a negative value for refunds*/
+ public function amountDue()
{
global $db;
//calculate amount due
$totalprice=0;
$res=$db->select("ticket","ticketid","orderid=".$db->escapeInt($this->orderid));
- if(count($res)>0)
+ if($res!==false && 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";
+ //add vouchers
+ $res=$db->select("voucher","price","orderid=".$db->escapeInt($this->orderid));
+ if($res!==false && count($res)>0)
+ foreach($res as $vc){
+ $totalprice+=$vc["price"];
+ }
+ //add shipping
+ if($this->status==ORDER_PLACED || $this->status==ORDER_SENT)
+ $totalprice+=$this->shippingcosts;
+ //compare with what has been paid, return diff
+ return $totalprice-$this->amountpaid;
+ }
+
+ /**returns the amount already paid*/
+ public function amountPaid()
+ {
+ return $this->amountpaid;
+ }
+
+ /**helper function: returns whether the order has outstanding payments/refunds*/
+ public function getPaymentStatus()
+ {
+ $adue=$this->amountDue();
+ if($adue==0)return "ok";
+ if($adue<0)return "needrefund";
else return "needpayment";
}
/**sets the order to being cancelled, returns true on success*/
public function setCancelled()
{
- if(!$this->isValid())return false;
- if($this->status!=ORDER_PLACED)return false;
global $db;
- $db->update("order",array("status"=>ORDER_CANCELLED,"senttime"=>time()),"orderid=".$db->escapeInt($this->orderid));
+ $db->beginTransaction();
+ //check validity and status
+ $res=$db->select("order","status","orderid=".$db->escapeInt($this->orderid));
+ if($res===false || count($res)<1){
+ $db->rollbackTransaction();
+ return false;
+ }
+ if($res[0]["status"]!=ORDER_PLACED){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //check tickets
+ $res=$db->select("ticket","status","orderid=".$db->escapeInt($this->orderid));
+ for($i=0;$i<count($res);$i++){
+ if($res[$i]["status"]==TICKET_USED){
+ $db->rollbackTransaction();
+ return false;
+ }
+ }
+ //check vouchers
+ $res=$db->select("voucher","price,isused,value","orderid=".$db->escapeInt($this->orderid));
+ for($i=0;$i<count($res);$i++){
+ //already cancelled?
+ if($res[$i]["price"]==0 &&$res[$i]["value"]==0)
+ continue;
+ //unused?
+ if(!$res[$i]["isused"])
+ continue;
+ //else fail
+ $db->rollbackTransaction();
+ return false;
+ }
//propagate to tickets
$db->update("ticket",array("status"=>TICKET_CANCELLED),"orderid=".$db->escapeInt($this->orderid));
- //TODO: propagate to vouchers
-
+ //propagate to vouchers
+ $db->update("voucher",array("price"=>0,"value"=>0,"isused"=>0),"orderid=".$db->escapeInt($this->orderid));
+ //set order to cancelled
+ $db->update("order",array("status"=>ORDER_CANCELLED,"senttime"=>time()),"orderid=".$db->escapeInt($this->orderid));
+ $db->commitTransaction();
return true;
}
}else $price=-1;
$order->addTicket($tc->getAttribute("event")+0,$price);
}
- //TODO: get vouchers
+ //get vouchers
+ foreach($doc->getElementsByTagName("Voucher") as $vc){
+ $v=trim($vc->getAttribute("value"));
+ if($vc->hasAttribute("price"))
+ $p=trim($vc->getAttribute("price"));
+ else
+ $p=false;
+ $order->addVoucher($v,$p);
+ }
+
+ //get shipping
+ foreach($doc->getElementsByTagName("Shipping") as $sp){
+ if($sp->hasAttribute("price"))
+ $p=trim($sp->getAttribute("price"));
+ else
+ $p=false;
+ $t=trim($sp->getAttribute("type"));
+ $order->setShipping($t,$p);
+ }
//get opt. address
foreach($doc->getElementsByTagName("DeliveryAddress") as $da){
global $db;
$xml=new DomDocument;
$doc=$xml->createElement("OrderList");
- $res=$db->select("order","orderid,customerid,status,amountpaid",$where);
+ $res=$db->select("order","orderid,customerid,status,amountpaid,shippingtype,shippingcosts",$where);
foreach($res as $ord){
+ $price=0;
+ //check shipping
+ if(!$db->isNull($ord["shippingtype"]))
+ $price+=$ord["shippingcosts"];
//collect tickets
$tres=$db->select("ticket","price,status","orderid=".$db->escapeInt($ord["orderid"]));
- $price=0;
foreach($tres as $tc)
if(($tc["status"]&TICKET_MPAY)!=0)
$price+=$tc["price"];
+ //collect vouchers
+ $tres=$db->select("voucher","price","orderid=".$db->escapeInt($ord["orderid"]));
+ foreach($tres as $tc)
+ $price+=$tc["price"];
//generate XML
$ox=$xml->createElement("Order");
$ox->setAttribute("id",$ord["orderid"]);
header("X-MagicSmoke-Status: Ok");
else{
header("X-MagicSmoke-Status: Error");
- echo "Unable to update order comment.";
+ echo tr("Unable to update order comment.");
}
}
+
+//change the shipping method on an order
+function setOrderShippingXml($txt)
+{
+ //parse XML data
+ $xml=new DomDocument;
+ $xml->loadXml($txt);
+ $doc=$xml->documentElement;
+ $oid=$doc->getAttribute("orderid")+0;
+ if($doc->hasAttribute("type"))
+ $type=$doc->getAttribute("type");
+ else
+ $type=false;
+ if($doc->hasAttribute("price"))
+ $price=$doc->getAttribute("price");
+ else
+ $price=false;
+ //set shipping
+ global $db;
+ global $session;
+ $db->beginTransaction();
+ $res=$db->select("order","status","orderid=".$oid);
+ if($res===false || count($res)<1){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Invalid Order.");
+ $db->rollbackTransaction();
+ return;
+ }
+ //TODO: check order status (define rules first)
+ if($type===false){
+ //remove shipping
+ $db->update("order",array("shippingtype"=>false,"shippingcosts"=>0),"orderid=".$db->escapeInt($oid));
+ }else{
+ //set a shipping option
+ $ship=$db->select("shipping","cost","shipid=".$db->escapeInt($type));
+ if($ship===false || count($ship)<1){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Invalid Shipping Method.");
+ $db->rollbackTransaction();
+ return;
+ }
+ //check price
+ if($price===false || !$session->canExecute("_repriceshipping"))
+ $price=$ship[0]["cost"];
+ $db->update("order",array("shippingtype"=>$type,"shippingcosts"=>$price),"orderid=".$db->escapeInt($oid));
+ }
+ $db->commitTransaction();
+ //dump order object
+ $ord=new Order($oid);
+ header("X-MagicSmoke-Status: Ok");
+ $ord->dumpXml();
+}
+
+//get shipping list
+function getShippingXml()
+{
+ $xml=new DomDocument;
+ $root=$xml->createElement("ShippingList");
+ global $db,$session;
+ $res=$db->select("shipping","*","");
+ $all=$session->canExecute("setshipping")||$session->canExecute("_anyshipping");
+ if($res!==false && count($res)>0)
+ foreach($res as $sh){
+ if(!$sh["canallusers"] && !$all)continue;
+ $sx=$xml->createElement("ShippingOption");
+ $sx->setAttribute("type",$sh["shipid"]);
+ $sx->setAttribute("price",$sh["cost"]);
+ $sx->setAttribute("web",$sh["canuseweb"]?"1":"0");
+ $sx->setAttribute("anyUser",$sh["canallusers"]?"1":"0");
+ $sx->appendChild($xml->createTextNode($sh["description"]));
+ $root->appendChild($sx);
+ }
+ $xml->appendChild($root);
+ header("X-MagicSmoke-Status: Ok");
+ print($xml->saveXml());
+}
+
+//implement set shipping info
+function setShippingXml($txt)
+{
+ //parse XML data
+ $xml=new DomDocument;
+ $xml->loadXml($txt);
+ $doc=$xml->documentElement;
+ if($doc->hasAttribute("type"))
+ $type=$doc->getAttribute("type")+0;
+ else
+ $type=false;
+ $price=$doc->getAttribute("price")+0;
+ if($price<0)$price=0;
+ $web=$doc->getAttribute("web")+0;
+ $any=$doc->getAttribute("anyUser")+0;
+ $dsc="";
+ foreach($doc->childNodes as $cn)
+ if($cn->nodeType==XML_TEXT_NODE)
+ $dsc=$cn->wholeText;
+ //change/create
+ global $db;
+ if($type===false){
+ $type=$db->insert("shipping",array("cost" => $price, "canuseweb" => $web?1:0,
+ "canallusers" => $any?1:0, "description" => $dsc));
+ if($type===false){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Unable to create new shipping method.");
+ return;
+ }
+ }else{
+ $succ=$db->update("shipping",array("cost" => $price, "canuseweb" => $web?1:0,
+ "canallusers" => $any?1:0, "description" => $dsc),
+ "shipid=".$db->escapeInt($type));
+ if($succ===false || $succ<1){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Unable to change shipping method.");
+ return;
+ }
+ }
+ header("X-MagicSmoke-Status: Ok");
+ echo $type;
+}
+//delete shipping info
+function deleteShippingXml($sid)
+{
+ global $db;
+ if(!is_numeric($sid)){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Expected a numeric shipping ID.");
+ }
+ $r=$db->deleteRows("shipping","shipid=".$db->escapeInt($sid));
+ if($r==false || $r<1){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Unable to delete shipping method.");
+ return;
+ }
+ header("X-MagicSmoke-Status: Ok");
+}
?>
\ No newline at end of file
return getRandom(16*4);
}
+//don't apply de-randomization
+define("RND_ANYRANGE",-1);
+//bits to keep
+define("RND_MASK",7);
+//added to a ticket
+define("RND_TICKET",0x00);
+//added to a voucher
+define("RND_VOUCHER",0x08);
+//not used yet:
+define("RND_OTHER1",0x10);
+define("RND_OTHER1",0x18);
+
/**return a new Code-39 capable ID; length is the amount of characters*/
-function getCode39ID($length)
+function getCode39ID($length,$range=RND_ANYRANGE)
{
$c39="23456789ABCDEFGHJKLMNPRSTUVWXYZ+";
$rnd=getRandom($length*8);
//cut random
$r="0x".substr($rnd,$i*2,2);
$r=($r+0)&31;
+ //if this is the first char, further manipulate it
+ if($i==0 && $range!=RND_ANYRANGE){
+ $r=($r&RND_MASK)|$range;
+ }
//append char
$ret.=substr($c39,$r,1);
}
//generate ticket ID
$db->beginTransaction();
do{
- $tid=getCode39ID(self::$NumTicketChars);
+ $tid=getCode39ID(self::$NumTicketChars,RND_TICKET);
$res=$db->select("ticket","ticketid","ticketid=".$db->escapeString($tid));
if(count($res)==0)break;
}while(true);
--- /dev/null
+<?
+//
+// PHP Implementation: voucher
+//
+// Description:
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2008
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+/* TRANSLATOR php:: */
+
+class Voucher
+{
+ private $voucherid=false;
+ private $orderid=false;
+ private $price=false;
+ private $value=false;
+ private $isused=false;
+
+ private static $NumVoucherChars=false;
+
+ /**create a new voucher: with id from DB or for later creation*/
+ public function __construct($voucherid=false)
+ {
+ global $db;
+ if(self::$NumVoucherChars===false){
+ self::$NumVoucherChars=$db->getConfig("VoucherIDChars")+0;
+ if(self::$NumVoucherChars<=5)self::$NumVoucherChars=10;
+ }
+ if($voucherid!==false){
+ $res=$db->select("voucher","*","voucherid=".$db->escapeString($voucherid));
+ if($res===false || count($res)<1)return;
+ $this->voucherid=$res[0]["voucherid"];
+ $this->orderid=$res[0]["orderid"];
+ $this->price=$res[0]["price"];
+ $this->value=$res[0]["value"];
+ $this->isused=$res[0]["isused"];
+ }
+ }
+
+ /**return whether this voucher has an equivalent in the DB*/
+ public function isValid()
+ {
+ return $this->voucherid!==false;
+ }
+
+ /**returns the remaining value in cent*/
+ public function remainingValue()
+ {
+ return $this->value;
+ }
+
+ /**returns the price of the voucher*/
+ public function price()
+ {
+ return $this->price;
+ }
+
+ /**returns the ID of the order this voucher belongs to*/
+ public function orderID()
+ {
+ return $this->orderid;
+ }
+
+ /**returns whether the voucher is cancelled*/
+ public function isCancelled()
+ {
+ return $this->price==0 && $this->value==0;
+ }
+
+ /**returns whether the voucher has already been used*/
+ public function isUsed()
+ {
+ return $this->isused;
+ }
+
+ /**returns whether the voucher can be cancelled*/
+ public function canCancel()
+ {
+ if($this->isCancelled())return true;
+ if(!$this->isUsed())return true;
+ return false;
+ }
+
+ /**returns whether the voucher can be forcefully emptied*/
+ public function canEmpty()
+ {
+ return !$this->isCancelled();
+ }
+
+ /**returns whether the voucher can pay for anything*/
+ public function canPay()
+ {
+ return $this->value!=0;
+ }
+
+ /**actually cancel the voucher (does all checks again); returns true on success*/
+ public function cancelVoucher()
+ {
+ if(!isValid())return false;
+ global $db;
+ $db->beginTransaction();
+ //recheck
+ $res=$db->select("voucher","*","voucherid=".$db->escapeString($this->voucherid));
+ if($res===false || count($res)<1){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //is it non-cancelled and used?
+ if(($res[0]["price"]!=0 || $res[0]["value"]!=0) && $res[0]["isused"]){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //overwrite
+ $db->update("voucher",array("price"=>0,"value"=>0,"isused"=>0),"voucherid=".$db->escapeString($this->voucherid));
+ $db->commitTransaction();
+ return true;
+ }
+
+ /**actually empty a voucher*/
+ public function emptyVoucher()
+ {
+ //sanity check
+ if(!$this->isValid())return;
+ if(!$this->canEmpty())return;
+ //now do the deed
+ global $db;
+ $db->update("voucher",array("value"=>0,"isused"=>1),"voucherid=".$db->escapeString($this->voucherid));
+ }
+
+ /**create the voucher in the database; returns false on failue*/
+ public function addToOrder($orderid,$price,$value)
+ {
+ //since this is called from Order only, we assume orderid to be correct
+ //sanity check (should not fail, since Order also checks)
+ if($price<0 || $value<=0)return false;
+ //create a new ID
+ global $db;
+ $db->beginTransaction();
+ do{
+ $vid=getCode39ID(self::$NumVoucherChars,RND_VOUCHER);
+ $res=$db->select("voucher","voucherid","voucherid=".$db->escapeString($vid));
+ if(count($res)==0)break;
+ }while(true);
+ //create entry
+ $res=$db->insert("voucher",array("voucherid"=>$vid,"price"=>$price,"value"=>$value,"isused"=>0,"orderid"=>$orderid));
+ if($res===false){
+ $db->rollbackTransaction();
+ return false;
+ }
+ $db->commitTransaction();
+ $this->voucherid=$vid;
+ $this->orderid=$oroderid;
+ $this->price=$price+0;
+ $this->value=$value+0;
+ $this->isused=false;
+ return true;
+}
+
+ /**use the voucher to pay for an order; return true on success*/
+ public function payForOrder($orderid)
+ {
+ //pre-check
+ if(!$this->isValid() || !$this->canPay())return false;
+ //now go to the DB
+ global $db;
+ $db->beginTransaction();
+ //get voucher data and recheck
+ $vres=$db->select("voucher","*","voucherid=".$db->escapeString($this->voucherid));
+ if($vres===false || count($vres)<1){
+ $db->rollbackTransaction();
+ return false;
+ }
+ if($vres[0]["value"]<=0){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //get my own order status
+ $myord=new Order($this->orderid);
+ if(!$myord->isValid()){
+ $db->rollbackTransaction();
+ return false;
+ }
+ $ps=$myord->getPaymentStatus();
+ if($ps!="needrefund" && $ps!="ok"){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //get the target order data
+ $ord=new Order($orderid);
+ if(!$ord->isValid()){
+ $db->rollbackTransaction();
+ return false;
+ }
+ $adue=$ord->amountDue();
+ if($adue<=0){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //get amount to swap
+ $pay=$vres[0]["value"];
+ if($adue<$pay)$pay=$adue;
+ //store corrected voucher
+ $this->value=$vres[0]["value"]-$pay;
+ $b=$db->update("voucher",array("value"=>$this->value,"isused"=>1),"voucherid=".$db->escapeString($this->voucherid))!==false;
+ //store corrected order
+ $a=$ord->amountPaid()+$pay;
+ $b&=$db->update("order",array("amountpaid"=>$a),"orderid=".$db->escapeInt($orderid))!==false;
+ //if anything went wrong: roll back
+ if(!$b){
+ $db->rollbackTransaction();
+ return false;
+ }
+ //whoo. got it!
+ $db->commitTransaction();
+ return true;
+ }
+
+ /**dumps the XML representation of the voucher*/
+ function dumpXml()
+ {
+ $xml=new DomDocument;
+ $doc=$xml->createElement("Voucher");
+ $doc->setAttribute("id",$this->voucherid);
+ $doc->setAttribute("price",$this->price);
+ $doc->setAttribute("value",$this->value);
+ $doc->setAttribute("used",$this->isused?"1":"0");
+ $xml->appendChild($doc);
+ print($xml->saveXml());
+ }
+};
+
+function getVoucherPricesXml()
+{
+ global $db;
+ header("X-MagicSmoke-Status: Ok");
+ $r=$db->getConfig("ValidVouchers");
+ if($r!==false)print($r);
+}
+
+function cancelVoucherXml($vid)
+{
+ $vc=new Voucher($vid);
+ if($vc->isValid() && $vc->canCancel()){
+ if($vc->cancelVoucher()){
+ header("X-MagicSmoke-Status: Ok");
+ return;
+ }
+ }
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Unable to cancel voucher.");
+}
+
+function emptyVoucherXml($vid)
+{
+ $vc=new Voucher($vid);
+ if(!$vc->isValid()){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Invalid voucher, cannot empty it.");
+ return;
+ }
+ header("X-MagicSmoke-Status: Ok");
+ $vc->emptyVoucher();
+}
+
+function useVoucherXml($txt)
+{
+ //split data
+ $splt=explode("\n",$txt);
+ if(count($splt)<2){
+ header("X-MagicSmoke-Status: SyntaxError");
+ echo tr("Expected two arguments: voucher id and order id.");
+ return;
+ }
+ $vc=new Voucher(trim($splt[0]));
+ if(!$vc->isValid()){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Invalid voucher id.");
+ return;
+ }
+ if($vc->payForOrder(trim($splt[1]))){
+ header("X-MagicSmoke-Status: Ok");
+ print($vc->remainingValue());
+ }else{
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Unable to process payment via voucher.");
+ }
+}
+
+function getVoucherXml($vid)
+{
+ $vc=new Voucher($vid);
+ if(!$vc->isValid()){
+ header("X-MagicSmoke-Status: Error");
+ echo tr("Invalid voucher ID.");
+ return;
+ }
+ header("X-MagicSmoke-Status: Ok");
+ $vc->dumpXml();
+}
+
+?>
\ No newline at end of file
if($dbScheme->isBlobColumn($table,$k))
$ret.=$this->escapeBlob($v);
else
+ if($dbScheme->isBoolColumn($table,$k))
+ $ret.=$this->escapeBool($v);
+ else
//don't know how to escape it...
$ret.="NULL";
}
/**returns the error string of the last operation*/
public abstract function lastError();
+
+ /**returns whether the result value is NULL; the default interprets both null and false as NULL; overwrite if the DB can return false for a boolean*/
+ public function isNull($val)
+ {
+ if($val===false || $val===null)return true;
+ else return false;
+ }
};
?>
\ No newline at end of file
);
$this->preset["config"]=array(
array("ckey"=>"MagicSmokeVersion","cval"=>$this->sversion),
- array("ckey"=>"ValidVouchers","cval"=>"10 20 25 50"),
+ array("ckey"=>"ValidVouchers","cval"=>"1000 2000 2500 5000"),
array("ckey"=>"OrderStop","cval"=>"24"),
array("ckey"=>"SaleStop","cval"=>"0"),
array("ckey"=>"TicketIDChars","cval"=>"10"),
$this->scheme["shipping"]=array(
"shipid" => array("seq32","primarykey"),
"cost" => array("int32","notnull"), //default cost of this shipping type
- "canuseweb" => array("bool","default:false"), //is offered on web interface
- "canallusers" => array("bool","default:true") //all GUI users may select it
+ "canuseweb" => array("bool","defaultbool:false"), //is offered on web interface
+ "canallusers" => array("bool","defaultbool:true"), //all GUI users may select it
+ "description" => array("string") //description for the shipping type
);
//orders by customers
//this is for comparison with the price fields in ticket and voucher tables
"amountpaid" => array("int32"),
//shipping price
- "shippingcosts" => array("int32","default:0"),
+ "shippingcosts" => array("int32","defaultint:0"),
//pointer to shipping type (none per default, programmatic default is in config)
"shippingtype" => array("int32","null","foreignkey:shipping:shipid")
);
//tickets
$this->scheme["ticket"]=array(
+ //a 8-32 char code (code39: case-insensitive letters+digits) for the ticket
"ticketid" => array("string:32","primarykey"),
"eventid" => array("int32","foreignkey:event:eventid"),
//initially a copy from event, can be adjusted by seller
);
//vouchers and re-imbursments
$this->scheme["voucher"]=array(
- //a 8-32 char code (code39: case-insensitive letters+digits) for the voucher)
+ //a 8-32 char code (code39: case-insensitive letters+digits) for the voucher
"voucherid" => array("string:32","primarykey"),
- //if ordered: order-info
+ //price of the voucher (0 if cancelled)
"price" => array("int32","notnull"),
+ //order this voucher belongs to
"orderid" => array("int32","foreignkey:order:orderid"),
- //unix-timestamp of original sales date/time
- "salestime" => array("int32","notnull"),
- //remaining value in cents
+ //marker: voucher has been used to pay something
+ "isused" => array("bool","defaultbool:false"),
+ //remaining value in cents (0 for cancelled)
"value" => array("int32","notnull")
);
}
}
- /**returns true if the given column is of a string type*/
+ /**returns true if the given column is of a blob type*/
public function isBlobColumn($tab,$col)
{
if(!isset($this->scheme[$tab][$col]))
return false;
}
}
+
+ /**returns true if the given column is of a bool type*/
+ public function isBoolColumn($tab,$col)
+ {
+ if(!isset($this->scheme[$tab][$col]))
+ return false;
+ $tpa=explode(":",$this->scheme[$tab][$col][0]);
+ switch($tpa[0]){
+ case "bool":
+ case "boolean":
+ return true;
+ default:
+ return false;
+ }
+ }
};
$dbScheme=new DbScheme;
?>
\ No newline at end of file
<?
//load the linguist dummies, since we use them quite often
include_once("inc/tr.php");
-//internal info: server version
-$MAGICSMOKEVERSION="0.1 alpha";
//load DB drivers
include('./inc/db/db.php');
include('./inc/db/db_mysql.php');
include("./inc/classes/random.php");
include("./inc/classes/order.php");
include("./inc/classes/ticket.php");
+include("./inc/classes/voucher.php");
include("./inc/classes/cart.php");
include('./inc/classes/error.php');
include('./inc/classes/language_manager.php');
--- /dev/null
+//This file contains the protocol version of MagicSmoke.
+
+//Syntax:
+// defversion(token,version)
+// token - identifies the version described
+// version - is the version string
+// both arguments MUST NOT have quotes
+// lines with // and empty lines are ignored
+
+// C++: defversion is a preprocessor macro
+// PHP: scans each line splitting at () and ,
+
+//minimum version that the server understands (4 hex digits)
+defversion(MINSERVER,0000)
+//current version of the server
+defversion(CURSERVER,0002)
+
+//current human readable version of the server
+defversion(HRSERVER,0.2 beta)
+
+//minimum version that the client requires
+defversion(MINCLIENT,0000)
+//current version of the client
+defversion(CURCLIENT,0002)
+
+//current human readable version of the client
+defversion(HRCLIENT,0.2 beta)
--- /dev/null
+<?
+
+//function to register version info
+function defversion()
+{
+ global $MSVERSION;
+ foreach(file('inc/machine/version.inc') as $line){
+ $line=trim($line);
+ //check whether we need this line
+ if(substr($line,0,10)!="defversion")continue;
+ //extract useful information
+ $line=trim(strtr($line,array("defversion"=>"","("=>"",")"=>"",";"=>"")));
+ $sp=explode(",",$line);
+ //syntax check
+ if(count($sp)!=2)continue;
+ //store
+ $k=trim($sp[0]);$v=trim($sp[1]);
+ $MSVERSION[$k]=$v;
+ }
+}
+
+/**this variable contains version information about the server protocol:
+ MINSERVER=minimum protocol version the server understands;
+ CURSERVER=protocol version the server implements;
+ HRSERVER=human readable server version;
+ note that the database layer has its own version that is not stored here*/
+$MSVERSION=array();
+
+//load version include
+defversion();
+
+?>
\ No newline at end of file
header("Content-Type: application/x-MagicSmoke");
include_once("inc/tr.php");
+include_once("inc/machine/version.php");
//check whether the request is known
/* TRANSLATOR TransactionNames:: */
tr("geteventlist"),tr("geteventdata"),tr("seteventdata"),tr("eventsummary"),tr("cancelevent"),//event infos
tr("getroomdata"),tr("setroomdata"),//room infos
tr("getcustomerlist"),tr("getcustomer"),tr("setcustomer"),tr("deletecustomer"), //customer info
- tr("checkorder"),tr("createorder"),tr("createsale"),tr("getorderlist"),tr("getorder"),tr("orderpay"),tr("orderrefund"),tr("ordershipped"),tr("cancelorder"),tr("orderbyticket"),tr("getordersbyevents"),tr("setordercomment"), //sell/order stuff
+ tr("checkorder"),tr("createorder"),tr("createsale"),tr("getorderlist"),tr("getorder"),tr("orderpay"),tr("orderrefund"),tr("ordershipped"),tr("cancelorder"),tr("orderbyticket"),tr("getordersbyevents"),tr("setordercomment"),tr("orderchangeshipping"), //sell/order stuff
+ tr("getshipping"),tr("setshipping"),tr("deleteshipping"), //shipping info
tr("getticket"),tr("useticket"),tr("changeticketprice"),tr("ticketreturn"),//ticket management
+ tr("getvoucherprices"),tr("cancelvoucher"),tr("emptyvoucher"),tr("usevoucher"),tr("getvoucher"), //voucher management
tr("gettemplatelist"),tr("gettemplate"),tr("settemplate") //templates
);
/**special roles begin with _ and are listed here (in lower case and wrapped in tr())*/
$SPECIALROLES=array(
tr("_admin"),//system administrator
tr("_anyshipping"),//user can assign any kind of shipping
- tr("_repriceshipping")//user may alter shipping price
+ tr("_repriceshipping"),//user may alter shipping price
+ tr("_anyvoucher"),//user may generate vouchers of any value/price, not just configured ones
+ tr("_anypricevoucher")//user may generate vouchers with price different from value
);
/* TRANSLATOR php::
*/
// server info can be answered without performing any more initialization
if($SMOKEREQUEST=="serverinfo"){
header("X-MagicSmoke-Status: Ok");
- print("<Info>\n <ServerVersion proto=\"0000 0000\">$MAGICSMOKEVERSION</ServerVersion>\n <AuthAlgorithm>$ClientAuthAlgo</AuthAlgorithm>\n</Info>");
+ print("<Info>\n <ServerVersion proto=\"");
+ print($MSVERSION["MINSERVER"]." ".$MSVERSION["CURSERVER"]);
+ print("\">".$MSVERSION["HRSERVER"]);
+ print("</ServerVersion>\n <AuthAlgorithm>$ClientAuthAlgo</AuthAlgorithm>\n</Info>");
exit();
}
setOrderCommentXml(trim($REQUESTDATA));
exit();
}
+if($SMOKEREQUEST=="orderchangeshipping"){
+ setOrderShippingXml(trim($REQUESTDATA));
+ exit();
+}
+
+//get shipping info
+if($SMOKEREQUEST=="getshipping"){
+ getShippingXml();
+ exit();
+}
+//set/create shipping info
+if($SMOKEREQUEST=="setshipping"){
+ setShippingXml(trim($REQUESTDATA));
+ exit();
+}
+//delete shipping info
+if($SMOKEREQUEST=="deleteshipping"){
+ deleteShippingXml(trim($REQUESTDATA));
+ exit();
+}
+
//get a ticket
if($SMOKEREQUEST=="getticket"){
exit();
}
+//get all valid prices for vouchers
+if($SMOKEREQUEST=="getvoucherprices"){
+ getVoucherPricesXml();
+ exit();
+}
+//return a voucher: cancels it
+if($SMOKEREQUEST=="cancelvoucher"){
+ cancelVoucherXml(trim($REQUESTDATA));
+ exit();
+}
+//return a voucher: emties it
+if($SMOKEREQUEST=="emptyvoucher"){
+ emptyVoucherXml(trim($REQUESTDATA));
+ exit();
+}
+//use a voucher to pay
+if($SMOKEREQUEST=="usevoucher"){
+ useVoucherXml(trim($REQUESTDATA));
+ exit();
+}
+//get info about a voucher
+if($SMOKEREQUEST=="getvoucher"){
+ getVoucherXml(trim($REQUESTDATA));
+ exit();
+}
+
//EOF
header("X-MagicSmoke-Status: Error");
die(tr("Internal Error: unknown command, hiccup in code structure."));