De logica van TNT *update 2*

Onlangs bestelde ik een paar boeken bij Amazon.com in de VS. Niets bijzonders, want dat doe ik regelmatig. Maar ja, het gaat wel eens mis.

Gistermiddag komt de buurvrouw naar mij toe met een grote doorzichtige plastic zak. "Deze heb ik net van een TNT bezorger in handen gedrukt gekregen. Volgens mij is hij wel beschadigd."

Met verbazing kijk ik naar het pakket. In de zak zit het restant van wat ooit een solide boekendoos van Amazon.com was en drie boeken. Alle drie zwaar beschadigd.

20110120-boek.jpg

20110120-boek2.jpg

Met het pakket ongeopend onder mijn arm ga ik naar het businesspoint van TNT. Een vriendelijke mevrouw kijkt met een mix van verbijstering en afschuw naar het pakket en zegt. "Zoiets had toch nooit bezorgd mogen worden? Helaas is iedereen hier al weg. Ik adviseer u morgen terug te komen."

Vanmorgen terug naar het businesspoint. Daar had de dame die mij gisteren hielp een uitgebreide notitie achtergelaten. "Helaas kan ik u niet verder helpen", zei een andere, vrij resolute, dame achter de balie. "Ja, u kunt het pakket wel terugsturen, maar dan moet u wel de verzendkosten betalen. Belt u dat nummer van de klantenservice."

Thuisgekomen bel ik dus 058 23333374. Daar hoor ik van een gehaaste stem op een antwoordapparaat dat het nummer niet meer in gebruik is. Voordat ik het nummer kan opschrijven, is het bandje al afgelopen. Het nummer een keer rustig herhalen, is blijkbaar teveel gevraagd. Ik bel nog een keer terug en schrijf het correcte nummer op: 058 2333382.

Een vriendelijke man vertelt mij dat hij de klacht zal doorgeven aan de afdeling klachtenafhandeling. "Zij nemen binnen vijf werkdagen contact met u op." Over de afhandeling kon hij verder niets zeggen, want "dit betreft het buitenland en dat is altijd heel ingewikkeld."

Tot mijn stomme verbazing - hebben mijn berichten op Twitter met #TNT daar wellicht iets mee te maken? - word ik binnen een uur teruggebeld door een mevrouw van de klachtenafhandeling.

Met haar had ik de volgende conversatie:

"De buren hadden dat pakket niet mogen accepteren. Nu zijn zij verantwoordelijk."

Pardon? Dit pakket had nooit aangeboden mogen worden, bedoelt u toch te zeggen?

"Nee meneer, als de buren het pakket aannemen, zijn zij verantwoordelijk."

Maar mevrouw: het pakket ligt hier ongeopend voor mij.

"Oh... je kunt aan de buitenkant zien dat het beschadigd is. Dan hadden ze het zeker niet moeten aannemen. Maar ik ga toch een oplossing zoeken. Daarvoor heb ik wel heel veel gegevens nodig. Daarbij zal de verzender in de VS dezelfde procedure moeten starten. Nee, nee... wij nemen daarover geen contact op. Dat moet u doen. U moet begrijpen dat het voor ons onmogelijk is om met alle verzenders contact op te nemen in geval van klachten."

Dus ik begrijp het goed: De verzender en ontvanger betalen. De transporteur vernield en de klanten moeten het maar uitzoeken en een enorme procedure starten?

"Ja, maar u bekijkt het vanuit uw eigen perspectief. U moet het ook eens vanuit een ander perspectief bekijken"

Wacht even mevrouw. Ik wil het maar vanuit één perspectief bekijken en dat zou u ook moeten doen: het perspectief van de klant.

"Dat doe ik ook, maar ú moet het vanuit een ander perspectief bekijken."

Weet u mevrouw. Het pakket ligt hier ongeopend op tafel. U laat het maar ophalen en stuur het terug. Ik wil daar wel een bewijs van.

"Oh... dat kunnen we ook doen, maar nog even voor mijn informatie zat er geen excuusnotificatie bij het pakket?"

Op de buitenkant zit alleen een gele sticker met barcode en een witte sticker met postcode en huisnummer.

"Nee meneer er moet ook een notificatie bijzitten."

Er zit verder helemaal niets op of bij. Misschien erin, maar dat weet ik niet, want het pakket is ongeopend.

"Dat is raar... Ik ga ik regelen dat het wordt opgehaald. Dan is er iets misgegaan in ons distributiecentrum. Zonder notificatie had dat pakket nooit bezorgd mogen worden. Bent u vandaag en morgen de hele dag thuis dan regel ik dat het wordt opgehaald. Nee meneer... ik kan geen indicatie geven wanneer de chauffeur komt."

Einde conversatie.

Ik begrijp het dus goed. Omdat er geen excuusnotificatie bij het pakket zat, had het niet bezorgd mogen worden. Dat de doos blijkbaar tussen twee containers of op een andere manier is gemangeld, waardoor van één boek zo'n twintig procent is afgesneden, van een ander boek de rug zwaar is beschadigd en van het derde boek ook een deel ontbreekt en dit geheel in een plastic zak is gedumpt, is blijkbaar geen reden om je af te vragen of zo'n pakket besteld moet worden. Dat is TNT logica.

En dan rest mij nu de uitdaging om Amazon uit te leggen dat er toch 'iets' is misgegaan en dat ik toch echt graag mijn geld terug wil krijgen. Ik vrees dat dit wordt vervolgd.

Eigenlijk mag ik helemaal niet klagen: Er zat geen aanslag van invoerrechten en BTW bij terwijl de waarde van dit papier toch echt boven de vrijstellingsgrens lag. Dat is dan toch weer mooi meegenomen!

Update
Het pakket is ondertussen al opgehaald. De chauffeur vertelde dat zij het nu gaan afhandelen met Amazon. Dat is goed nieuws, maar in strijd met wat vanmorgen werd verteld, dat TNT er natuurlijk niet aan kan beginnen om bij klachten met de afzender contact te zoeken. Ik ben benieuwd. Iets zegt me dat ook dit nog verder wordt vervolgd.

Update 2
Amazon heeft op basis van de foto's direct besloten het geld terug te storten. Daarop heb ik een nieuwe bestelling gedaan en die boeken zijn - zoals gebruikelijk - in prima staat afgeleverd. Dus uiteindelijk leefden we allemaal nog lang en gelukkig (of zoiets).

© Harold Makaske 20 januari 2011 to allow skipping count when used in template (ie to not count on click in main page) V1.4 - Fixed ignoe same IP count problem V1.5 - Added views_log table and changed plugin performance to check for unique visits by IP address. [gRegor] - Added option to set the length of time before re-counting hits from the same IP address (default: 2 hours) [gRegor] V1.6 - Added plugin menu to display all view count, w/ counter reset function - Delete view counter and log for deleted item V1.7 - use sql_query V1.8 - Admin page enhancement to preserve order and sort info v1.9 - Added item title in admin menu v1.9.1 - ignore draft in admin menu */ class NP_Views extends NucleusPlugin { // Note: I never run this plugin on 2.0 and have no idea whether it // wil work on <2.5. A user can simply chnage it to return // '200' and see if it works (likely will). I will gladly // change the min version to 2.0 and add the sql_table fix // upon such report. 8) function getMinNucleusVersion() { return '250'; } function getName() { return 'Views'; } function getAuthor() { return 'Rodrigo Moraes | Edmond Hui (admun) | gRegor Morrill'; } function getURL() { return 'http://www.tipos.com.br'; } function getVersion() { return '1.9.1'; } function getDescription() { return 'This plugin counts how many times an entry has been displayed.'; } function getEventList() { return array('PostAddItem', 'QuickMenu', 'PostDeleteItem'); } function supportsFeature($what) { switch($what) { case 'SqlTablePrefix': return 1; default: return 0; } } function getTableList() { return array( sql_table('plugin_views'), sql_table('plugin_views_log') ); } function install() { sql_query('CREATE TABLE IF NOT EXISTS ' . sql_table('plugin_views') . ' (id int(11) NOT NULL default "0", views int(15) NOT NULL default "0")'); sql_query('CREATE TABLE IF NOT EXISTS ' . sql_table('plugin_views_log') . ' (id int(11) NOT NULL auto_increment, ip varchar(20) NOT NULL default "", itemid int(11) NOT NULL default "0", viewtime varchar(32) NOT NULL default "", PRIMARY KEY (id) )'); $this->createOption('silent','Silent mode - No #Display shown in Item (still need to add the skinVar, for use with MostViewed)','yesno','no'); $this->createOption('deletetables','Delete this plugin\'s table and data when uninstalling?','yesno','yes'); $this->createOption('timespan', 'Hours to wait before re-counting visitors', 'text', '2'); } function unInstall() { if ($this->getOption('deletetables') == 'yes') { sql_query('DROP TABLE ' . sql_table('plugin_views') ); sql_query('DROP TABLE ' . sql_table('plugin_views_log') ); } } function hasAdminArea() { return 1; } /** * Adds an entry to the 'Quick Menu' on the Nucleus administration pages. * The entry will link to the commentcontrol admin page */ function event_QuickMenu(&$data) { global $member; if (!($member->isLoggedIn() && $member->isAdmin())) return; array_push( $data['options'], array( 'title' => 'View Counts', 'url' => $this->getAdminURL(), 'tooltip' => 'See the view count of all items' ) ); } function doTemplateVar(&$item, $input) { $itemid = $item->itemid; $remote_ip = ServerVar('REMOTE_ADDR'); $timespan = $this->getOption('timespan') * 3600; $now = time(); // get the current Views count $query = "SELECT views FROM " . sql_table('plugin_views') . " WHERE id=" . $itemid; $result = sql_query($query); $row = mysql_fetch_object($result); $views = intval($row->views); // Only do count updates if "skipcount" is not set if ($input != 'skipcount') { // This takes care of previous items if (mysql_num_rows($result) == 0) { $query = "INSERT INTO " . sql_table('plugin_views') . " (id, views) VALUES('$itemid', '1')"; sql_query($query); //$views = 0; } // end if // Check the views_log table to see if this IP has a viewtime for this item $query = "SELECT viewtime FROM " . sql_table('plugin_views_log') . " WHERE ip='" . $remote_ip . "' AND itemid=" . $itemid; $result = sql_query($query); // No views from this IP in the past X hours, so update the Views count if (mysql_num_rows($result) == 0) { $views++; $this->_updateViewsCount($itemid, $views); $this->_addViewsLog($itemid, $remote_ip, $now); } // end if else { $viewtime = mysql_result($result, 0, 'viewtime'); // It's been longer than X hours, so recount if (($now - $timespan) > $viewtime) { $views++; $this->_updateViewsCount($itemid, $views); $this->_updateViewsLog($itemid, $remote_ip, $now); } } // end else } // end if // Clear logs that are more than X hours old $time = $now - $timespan; $query = "DELETE FROM " . sql_table('plugin_views_log') . " WHERE (viewtime < $time)"; sql_query($query); if ($this->getOption('silent') == 'no') { echo $views; } // end if } function event_PostAddItem($data) { $itemid = $data['itemid']; $query = "INSERT INTO " . sql_table('plugin_views') . " (id, views) VALUES('$itemid', '0')"; sql_query($query); } function event_PostDeleteItem($data) { $itemid = $data['itemid']; $query = "DELETE FROM " . sql_table('plugin_views') . " WHERE id=". $itemid; sql_query($query); $query = "DELETE FROM " . sql_table('plugin_views_log') . " WHERE itemid=". $itemid; sql_query($query); } function _updateViewsCount($itemid, $views) { // update the Views table with the new count $query = "UPDATE " . sql_table('plugin_views') . " SET views='$views' WHERE id=$itemid"; sql_query($query); } function _addViewsLog($itemid, $ip, $time) { // add IP and itemid to views_log table so it won't be recounted for X hours $query = "INSERT INTO " . sql_table('plugin_views_log') . " (ip, itemid, viewtime) VALUES ('$ip', '$itemid', '$time')"; sql_query($query); } function _updateViewsLog($itemid, $ip, $time) { // update the views_log viewtime so it won't be recounted for X hours $query = "UPDATE " . sql_table('plugin_views_log') . " SET viewtime='$time' WHERE ip='$ip'"; sql_query($query); } function doAction($actionType) { global $CONF, $member; if (!($member->isLoggedIn() && $member->isAdmin())) return 'Sorry. not allowed'; if ($actionType == 'resetview'){ $id = requestVar('id'); $query = "UPDATE " . sql_table('plugin_views') . " SET views=0 WHERE id=$id"; sql_query($query); } else if ($actionType == 'resetallview') { $query = "UPDATE " . sql_table('plugin_views') . " SET views=0"; sql_query($query); } $order = requestVar('order'); $sort = requestVar('sort'); header('Location: ' . $CONF['PluginURL'] . 'views/index.php?sort=' . $sort . '&order='.$order); } } ?> - Hoofdstuk: 5. Losse gedachten