blog

Photo of receipt by Michael Walter on Unsplash

Sales Receipt File Format (part 3)

by | Sep 9, 2013 | Developer Blog | 0 comments

Last time, I talked about what a sales receipt represented in JSON format might look like. But in the aftermath, I realized something important: I had left too many options open for how to display the information.

Initially that detail wasn’t a concern, and I had been focusing solely on versatility, ease of support, and Murphy’s Law. Considering the file format only in terms of a hierarchy of data fields, I disqualified XML in favor of shorter & sweeter JSON. But part of the "versatility" is in allowing different stores to present their receipts in their own arrangement, not so much because each store might present different optional fields, but because the receipt can be part of the store’s branding, or "user experience," and I shouldn’t be the one to override those subtle details. By using XML, I still allow applications to rearrange the data into an efficiently uniform, clinical presentation if they so desire, but I also allow stores to provide a default presentation of their own choosing. Plus I don’t leave the initial visualization up to your imagination because the XML could be embedded in a browseable XHTML. Or better yet, the receipt could be defined by a "microformat" like RDFa. More people win this way.

Defining a sub-vocabulary of RDFa means we’re essentially just adding attributes to a normal (X)HTML page. Incidentally, I chose the RDFa convention even though it’s overkill compared to "microformats" and Microdata because it’s actually less stupid than the other two. "Microformats" assume they’re never going to clash with your CSS, which is naïve. And Microdata is unnecessarily complicated for something that’s supposed to be "simpler," and does not respect well-formed XML. In other words, I feel that only RDFa was designed with battle-hardened, "real world" experience. But I should give those guys a break; just look at my own ignorant attempt I’m making here. It can take a few lives to understand all the pitfalls of a problem domain.

Anyway, without formally describing a vocabulary, I’ve highlighted the RDFa bits of an otherwise ordinary HTML page, so you can get the idea. Compare it with last time‘s format:

<html xmlns:sr="http://example.com/sales-receipt-2013/#" prefix="sr: http://example.com/sales-receipt-2013/#">
<body>
<div resource="/receipt/96060" typeof="sr:receipt">
   <span property="sr:currency" content="USD"/>
   <p property="sr:seller">ARCO</p>
   <table>
      <tr>
         <td>Invoice #</td>
         <td property="sr:number">96060</td>
      </tr>
      <tbody property="sr:time" content="2010-05-07T15:15-05:00">
         <tr>
            <td>Date</td>
            <td>05/07/10</td>
         </tr>
         <tr>
            <td>Time</td>
            <td>15:15</td>
         </tr>
      </tbody>
      <tr>
         <td>Auth #</td>
         <td about="/payment/96060" property="sr:approval">011514</td>
      </tr>
   </table>
   <table resource="/payment/96060" typeof="sr:payment">
      <tr><td property="sr:method">MC</td></tr>
      <tr><td>Acct #</td></tr>
      <tr><td property="sr:account">############4321</td></tr>
   </table>
   <table resource="/item/96060/1" typeof="sr:item">
      <tr>
         <td>Pump</td>
         <td property="sr:units">Gallons</td>
      <td>Price</td>
      </tr>
      <tr>
         <td>03</td>
         <td property="sr:quantity">8.549</td>
         <td property="sr:each" content="2.999">$ 2.999</td>
      </tr>
   </table>
   <table>
      <tr>
         <td>Product</td>
         <td>Amount</td>
      </tr>
      <tr about="/item/96060/1">
         <td property="sr:item-desc">Unleaded</td>
         <td property="sr:price" content="25.64">$ 25.64</td>
      </tr>
   </table>
   <table>
      <tr>
         <td>Total Sale</td>
         <td property="sr:total" content="25.64">$ 25.64</td>
      </tr>
   </table>
</div>
<div resource="/receipt/4572" typeof="sr:receipt">
   <h3 property="sr:seller">Berghotel<br/>Grosse Scheidegg</h3>
   <p>3818 Grindelwald<br/>Familie R.M&uuml;ller</p>
   <table>
      <tr>
         <td property="sr:number" content="4572">Rech.Nr. 4572</td>
         <td property="sr:time" content="2007-07-30T13:29:17+01:00">30.07.2007/13:29:17</td>
      </tr>
      <tr>
         <td>Bar</td>
         <td>Tisch 7/01</td>
      </tr>
   </table>
   <table>
      <tr typeof="sr:item">
         <td property="sr:quantity">2</td>
         <td>&times;</td>
         <td property="sr:item-desc">Latte Macchiato</td>
         <td>&agrave;</td>
         <td property="sr:each">4.50</td>
         <td property="sr:currency">CHF</td>
         <td property="sr:price">9.00</td>
         <td property="sr:included" typeof="sr:item-fee">
            <span property="sr:label" content="MwSt"/>
            <span property="sr:rate" content="0.076"/>
            <span property="sr:amount" content="0.64"/>
         </td>
      </tr>
      <tr typeof="sr:item">
         <td property="sr:quantity">1</td>
         <td>&times;</td>
         <td property="sr:item-desc">Gloki</td>
         <td>&agrave;</td>
         <td property="sr:each">5.00</td>
         <td property="sr:currency">CHF</td>
         <td property="sr:price">5.00</td>
         <td property="sr:included" typeof="sr:item-fee">
            <span property="sr:label" content="MwSt"/>
            <span property="sr:rate" content="0.076"/>
            <span property="sr:amount" content="0.35"/>
         </td>
      </tr>
      <tr typeof="sr:item">
         <td property="sr:quantity">1</td>
         <td>&times;</td>
         <td property="sr:item-desc">Schweinschnitzel</td>
         <td>&agrave;</td>
         <td property="sr:each">22.00</td>
         <td property="sr:currency">CHF</td>
         <td property="sr:price">22.00</td>
         <td property="sr:included" typeof="sr:item-fee">
            <span property="sr:label" content="MwSt"/>
            <span property="sr:rate" content="0.076"/>
            <span property="sr:amount" content="1.55"/>
         </td>
      </tr>
      <tr typeof="sr:item">
         <td property="sr:quantity">1</td>
         <td>&times;</td>
         <td property="sr:item-desc">Ch&auml;ssp&auml;tzli</td>
         <td>&agrave;</td>
         <td property="sr:each">18.00</td>
         <td property="sr:currency">CHF</td>
         <td property="sr:price">18.00</td>
         <td property="sr:included" typeof="sr:item-fee">
            <span property="sr:label" content="MwSt"/>
            <span property="sr:rate" content="0.076"/>
            <span property="sr:amount" content="1.31"/>
         </td>
      </tr>
   </table>
   <table>
      <tr>
         <td>Total :</td>
         <td property="sr:currency">CHF</td>
         <td property="sr:total">54.50</td>
      </tr>
   </table>
   <table>
      <tr typeof="sr:subtotal">
         <td property="sr:label" content="MwSt">Incl. 7.6% MwSt</td>
         <td>54.50 CHF:</td>
         <td property="sr:amount">3.85</td>
      </tr>
   </table>
   <p>Entspricht in Euro: 36.33 EUR</p>
   <p>Es bediente Sie: Ursula</p>
</div>
</body>
</html>

RDFa is flexible enough to allow the fields of a record to be given way out of order if the store desires such a layout. Note also how data fields may be included without rendering them by utilizing the content attribute on an empty element. This means a store such as Amazon could theoretically add this extra markup to the way they’re already rendering HTML receipts without affecting the visible presentation. If your accounting software wants to subsequently re-present the receipt in its own internal format, that’s fine, but Amazon at least gets to present it initially in their own layout.

+ more

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more