Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
122 lines (113 sloc) 4.71 KB
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace org.accordproject.perishablegoods
import org.accordproject.cicero.contract.*
import org.accordproject.cicero.runtime.*
import org.accordproject.time.*
import org.accordproject.money.MonetaryAmount
// Auxiliary function calculating penalty from temperature readings
define function calculateTempPenalty(minTemperature : Double,
maxTemperature : Double,
penaltyFactor : Double,
readings : SensorReading[]) : Double {
let tempReadings = foreach x in readings return x.centigrade;
// find the lowest temperature reading
let lowestReading = min(tempReadings);
// find the highest temperature reading
let highestReading = max(tempReadings);
if lowestReading < minTemperature
then return (minTemperature - lowestReading) * penaltyFactor
else if highestReading > maxTemperature
then return (highestReading - maxTemperature) * penaltyFactor
else return 0.0
}
// Auxiliary function calculating penalty from humidity readings
define function calculateHumPenalty(minHumidity : Double,
maxHumidity : Double,
penaltyFactor : Double,
readings : SensorReading[]) : Double {
let humReadings = foreach x in readings return x.humidity;
// find the lowest humidity reading
let lowestReading = min(humReadings);
// find the highest temperature reading
let highestReading = max(humReadings);
if lowestReading < minHumidity
then return (minHumidity - lowestReading) * penaltyFactor
else if highestReading > maxHumidity
then return (highestReading - maxHumidity) * penaltyFactor
else return 0.0
}
define function unitsWithinBounds(units : Integer, min : Integer, max : Integer) : Boolean {
return min <= units
and units <= max
}
contract PerishableGoods over PerishableGoodsContract {
clause payout(request : ShipmentReceived) : PriceCalculation emits PaymentObligation {
// Guard against unitCount outside the bounds specified in the clause
enforce unitsWithinBounds(request.unitCount, contract.minUnits, contract.maxUnits)
else throw ErgoErrorResponse{ message: "Units received out of range for the contract" };
let zeroMoney = MonetaryAmount{
doubleValue: 0.0,
currencyCode: contract.unitPrice.currencyCode
};
// Guard against requests for payout after the dueDate
enforce isBefore(now(),contract.dueDate)
else
return PriceCalculation{
shipment : request.shipment,
totalPrice : zeroMoney,
penalty : zeroMoney,
late : true
};
// Guard against missing temperature readings
let readings : SensorReading[] = request.shipment.sensorReadings ?? [];
enforce readings != []
else throw ErgoErrorResponse{ message : "No temperature readings received"};
// Calculates payout
let payOut = contract.unitPrice.doubleValue * integerToDouble(request.unitCount);
// Calculates penalty if any
let penalty =
calculateTempPenalty(contract.minTemperature,
contract.maxTemperature,
contract.penaltyFactor,
readings)
+ calculateHumPenalty(contract.minHumidity,
contract.maxHumidity,
contract.penaltyFactor,
readings);
// Returns a Price calculation, applying any penalities
let totalPenalty = MonetaryAmount{
doubleValue: penalty * integerToDouble(request.unitCount),
currencyCode: contract.unitPrice.currencyCode
};
let totalPrice = MonetaryAmount{
doubleValue: max([payOut - totalPenalty.doubleValue, 0.0]),
currencyCode: contract.unitPrice.currencyCode
};
emit PaymentObligation{
contract: contract,
promisor: some(contract.importer),
promisee: some(contract.grower),
deadline: none,
amount: totalPrice,
description: contract.importer.partyId ++ " should pay shipment amount to " ++ contract.grower.partyId
};
return PriceCalculation{
shipment : request.shipment,
totalPrice : totalPrice,
penalty : totalPenalty,
late : false
}
}
}