(: This application implements a slightly simplified and adapted version of version 1.3 of the
TPC-APP benchmark ( http://www.tpc.org/ ) using the Demaq Queue Language (DQL) :)
(:--------------------------------------- Prolog ---------------------------------------- :)
declare default errorqueue errorMessages;
(:declare namespace soap = "http://schemas.xmlsoap.org/soap/envelope/";:)
declare function local:soapify($message as node()) as node()+
{
{$message}
};
declare function local:desoapify($message as node()) as node()+
{
$message (:TODO:)
(: /soap:Envelope/soap:Body/*:)
};
declare function local:shippingCosts($shippingZone as xs:integer, $volume as xs:double) as xs:double
{
(:TODO: use matrix:)
42.0
};
declare variable $taxRate as xs:decimal := 0.17; (:TODO dynamic lookup:)
(:---------------------------------- Application Code ----------------------------------- :)
(: -------------------------------- Error handling -------------------------------------- :)
create queue errorMessages kind basic mode persistent;
create rule logErrors for errorMessages
(
qs:traceXDM(.),
enqueue message . into outgoingMessages
)
;
(:-------------------- Communication analysis / local message forwarding ---------------- :)
create queue incomingMessages kind incoming interface "http" port "2342"
response outgoingMessages mode persistent;
create rule routeIncomingRequests for incomingMessages
let $payload := local:desoapify(.)
return
if($payload//NewCustomer)
then enqueue message $payload//NewCustomer into newCustomer
else
if($payload//ChangePaymentMethod)
then enqueue message $payload//ChangePaymentMethod into changePayment
else
if($payload//CreateOrder)
then enqueue message $payload//CreateOrder into createOrder
else
if($payload//OrderStatus)
then enqueue message $payload//OrderStatus into orderStatus
else
if($payload//NewProducts)
then enqueue message $payload//NewProducts into newProduct
else
if($payload//CheckProducts)
then enqueue message $payload//CheckProducts into checkProducts
else
if($payload//ProductDetail)
then enqueue message $payload//ProductDetail into productDetail
else
if($payload//firstMessage)
then qs:traceMessage(concat("First message processed at ",
xs:string(current-dateTime())))
else enqueue message Encountered unexpected message in
routeIncomingRequests rule into outgoingMessages
;
(: --------------------- Handle "new customer" requests --------------------:)
create queue newCustomer kind basic mode transient;
create queue createCustomer kind basic mode transient;
create queue customerPovResponses kind basic mode transient;
create queue customers kind basic mode persistent;
create queue addresses kind basic mode persistent;
create property businessName
queue newCustomer fixed value /NewCustomer/BUSINESS_NAME/text();
create slicing customerRequestsBN on businessName require false();
create property addressCity
queue addresses fixed value /address/city/text();
create slicing addressesByCity on addressCity require false();
create rule newCustomerRequest for newCustomer
let $newCustomer := /NewCustomer
let $paymentMethod as xs:string := string($newCustomer/PAYMENT_METHOD)
return
if($paymentMethod eq "PO")
then
(: we must check credit status of customer by invoking POV service:)
let $businessName := qs:property("businessName")
let $poid as xs:integer := xs:integer(data(/NewCustomer/PO_ID))
let $request :=
{$businessName}
{$poid}
return
enqueue message local:soapify($request) into externalCustomerPOV
else
(: simply forward without checking credit status:)
enqueue message . into createCustomer;
create rule customerPovResponseRequest for customerPovResponses
let $result as xs:integer := xs:integer(data(//POV_AuthorizationResult))
let $businessName := //Business_Name/text()
return
if($result gt 0)
then
let $correlatedRequest as node() := qs:slice($businessName,"customerRequestsBN")[last()]
(:add PO ID:)
let $message := {$correlatedRequest/NewCustomer/* ,
{$result}}
return enqueue message $message into createCustomer
else
(:send error notification to user:)
enqueue message POV Checking failed
into errorMessages;
create rule handleCreateCustomer for createCustomer
let $newCustomer := /NewCustomer
let $addr1 := $newCustomer/BILLING_ADDR1/text()
let $addr2 := $newCustomer/BILLING_ADDR2/text()
let $city := $newCustomer/BILLING_CITY/text()
let $state := $newCustomer/BILLING_STATE/text()
let $zip := $newCustomer/BILLING_ZIP/text()
let $country := $newCustomer/BILL_COUNTRY/text()
let $addresses := qs:slice($city, "addressesByCity")
let $previousAddress as node()? := $addresses[/address/addr1/text() eq $addr1 and
/address/addr2/text() eq $addr2 and /address/state/text() eq $state and
/address/zip/text() eq $zip and /address/country/text() eq $country]//id
let $customerID := $newCustomer/CUSTOMER_ID/text()
let $partialCustomerRecord :=
{$customerID}
{$newCustomer/BUSINESS_NAME/text()}
{$newCustomer/BUSINESS_INFO/text()}
{$newCustomer/PASSWORD/text()}
{$newCustomer/CONTACT_F_NAME/text()}
{$newCustomer/CONTACT_L_NAME/text()}
{$newCustomer/CONTACT_PHONE/text()}
{$newCustomer/CONTACT_EMAIL/text()}
{$newCustomer/PAYMENT_METHOD/text()}
{$newCustomer/CREDIT_INFO/text()}
{$newCustomer/PO}
0>
return
(
if(not(empty($previousAddress)))
then
let $customerRecord := {$previousAddress/text()}
{$partialCustomerRecord/*}
return enqueue message $customerRecord into customers
else
(:address not found yet, create new:)
let $newAddressID as xs:string := qs:uniqueID()
let $address :=
{$newAddressID}
{$addr1}
{$addr2}
{$city}
{$state}
{$zip}
{$country}
let $customerRecord := {$newAddressID}
{$partialCustomerRecord/*}
return
(
enqueue message $address into addresses,
enqueue message $customerRecord into customers
)
,
let $customerReply :=
{$customerID}
return enqueue message local:soapify($customerReply) into outgoingMessages
)
;
(: ------------------------------- Change payment requests -----------------------------------:)
create queue changePayment kind basic mode transient;
create queue paymentPovResponses kind basic mode transient;
create queue updatePayment kind basic mode transient;
create slicing property customersByID
queue customers fixed value /customer/customerID/text() require count(qs:history()) eq 1;
create slicing property customersByBusinessName
queue customers fixed value /customer/businessName/text() require count(qs:history()) eq 1;
create slicing property changePaymentRequestsByCustomerID
queue changePayment fixed value /ChangePaymentMethod/C_ID/text() require count(qs:history()) eq 1;
create rule handleChangePaymentRequest for changePayment
let $request := /ChangePaymentMethod
let $customerID := $request/C_ID/text()
let $paymentMethod := $request/PAYMENT_METHOD/text()
let $creditInfo := $request/CREDIT_INFO/text()
let $poid := $request/PO_ID/text()
return
if($paymentMethod eq "PO")
then
let $customerMasterdata := qs:slice($customerID, "customersByID")/customer
let $businessName := $customerMasterdata/businessName/text()
let $povRequest :=
{$businessName}
{$poid}
return enqueue message local:soapify($povRequest) into externalPaymentPOV
else
(: no check needed, directly proceed :)
enqueue message . into updatePayment
;
create rule handlePaymentPovResponse for paymentPovResponses
let $result as xs:integer := xs:integer(data(//POV_AuthorizationResult))
let $businessName := //Business_Name/text()
return
if($result gt 0)
then
let $customer := qs:slice($businessName,"customersByBusinessName")
let $customerID := $customer/customer/customerID/text()
let $correlatedRequest := qs:slice($customerID,"changePaymentRequestsByCustomerID")
let $message := {$correlatedRequest/ChangePaymentMethod/* ,
{$result}}
return enqueue message $message into updatePayment
else
(:send error notification to user:)
enqueue message POV Checking failed
into errorMessages;
create rule handleUpdatePaymentRequest for updatePayment
let $request := /ChangePaymentMethod
let $customerID := $request/C_ID/text()
let $oldMasterData := qs:slice($customerID,"customersByID")/customer
let $paymentMethod := $request/PAYMENT_METHOD/text()
let $creditInfo := $request/CREDIT_INFO/text()
let $newMasterData :=
{($oldMasterData/addressID, $oldMasterData/customerID, $oldMasterData/businessName,
$oldMasterData/businessInfo, $oldMasterData/password, $oldMasterData/contactFirstname,
$oldMasterData/contactLastname, $oldMasterData/contactPhone, $oldMasterData/contactEmail)}
{$paymentMethod}
{$creditInfo}
{$request/PO}
{$oldMasterData/discount}
let $customerReply := {$paymentMethod}
return
(
enqueue message $newMasterData into customers ,
enqueue message local:soapify($customerReply) into outgoingMessages
)
;
(: ------------------------------- Create order requests -----------------------------------:)
create queue createOrder kind basic mode transient;
create queue orderPGEResponses kind basic mode persistent;
create queue orderAddressHandling kind basic mode transient;
create queue costCalculation kind basic mode transient;
create queue capacityCheck kind basic mode transient;
create queue orders kind basic mode persistent;
create queue items kind basic mode persistent;
create queue stocks kind basic mode persistent;
create queue shipping kind basic mode persistent;
create slicing property itemByID
queue items fixed value /item/id/text() require count(qs:history()) eq 1;
create slicing property createOrderByOrderID
queue createOrder fixed value /CreateOrder/ORDER_ID/text() require count(qs:history()) eq 1;
create slicing property addressesByID
queue addresses fixed value /address/id/text() require false();
create slicing property stocksByItemID
queue stocks fixed value /stock/id/text() require count(qs:history()) eq 1;
create rule handleCreateOrderRequests for createOrder
let $request as node() := /CreateOrder
let $customerID := $request/CUSTOMER_ID/text()
let $orderID := $request/ORDER_ID/text()
(:determine payment method:)
let $customerMasterdata as node()? := qs:slice($customerID, "customersByID")/customer
return
if(empty($customerMasterdata))
then
enqueue message Cannot find customer masterdata for customerID {$customerID}
into errorMessages
else
let $paymentMethod := $customerMasterdata/paymentMethod/text()
return
if($paymentMethod eq "CC")
then
(:invoke external payment gateway to check credit card:)
let $pgeRequest :=
{(//CC_TYPE, //CC_EXPIRY, //CC_NAME)}
{$orderID}
return enqueue message local:soapify($pgeRequest) into externalPGE
else enqueue message . into orderAddressHandling (:simply forward:)
;
create rule handleOrderPGEResponses for orderPGEResponses
let $result as xs:integer := xs:integer(data(//PGE_Validate_CardResult))
return
if($result gt 0)
then
let $orderID := //orderID/text()
let $correlatedRequest as node():= qs:slice($orderID,"createOrderByOrderID")/CreateOrder
let $message :=
{$correlatedRequest/*}
{$result}
return enqueue message $message into orderAddressHandling
else
enqueue message External PGE gateway failure into errorMessages
;
(:TODO: factorize address creation code with newcustomer code above:)
create rule handleOrderAddress for orderAddressHandling
let $request as node() := /CreateOrder
let $customerID := $request/CUSTOMER_ID/text()
let $shippingAddress as node()? := $request/SHIPPING_STREET1
return
if(not(empty($shippingAddress)))
then
(: we got a shipping address :)
let $addr1 := $request/SHIPPING_STREET1/text()
let $addr2 := $request/SHIPPING_STREET2/text()
let $city := $request/SHIPPING_CITY/text()
let $state := $request/SHIPPING_STATE/text()
let $zip := $request/SHIPPING_ZIP/text()
(: check whether shipping address already exists in database :)
let $addresses := qs:slice($city, "addressesByCity")
(: get country from customer master data default address (pretty strange!):)
let $customerMasterdata as node() := qs:slice($customerID, "customersByID")/customer
let $defaultAddressID := $customerMasterdata/addressID/text()
let $defaultAddress as node() := qs:slice($defaultAddressID, "addressesByID")/address
let $country as node() := $defaultAddress/country
let $previousAddress as node()? := $addresses[/address/addr1/text() eq $addr1 and
/address/addr2/text() eq $addr2 and /address/state/text() eq $state and
/address/zip/text() eq $zip and /address/country/text() eq $country]//id
return
if(empty($previousAddress))
then
(:add address to queue:)
let $newAddressID as xs:string := qs:uniqueID()
let $address :=
{$newAddressID}
{$addr1}
{$addr2}
{$city}
{$state}
{$zip}
{$country}
return
(
enqueue message $address into addresses,
let $result:={$request/*}{$newAddressID}
return enqueue message $result into costCalculation
)
else
let $addressID := $previousAddress/text()
let $result := {$request/*}{$addressID}
return enqueue message $result into costCalculation
else
let $customerMasterdata as node() := qs:slice($customerID, "customersByID")/customer
let $addressID := $customerMasterdata/addressID/text()
let $address as node() := qs:slice($addressID, "addressesByID")/address
let $result :=
{$request/*}
{$address/addr1/text()}
{$address/addr2/text()}
{$address/city/text()}
{$address/state/text()}
{$address/zip/text()}
{$address/country/text()}
{$addressID}
return enqueue message $result into costCalculation
;
create rule calculateCosts for costCalculation
let $request as node() := /CreateOrder
(:obtain customer discount:)
let $customerID := $request/CUSTOMER_ID/text()
let $customerMasterdata as node() := qs:slice($customerID,"customersByID")/customer
let $discountValue as xs:double := xs:double(data($customerMasterdata/discount))
(:determine line items of order:)
let $orderLines:=
for $itemID at $itemPosition in $request/ITEM_ID/text()
let $quantity as xs:integer := xs:integer(data($request/QTY[$itemPosition]))
let $currentItem as node() := qs:slice($itemID, "itemByID")/item
let $cost as xs:double := xs:double(data($currentItem/cost))
let $dimensions := $currentItem/dimensions
let $length as xs:double := xs:double($dimensions/length)
let $width as xs:double := xs:double($dimensions/width)
let $height as xs:double := xs:double($dimensions/height)
let $volume as xs:double := $length * $width * $height
return
{$itemPosition}
{$itemID}
{$quantity}
{$volume*$quantity}
{$cost}
let $subTotal as xs:double := sum($orderLines/(cost*quantity))*(1-$discountValue div 100)
let $tax as xs:double := $subTotal * $taxRate
let $shipVolume as xs:double := sum($orderLines/totalVolume)
let $shippingCost as xs:double := local:shippingCosts(23, $shipVolume) (:TODO: zone:)
let $shippingType := $request/SHIPPING_TYPE/text()
let $shippingAddressID := $request/addressID/text()
let $authentificationID :=
if(empty($request/authentificationID))
then "PO"
else $request/authentificationID/text()
let $totalCosts as xs:double := $subTotal + $tax + $shippingCost
let $orderID := $request/ORDER_ID/text()
let $result :=
{$orderID}
{$customerID}
{current-dateTime()}
{$subTotal}
{$tax}
{$totalCosts}
{$shippingCost}
{$shippingType}
{$shippingAddressID}
{$authentificationID}
{$discountValue}
{$orderLines}
{
(:propagate shipping information:)
$request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY,
$request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME
}
return enqueue message $result into capacityCheck
;
create rule checkCapacity for capacityCheck
let $request := /order
(:check availability for all items:)
let $orderLines as node()* :=
for $orderLine in $request/orderLines/orderLine
let $itemID := $orderLine/itemID/text()
let $orderQuantity as xs:integer := xs:integer($orderLine/quantity)
let $stockItem as node()? := qs:slice($itemID, "stocksByItemID")/stock
let $stockQuantity as xs:integer :=
if(empty($stockItem))
then
0
else
xs:integer($stockItem/quantity)
return
{
$orderLine/*,
{
if($stockQuantity ge $orderQuantity)
then
"PENDING"
else
"BACK_ORDERED"
}
}
let $result :=
let $isBackorder as xs:boolean := not(empty($orderLines[status/text() eq "BACK_ORDERED"]))
return
{$request/id, $request/customerID, $request/date, $request/subTotal, $request/tax,
$request/total, $request/shippingCost, $request/shippingType,
$request/shippingAddressID, $request/authentificationID, $request/discount}
{$orderLines}
{
(:propagate shipping information:)
$request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY,
$request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME
}
{
if($isBackorder)
then
"BACK_ORDERED"
else
"PENDING"
}
return enqueue message $result into orders
;
create rule triggerShipping for orders
let $request as node() := /order
let $orderID := $request/id/text()
let $requestedTime := "2009-12-12"
let $status := $request/status/text()
let $street1 := $request/SHIPPING_STREET1/text()
let $street2 := $request/SHIPPING_STREET2/text()
let $city := $request/SHIPPING_CITY/text()
let $state := $request/SHIPPING_STATE/text()
let $zip := $request/SHIPPING_ZIP/text()
let $country := $request/SHIPPING_COUNTRY/text()
let $result :=
{$orderID}
{$requestedTime}
{$status}
{$street1}
{$street2}
{$city}
{$state}
{$zip}
{$country}
return enqueue message $result into shipping
;
create rule customerConfirmation for orders
let $request as node() := /order
let $orderID := $request/id/text()
let $status := $request/status/text()
let $total := $request/total/text()
let $items as node()*:=
for $item as node() in $request/orderLines/orderLine
return - {$item/id, $item/quantity, $item/cost, $item/status}
let $result :=
{$orderID}
{$status}
{$total}
{$request/total, $request/tax, $request/shippingCost, $request/discount}
{$items}
return enqueue message $result into outgoingMessages
;
(: ---------------------------------- Shipping process --------------------------------------:)
create queue shippingSNEResponse kind basic mode persistent;
create slicing property shippingsByOrderID
queue shipping fixed value /RequestShipping/orderID/text() require count(qs:history()) eq 1;
create slicing property ordersByOrderID
queue orders fixed value /order/id/text() require count(qs:history()) eq 1;
create rule handlePendingShippingRequest for shipping
let $request as node() := /RequestShipping
return
if($request/status/text() eq "PENDING")
then
let $result :=
{$request/orderID/text()}
{$request/street1/text()}
{$request/street2/text()}
{$request/city/text()}
{$request/state/text()}
{$request/zip/text()}
{$request/country/text()}
return enqueue message local:soapify($result) into externalSNE
else () (:handled by another rule:)
;
create rule handleshippingSNEResponse for shippingSNEResponse
let $request as node() := local:desoapify(.)//SNE_ShippingResult
let $orderID := $request/ORDER_ID/text()
let $trackingNumber as xs:integer := xs:integer($request/SNE_TrackingNumber)
let $correlatedShipping as node() := qs:slice($orderID, "shippingsByOrderID")/RequestShipping
return
if($trackingNumber gt 0)
then
(:everything worked, order is shipped, store in shipping queue (simulate inplace update):)
let $result :=
{$orderID}
SHIPPED
{current-dateTime()}
{
(: include all data from correlatedShipping request :)
$correlatedShipping/requestedTime, $correlatedShipping/street1,
$correlatedShipping/street2 , $correlatedShipping/city,
$correlatedShipping/state , $correlatedShipping/zip, $correlatedShipping/country
}
return enqueue message $result into shipping
else
enqueue message qs:slice($orderID, "shippingsByOrderID") into shipping (: try again :)
;
create rule handleBackorderedShippingRequest for shipping
let $request as node() := /RequestShipping
return
if($request/status/text() eq "BACK_ORDERED")
then
let $orderID := $request/orderID/text()
(: NOTE we loose Vpartitioning here by accessing orders:)
let $correlatedOrder as node() := qs:slice($orderID, "ordersByOrderID")/order
let $result :=
{$request/requestedTime/text()}
{$request/orderID/text()}
{
(: we can handle several backorders at once :)
for $backorderedItem as node() in $correlatedOrder/orderLines/orderLine
where $backorderedItem/status/text() eq "BACK_ORDERED"
return ({$backorderedItem/itemID/text()},
{$backorderedItem/quantity/text()})
}
{$request/street1/text()}
{$request/street2/text()}
{$request/city/text()}
{$request/state/text()}
{$request/zip/text()}
{$request/country/text()}
return enqueue message $result into stockManagement
else () (:handled by another rule:)
;
(: ---------------------------------- Stock management --------------------------------------:)
create queue stockManagement kind basic mode persistent;
create queue stockICEResponse kind basic mode persistent;
create queue stockAndShippingUpdates kind basic mode transient;
create slicing property stockManagementByOrderID
queue stockManagement fixed value /BackorderedRequest/SHIPPING_O_ID/text()
require count(qs:history()) eq 1;
create rule handleBackorderedStock for stockManagement
let $request as node() := /BackorderedRequest
let $result :=
{
$request/SHIPPING_REQUEST_TIME, $request/SHIPPING_O_ID,
for $itemID as node() at $position in $request/REORDER_I_ID
let $quantity as node() := $request/REORDER_QTY[$position]
return ({$itemID/text()},{$quantity/text()})
}
return enqueue message local:soapify($result) into externalICE
;
create rule handleStockICEResponse for stockICEResponse
let $request as node() := local:desoapify(.)//ICE_ReorderResponse
let $result := $request/ICE_ReorderResult/text()
let $orderID := $request/SHIPPING_O_ID/text()
return
if($result eq "OK")
then
enqueue message $request into stockAndShippingUpdates (:forward for processing:)
else
(:restart transaction by re-enqueuing initial request into stock management queue:)
let $correlatedRequest as node() := qs:slice($orderID, "stockManagementByOrderID")
return enqueue message $correlatedRequest into stockManagement
;
create rule updateStock for stockAndShippingUpdates
let $request as node() := /ICE_ReorderResponse
let $orderID := $request/SHIPPING_O_ID/text()
(: NOTE we loose Vpartitioning here by accessing orders:)
let $correlatedOrder as node() := qs:slice($orderID, "ordersByOrderID")/order
for $backorderedItem as node() in $correlatedOrder/orderLines/orderLine
where $backorderedItem/status/text() eq "BACK_ORDERED"
return
(: no need to access stocks here as tpc-app blindly sets a new value :)
(:TODO: should be random value between 20 and 30:)
let $result :=
{$backorderedItem/itemID/text()}
23
return enqueue message $result into stocks
;
create rule updateShipping for stockAndShippingUpdates
let $request as node() := /ICE_ReorderResponse
let $orderID := $request/SHIPPING_O_ID/text()
let $correlatedShipping as node() := qs:slice($orderID, "shippingsByOrderID")/RequestShipping
let $result :=
{$correlatedShipping/orderID, $correlatedShipping/requestedTime}
PENDING
{$correlatedShipping/street1, $correlatedShipping/street2, $correlatedShipping/city,
$correlatedShipping/state, $correlatedShipping/zip, $correlatedShipping/country }
return enqueue message $result into shipping (: try again :)
;
(: ------------------------------------ Order status ----------------------------------------:)
create queue orderStatus kind basic mode transient;
create queue verifiedOrderStatus kind basic mode transient;
create slicing property ordersByCustomerID
queue orders fixed value /order/customerID/text() require false();
create rule checkOrderStatusPassword for orderStatus
let $request as node() := /OrderStatus
let $customerID := $request/CUSTOMER_ID/text()
let $suppliedPassword := $request/PASSWD/text()
let $businessName := $request/BUSINESS_NAME/text()
let $orderCount := $request/ORDER_COUNT/text()
let $customerMasterdata as node()? := qs:slice($customerID, "customersByID")/customer
return
if(empty($customerMasterdata))
then
enqueue message Cannot access customer masterdata for customerID {$customerID}
into errorMessages
else if($suppliedPassword eq $customerMasterdata/password/text())
then
(:password is OK, forward to verifiedOrderStatus queue:)
let $result :=
{$customerMasterdata}
{$orderCount}
return enqueue message $result into verifiedOrderStatus
else
enqueue message Got wrong password for customer into errorMessages
;
create rule compileOrderStatus for verifiedOrderStatus
let $request as node() := /OrderStatus
let $customer as node() := $request/customer
let $customerID := $customer/customerID/text()
let $addressID := $customer/addressID/text()
let $customerAddress as node() := qs:slice($addressID, "addressesByID")/address
let $orderCount as xs:integer := xs:integer($request/orderCount)
let $orders as node()* := qs:slice($customerID, "ordersByCustomerID")/order
let $relevantOrders as node()* := reverse($orders)[position() le $orderCount]
let $result :=
{$customer/contactFirstname/text()}
{$customer/contactLastname/text()}
{$customer/contactPhone/text()}
{$customer/contactEmail/text()}
{$customerAddress/addr1/text()}
{$customerAddress/addr2/text()}
{$customerAddress/city/text()}
{$customerAddress/state/text()}
{$customerAddress/zip/text()}
{$customerAddress/country/text()}
{
for $order as node() in $orders
let $orderID := $order/id/text()
let $shipping as node()? := qs:slice($orderID, "shippingsByOrderID")/RequestShipping
let $processingDate := $shipping/processingDate/text()
return
(
{$orderID},
{$order/date/text()},
{$order/subTotal/text()},
{$order/tax/text()},
{$order/total/text()},
{$order/shippingType/text()},
{$processingDate},
{$order/status/text()},
{$order/shippingCost/text()},
{$order/discount/text()},
(:propagate shipping information:)
$request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY,
$request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME,
(:return all order lines with item details :)
for $orderLine as node() in $order/orderLines/orderLine
let $itemID := $orderLine/itemID/text()
let $itemMasterdata as node() := qs:slice($itemID, "itemByID")/item
return
(
{$itemID},
{$itemMasterdata/title/text()},
{$itemMasterdata/publisher/text()},
{$orderLine/cost/text()},
{$orderLine/quantity/text()},
{$orderLine/status/text()}
)
)
}
return enqueue message local:soapify($result) into outgoingMessages
;
(: ---------------------------------- New Item Service --------------------------------------:)
(: The implementation of this part differs from TPC-app, which only updates the publication
timestamp of an existing item. This service really adds a new item to the items queue (or
overwrites an existing entry with new data. :)
create queue newProduct kind basic mode persistent;
create queue createProduct kind basic mode transient;
create queue authors kind basic mode persistent;
create slicing property authorsByLastname
queue authors fixed value /author/lastname/text() require false();
create rule checkAuthor for newProduct
let $request as node() := /NewProducts
let $authorLastname := $request/AUTHOR_LNAME/text()
let $authorFirstname := $request/AUTHOR_FNAME/text()
let $author as node()? := qs:slice($authorLastname, "authorsByLastname")/author
return
(
if(empty($author))
then
let $authorID as xs:string := qs:uniqueID()
let $newAuthor :=
{$authorID}
{$authorFirstname}
{$authorLastname}
return
(
(:add master data for author, include authorID in NewProduct message:)
enqueue message $newAuthor into authors,
enqueue message {$request/*, $newAuthor/authorID}
into createProduct)
else
(: we already have masterdata for this author, add ID:)
enqueue message {$request/*, $author/authorID} into createProduct
)
;
create rule createNewProduct for createProduct
let $request as node() := /NewProducts
let $itemID as xs:integer := xs:integer($request/ITEM_ID)
let $item := -
{$itemID}
{$request/ITEM_TITLE/text()}
{current-dateTime()}
{$request/ITEM_PUBLISHER/text()}
{$request/ITEM_SUBJECT/text()}
{$request/ITEM_DESC/text()}
{$request/ITEM_SRP/text()}
{$request/ITEM_COST/text()}
{$request/ITEM_AVAIL/text()}
{$request/I_ISBN/text()}
{$request/ITEM_PAGE/text()}
{$request/ITEM_BACKING/text()}
{$request/ITEM_DIMENSIONS/ITEM_WIDTH/text()}
{$request/ITEM_DIMENSIONS/ITEM_HEIGHT/text()}
{$request/ITEM_DIMENSIONS/ITEM_LENGTH/text()}
{$request/authorID}
let $result := {$itemID}
return
(
enqueue message local:soapify($result) into outgoingMessages,
enqueue message $item into items
)
;
(: -------------------------------- Check Products Service ------------------------------------:)
(: this service is called "new products web service" in tpc-app and has been renamed here for
disambiguation with the new item web service :)
create queue checkProducts kind basic mode persistent;
create slicing property itemsBySubject
queue items fixed value /item/subject/text() require false();
create slicing property authorsByID
queue authors fixed value /author/authorID/text() require count(qs:history()) ge 1;
create rule handleCheckProducts for checkProducts
let $request as node() := /CheckProducts
let $subject := $request/Subject/text()
let $itemlimit as xs:integer := xs:integer($request/ItemLimit)
let $cutoff as xs:integer := xs:integer($request/CutOff)
let $formatString as xs:string := concat("PT",$cutoff,"M") (:OMG:)
let $deadline as xs:dateTime := current-dateTime() - xs:dayTimeDuration($formatString)
let $items as node()* := qs:slice($subject, "itemsBySubject")
let $result as node() :=
{qs:hostName()}
{
for $item as node() in reverse($items)[position() le $itemlimit]
where qs:timestamp($item) ge $deadline
return
let $authorID := $item//authorID/text()
let $author as node() := qs:slice($authorID, "authorsByID")/author
return
(
{$item//id/text()},
{$item//title/text()},
{$author/firstname/text()},
{$author/lastname/text()}
)
}
return enqueue message $result into outgoingMessages
;
(: ------------------------------- Product Details Service ----------------------------------:)
create queue productDetail kind basic mode persistent;
create rule handleProductDetail for productDetail
let $request as node() := /ProductDetail
let $result as node() :=
{
for $itemID in $request/ITEM_ID/text()
let $item as node() := qs:slice($itemID, "itemByID")/item
let $authorID := $item/authorID/text()
let $author as node() := qs:slice($authorID, "authorsByID")/author
return
(
{$itemID},
{$item/title/text()},
{$author/firstname/text()},
{$author/lastname/text()},
{$item/publishingDate/text()},
{$item/publisher/text()},
{$item/subject/text()},
{$item/description/text()},
{$item/cost/text()},
{$item/SRP/text()},
{$item/availability/text()},
{$item/ISBN/text()},
{$item/page/text()},
{$item/backing/text()},
{$item/dimensions/length/text()}
{$item/dimensions/width/text()}
{$item/dimensions/height/text()}
,
{$item/image/text()}
)
}
return enqueue message $result into outgoingMessages
;
(: -------------------------------- "External" services -------------------------------------:)
(: ------------------------------- "External" POV service -----------------------------------:)
(: TODO: factorize! :)
create queue externalCustomerPOV kind basic mode persistent;
create queue externalPaymentPOV kind basic mode persistent;
create rule externalCPOVRequest for externalCustomerPOV
let $businessName := //Business_Name/text()
let $poid as xs:integer := xs:integer(data(//PO_ID))
let $response :=
1
{//Business_Name} (:Include business name for correlation:)
return enqueue message local:soapify($response) into customerPovResponses;
create rule externalPPOVRequest for externalPaymentPOV
let $businessName := //Business_Name/text()
let $poid as xs:integer := xs:integer(data(//PO_ID))
let $response :=
1
{//Business_Name} (:Include business name for correlation:)
return enqueue message local:soapify($response) into paymentPovResponses;
(: ------------------------------- "External" PGE service -----------------------------------:)
create queue externalPGE kind basic mode persistent;
create rule handleExternalPGE for externalPGE
let $orderID := //orderID
let $response :=
1
{$orderID}
return enqueue message local:soapify($response) into orderPGEResponses;
(: ------------------------------- "External" SNE service -----------------------------------:)
create queue externalSNE kind basic mode persistent;
(:TODO: add image data:)
create rule handleExternalSNE for externalSNE
let $response :=
{//ORDER_ID}
17
BASE64ENCODINBASE64ENCODINBASE64ENCODINGGGBASE64ENCODING
return enqueue message local:soapify($response) into shippingSNEResponse
;
(: ------------------------------- "External" ICE service -----------------------------------:)
create queue externalICE kind basic mode persistent;
(: must retain all messages :)
create slicing property ICElog
queue externalICE fixed value //SHIPPING_O_ID/text() require false();
create rule handleExternalICE for externalICE
let $response :=
OK
{//SHIPPING_O_ID} (:added for correlation to initial request:)
return enqueue message $response into stockICEResponse
;