| 1 |
<?php
|
| 2 |
/**
|
| 3 |
* $Id$
|
| 4 |
*
|
| 5 |
* Drupal carbon CRAG module
|
| 6 |
*
|
| 7 |
* Created on 17-May-2007
|
| 8 |
*
|
| 9 |
* @file
|
| 10 |
*
|
| 11 |
* Provides the settlement reports that are unique to the Carbon
|
| 12 |
* Rationing Action Groups http://www.carbonrationing.org.uk.
|
| 13 |
*
|
| 14 |
* The file is included by carbon_report_page
|
| 15 |
*
|
| 16 |
* The functions in this file are not used or required for
|
| 17 |
* general use of the carbon module.
|
| 18 |
*
|
| 19 |
*/
|
| 20 |
|
| 21 |
|
| 22 |
class CarbonCrag
|
| 23 |
{
|
| 24 |
var $header = null ;
|
| 25 |
var $sampleRecord = null ;
|
| 26 |
var $params = null ;
|
| 27 |
|
| 28 |
function initialize(&$params)
|
| 29 |
{
|
| 30 |
if (empty($params["cost"])) $params["cost"] = 0.05 ;
|
| 31 |
$this->params = $params ;
|
| 32 |
require_once(CARBON_PATH ."/carbon_transfer.inc"); // carbon purchases/sales (used by crags)
|
| 33 |
}
|
| 34 |
|
| 35 |
/**
|
| 36 |
* create column headers in array this->header
|
| 37 |
*/
|
| 38 |
|
| 39 |
function initHeader()
|
| 40 |
{
|
| 41 |
$this->header = array(array(
|
| 42 |
"data" => t("account name"),"field" => "title","sort" => "asc"));
|
| 43 |
}
|
| 44 |
|
| 45 |
function writeOutput($map, $uid, $firstdate, $lastdate)
|
| 46 |
{
|
| 47 |
$target = $this->params["target"];
|
| 48 |
|
| 49 |
// for each item in the map, change the key and value to title and amount ;
|
| 50 |
$footprint_by_title = array();
|
| 51 |
|
| 52 |
$rows = array();
|
| 53 |
$debitCarbon = 0 ; $creditCarbon = 0 ;
|
| 54 |
$debitCash = 0 ; $debitCredit = 0 ;
|
| 55 |
|
| 56 |
$results = array();
|
| 57 |
foreach ($map as $nid => $account)
|
| 58 |
{
|
| 59 |
$stamps = carbon_stamp_array_load($nid, null, null);
|
| 60 |
|
| 61 |
$footprint = carbon_calculate_total($account, $stamps, $firstdate, $lastdate);
|
| 62 |
|
| 63 |
$results[] = $footprint;
|
| 64 |
|
| 65 |
$over = 0 ;
|
| 66 |
if (!empty($target))
|
| 67 |
{
|
| 68 |
$actual = sprintf("%.0f",$footprint->grand_co2);
|
| 69 |
$over = $actual - $target ;
|
| 70 |
|
| 71 |
if ($over > 0)
|
| 72 |
$debitCarbon += $over ;
|
| 73 |
else
|
| 74 |
$creditCarbon -= $over ;
|
| 75 |
}
|
| 76 |
}
|
| 77 |
|
| 78 |
if ($creditCarbon > $debitCarbon)
|
| 79 |
{
|
| 80 |
$creditPrice = $this->params["cost"]*$debitCarbon/$creditCarbon ;
|
| 81 |
}
|
| 82 |
else
|
| 83 |
{
|
| 84 |
$creditPrice = $this->params["cost"] ;
|
| 85 |
}
|
| 86 |
$debitPrice = $this->params["cost"] ; // debtors pay the same
|
| 87 |
|
| 88 |
$nummembers = 0 ;
|
| 89 |
$grouptotal = 0 ;
|
| 90 |
foreach ($results as $footprint)
|
| 91 |
{
|
| 92 |
$nummembers++ ;
|
| 93 |
$actual = sprintf("%.0f",$footprint->grand_co2);
|
| 94 |
$grouptotal += $footprint->grand_co2 ;
|
| 95 |
$under = $target - $actual ;
|
| 96 |
if ($under < 0)
|
| 97 |
{
|
| 98 |
$debitCash += $cash = $this->params["cost"] * $under ;
|
| 99 |
}
|
| 100 |
else
|
| 101 |
{
|
| 102 |
$creditCash -= $cash = $creditPrice * $under ;
|
| 103 |
}
|
| 104 |
$rows[] = array($footprint->account->account_link, $actual, $this->sign($under), $this->sign(sprintf("%.2f",$cash)));
|
| 105 |
|
| 106 |
}
|
| 107 |
$header = array(
|
| 108 |
array("data" => t("account name")),
|
| 109 |
array("data" => t("Emissions Kg")),
|
| 110 |
array("data" => t("Credit or Debit Kg")),
|
| 111 |
array("data" => t("Credit of Debit £"))
|
| 112 |
);
|
| 113 |
if ($nummembers == 0)
|
| 114 |
$comment = "No members" ;
|
| 115 |
else
|
| 116 |
$comment = t("Total emissions are !carbon-grand Kg. Average emissions per person are !carbon-average Kg." .
|
| 117 |
"</p><p>".
|
| 118 |
"Over emitters: the carbon price is set at !debit-carbon-price. ".
|
| 119 |
"The total carbon debt is !debit-carbon Kg. ".
|
| 120 |
"The total money to pay is !debit-cash. ".
|
| 121 |
"</p><p>".
|
| 122 |
"Under emitters: the carbon price is !credit-carbon-price. ".
|
| 123 |
"The total carbon credit is !credit-carbon Kg. ".
|
| 124 |
"If under emitters paid, total money that might be received: !credit-cash.",
|
| 125 |
|
| 126 |
array(
|
| 127 |
"!carbon-grand"=>sprintf("%.2f",$grouptotal),
|
| 128 |
"!carbon-average"=>sprintf("%.2f",$grouptotal/$nummembers),
|
| 129 |
"!credit-carbon"=>$creditCarbon,
|
| 130 |
"!credit-cash"=>sprintf("%.2f",$creditCash),
|
| 131 |
"!credit-carbon-price"=>sprintf("%.2f",$creditPrice),
|
| 132 |
|
| 133 |
"!debit-carbon"=>$debitCarbon,
|
| 134 |
"!debit-cash"=>sprintf("%.2f",$debitCash),
|
| 135 |
"!debit-carbon-price"=>sprintf("%.2f",$debitPrice)));
|
| 136 |
|
| 137 |
$daterange = _format_date_range($firstdate, $lastdate);
|
| 138 |
if (!empty($target))
|
| 139 |
{
|
| 140 |
$output .= "<p>Suggested CRAG Settlement for ". $daterange. ". Target is ".$target. "Kg CO2.</p>";
|
| 141 |
$output .= "<p>".$comment."</p>" ;
|
| 142 |
}
|
| 143 |
$output .= theme("table", $header, $rows);
|
| 144 |
return $output ;
|
| 145 |
}
|
| 146 |
|
| 147 |
|
| 148 |
function allocate(&$map, $uid, $org, $firstdate, $lastdate, $target, $carbonCost)
|
| 149 |
{
|
| 150 |
// calculate the footprints
|
| 151 |
foreach ($map as $nid => $footprint)
|
| 152 |
{
|
| 153 |
$t = new CarbonTransaction();
|
| 154 |
$t->initialize();
|
| 155 |
$t->title = "allocation" ;
|
| 156 |
$t->carbon = $target ;
|
| 157 |
$t->cost = null ;
|
| 158 |
$t->seller = 1 ; // admin
|
| 159 |
$t->buyer = $nid ; //
|
| 160 |
$t->valuedate = $lastdate ;
|
| 161 |
$transactions[] = $t ;
|
| 162 |
}
|
| 163 |
foreach ($transactions as $t)
|
| 164 |
{
|
| 165 |
$r = node_save($t);
|
| 166 |
drupal_set_message($t->toString($map), "info");
|
| 167 |
}
|
| 168 |
}
|
| 169 |
|
| 170 |
function deduct(&$map, $uid, $org, $firstdate, $lastdate, $target, $carbonCost)
|
| 171 |
{
|
| 172 |
// calculate the footprints
|
| 173 |
foreach ($map as $nid => $account)
|
| 174 |
{
|
| 175 |
$stamps = carbon_stamp_array_load($nid, null, null);
|
| 176 |
$footprints[$account->nid] = carbon_calculate_total($account, $stamps, $firstdate, $lastdate);
|
| 177 |
|
| 178 |
$t->initialize();
|
| 179 |
$t->title = "deduct" ;
|
| 180 |
$t->carbon = - $footprints[$account->nid];
|
| 181 |
$t->cost = null ;
|
| 182 |
$t->seller = 1 ; // admin
|
| 183 |
$t->buyer = $nid ; //
|
| 184 |
$t->valuedate = $lastdate ;
|
| 185 |
$transactions[] = $t ;
|
| 186 |
}
|
| 187 |
foreach ($transactions as $t)
|
| 188 |
{
|
| 189 |
$r = node_save($t);
|
| 190 |
drupal_set_message($t->toString($map), "info");
|
| 191 |
}
|
| 192 |
}
|
| 193 |
|
| 194 |
/**
|
| 195 |
* execute settlement by
|
| 196 |
* 1. adding quota to account at start period.
|
| 197 |
* 2. settle carbon amongst CRAG members
|
| 198 |
*
|
| 199 |
* @param key-value map of account number/footprint
|
| 200 |
* @param unknown_type $org
|
| 201 |
*/
|
| 202 |
|
| 203 |
function settle(&$map, $uid, $org, $firstdate, $lastdate, $target, $carbonCost)
|
| 204 |
{
|
| 205 |
$footprints = array();
|
| 206 |
|
| 207 |
// calculate the footprints
|
| 208 |
foreach ($map as $nid => $account)
|
| 209 |
{
|
| 210 |
$stamps = carbon_stamp_array_load($nid, null, null);
|
| 211 |
$footprints[$account->nid] = carbon_calculate_total($account, $stamps, $firstdate, $lastdate);
|
| 212 |
}
|
| 213 |
$debitCarbon = 0 ; $creditCarbon = 0 ;
|
| 214 |
$debitCash = 0 ; $debitCredit = 0 ;
|
| 215 |
|
| 216 |
|
| 217 |
if (empty($target))
|
| 218 |
return ;
|
| 219 |
|
| 220 |
$numMembers = 0 ;
|
| 221 |
$creditors = array() ;
|
| 222 |
$debtors = array() ;
|
| 223 |
|
| 224 |
foreach ($footprints as $nid => $f)
|
| 225 |
{
|
| 226 |
$numMembers++ ;
|
| 227 |
$actual = $f->grand_co2;
|
| 228 |
$f->CO2_above_target = $actual - $target ;
|
| 229 |
|
| 230 |
if ($f->CO2_above_target > 0)
|
| 231 |
{
|
| 232 |
$debitCarbon += $f->CO2_above_target;
|
| 233 |
$debtors[$nid] = $f->CO2_above_target ;
|
| 234 |
}
|
| 235 |
if ($f->CO2_above_target < 0)
|
| 236 |
{
|
| 237 |
$creditCarbon -= $f->CO2_above_target;
|
| 238 |
$creditors[$nid] = -$f->CO2_above_target ;
|
| 239 |
}
|
| 240 |
}
|
| 241 |
|
| 242 |
if (count($creditors) >= $numMembers)
|
| 243 |
{
|
| 244 |
drupal_set_message("All accounts in credit, no settlement required.");
|
| 245 |
return ;
|
| 246 |
}
|
| 247 |
|
| 248 |
if (count($debtors) >= $numMembers)
|
| 249 |
{
|
| 250 |
drupal_set_message("All accounts in debit, settlement not possible.");
|
| 251 |
return ;
|
| 252 |
}
|
| 253 |
|
| 254 |
/*
|
| 255 |
*
|
| 256 |
p(. If total debts is greater than total credits (i.e. there are enough surplus units and the group has come in under target)
|
| 257 |
|
| 258 |
p((. number of credits that each CRAGger in debt must purchase from ever other CRAGger [1..n] if the CRAGger is in credit
|
| 259 |
|
| 260 |
p(((. = size of debt * number of credits held by CRAGger [1..n] in credit / total credits
|
| 261 |
|
| 262 |
p(. else (the group has missed its target)
|
| 263 |
|
| 264 |
p((. number of credits that each CRAGger in debt must purchase from every other CRAGger [1..n] if s/he is in credit
|
| 265 |
|
| 266 |
p(((. = size of debt * number of credits held by CRAGger [1..n] / total debts
|
| 267 |
|
| 268 |
CRAGger [1..n] represents all the other members of the CRAG.
|
| 269 |
|
| 270 |
*/
|
| 271 |
$group_met_target = $creditCarbon < $debitCarbon ;
|
| 272 |
$total_purchase = 0 ;
|
| 273 |
$transactions = array();
|
| 274 |
foreach ($debtors as $did => $d)
|
| 275 |
{
|
| 276 |
// go to each account with carbon credit and purchase carbon
|
| 277 |
foreach ($creditors as $cid => $c)
|
| 278 |
{
|
| 279 |
if ($group_met_target)
|
| 280 |
$purchase = $footprints[$did]->CO2_above_target * -$footprints[$cid]->CO2_above_target / $debitCarbon ;
|
| 281 |
else
|
| 282 |
$purchase = $footprints[$did]->CO2_above_target * -$footprints[$cid]->CO2_above_target / $creditCarbon ;
|
| 283 |
|
| 284 |
$debtors[$did] -= $purchase ;
|
| 285 |
$creditors[$cid] -= $purchase ;
|
| 286 |
$total_purchase += $purchase ;
|
| 287 |
$t = new CarbonTransaction();
|
| 288 |
$t->initialize();
|
| 289 |
$t->title = "settlement" ;
|
| 290 |
$t->carbon = $purchase ;
|
| 291 |
$t->cost = $purchase * $carbonCost ;
|
| 292 |
$t->seller = $cid ; // creditors account id
|
| 293 |
$t->buyer = $did ; // debtors account id
|
| 294 |
$t->valuedate = $lastdate ;
|
| 295 |
$transactions[] = $t ;
|
| 296 |
}
|
| 297 |
}
|
| 298 |
|
| 299 |
// commit all the transactions as a batch
|
| 300 |
|
| 301 |
foreach ($transactions as $t)
|
| 302 |
{
|
| 303 |
$r = node_save($t);
|
| 304 |
drupal_set_message($t->toString($map), "info");
|
| 305 |
}
|
| 306 |
}
|
| 307 |
|
| 308 |
function setTitle($org, $user, $firstdate, $lastdate)
|
| 309 |
{
|
| 310 |
$title = empty($org) ? "" : "!org " ;
|
| 311 |
$title .= "CRAG settlement for !dates" ;
|
| 312 |
$daterange = _format_date_range($firstdate, $lastdate);
|
| 313 |
drupal_set_title(t($title, array("!org"=>$org, "!dates"=>_format_date_range($firstdate, $lastdate))));
|
| 314 |
}
|
| 315 |
|
| 316 |
|
| 317 |
function attachForm()
|
| 318 |
{
|
| 319 |
return "" ;
|
| 320 |
}
|
| 321 |
|
| 322 |
/**
|
| 323 |
* represent a signed number as a credit of debit as preferred by Jessica.
|
| 324 |
*/
|
| 325 |
|
| 326 |
function sign($amount)
|
| 327 |
{
|
| 328 |
return $amount < 0 ? "D ".-$amount : "C ".$amount ;
|
| 329 |
}
|
| 330 |
}
|
| 331 |
|
| 332 |
|
| 333 |
/**
|
| 334 |
* this form allows the user to set the carbon price, target etc
|
| 335 |
* and then request the report that shows the calculations.
|
| 336 |
*/
|
| 337 |
|
| 338 |
|
| 339 |
function carbon_crag_form($form_state, $default_params)
|
| 340 |
{
|
| 341 |
$path = drupal_get_path("module", "carbon");
|
| 342 |
drupal_add_css($path . "/carbon.css");
|
| 343 |
|
| 344 |
$form = array();
|
| 345 |
|
| 346 |
$form["select"] = array(
|
| 347 |
"#type" => "fieldset",
|
| 348 |
"#title" => "CRAG report selection",
|
| 349 |
"#collapsible" => FALSE,
|
| 350 |
"#collapsed" => FALSE);
|
| 351 |
|
| 352 |
$form["select"]["report"] = array(
|
| 353 |
"#type" => "hidden",
|
| 354 |
"#value" => "crag");
|
| 355 |
|
| 356 |
carbon_report_form_build($form["select"], $default_params, array("org", "firstdate", "period"));
|
| 357 |
|
| 358 |
|
| 359 |
$form["select"]["target"] = array(
|
| 360 |
"#type" => "textfield",
|
| 361 |
"#title" => t("target"),
|
| 362 |
"#description" => t("Kg of CO2"),
|
| 363 |
"#default_value" => $default_params["target"],
|
| 364 |
"#required" => FALSE,
|
| 365 |
"#size" => 12,
|
| 366 |
"#maxlength" => 40,
|
| 367 |
"#prefix"=> "<DIV class='float-left'>",
|
| 368 |
"#suffix"=> "</DIV>",
|
| 369 |
"#weight" => 3);
|
| 370 |
|
| 371 |
$form["select"]["cost"] = array(
|
| 372 |
"#type" => "textfield",
|
| 373 |
"#title" => t("cost of carbon"),
|
| 374 |
"#description" => t("£ per Kg"),
|
| 375 |
"#default_value" => $default_params["cost"],
|
| 376 |
"#required" => FALSE,
|
| 377 |
"#size" => 12,
|
| 378 |
"#maxlength" => 40,
|
| 379 |
"#prefix"=> "<DIV class='float-left'>",
|
| 380 |
"#suffix"=> "</DIV>",
|
| 381 |
"#weight" => 4);
|
| 382 |
/*
|
| 383 |
$form["select"]["allocate"] = array(
|
| 384 |
"#type" => "submit",
|
| 385 |
"#value" => t("allocate"),
|
| 386 |
"#prefix"=> "<DIV class='float-left'>",
|
| 387 |
"#suffix"=> "</DIV>",
|
| 388 |
"#weight" => 6);
|
| 389 |
|
| 390 |
$form["select"]["deduct"] = array(
|
| 391 |
"#type" => "submit",
|
| 392 |
"#value" => t("deduct"),
|
| 393 |
"#prefix"=> "<DIV class='float-left'>",
|
| 394 |
"#suffix"=> "</DIV>",
|
| 395 |
"#weight" => 7);
|
| 396 |
*/
|
| 397 |
$form["select"]["submit"] = array(
|
| 398 |
"#type" => "submit",
|
| 399 |
"#value" => t("show settlement"),
|
| 400 |
"#prefix"=> "<DIV class='float-left'>",
|
| 401 |
"#suffix"=> "</DIV>",
|
| 402 |
"#weight" => 8);
|
| 403 |
|
| 404 |
$form["select"]["pay"] = array(
|
| 405 |
"#type" => "submit",
|
| 406 |
"#value" => t("pay settlement"),
|
| 407 |
"#prefix"=> "<DIV class='float-left'>",
|
| 408 |
"#suffix"=> "</DIV>",
|
| 409 |
"#weight" => 9);
|
| 410 |
|
| 411 |
return $form ;
|
| 412 |
}
|
| 413 |
|
| 414 |
|
| 415 |
|
| 416 |
function carbon_crag_form_submit(&$form, &$form_state)
|
| 417 |
{
|
| 418 |
$form_values = $form_state['values'];
|
| 419 |
if (isset($form_values["pay"]))
|
| 420 |
{
|
| 421 |
$op = ($form_values["op"]);
|
| 422 |
if ($op == t("pay settlement"))
|
| 423 |
{
|
| 424 |
_carbon_settle($form, $form_values);
|
| 425 |
drupal_set_message("Settlement processed", "info");
|
| 426 |
}
|
| 427 |
}
|
| 428 |
return carbon_report_form_submit($form, $form_state);
|
| 429 |
}
|
| 430 |
|
| 431 |
function _carbon_settle($form, $form_values)
|
| 432 |
{
|
| 433 |
global $user;
|
| 434 |
global $charts ; $charts = array(t("none"), t("bar"), t("pie"));
|
| 435 |
global $orgs ; // no idea where this is set
|
| 436 |
|
| 437 |
$params = _gather_form($form_values, array("report"=>"accounts", "firstdate"=>"20050101", "period"=>60, "org"=>"","chart"=>"pie",
|
| 438 |
"target"=> null, "cost"=> 0.05));
|
| 439 |
|
| 440 |
if (!empty($params["org"]))
|
| 441 |
$org = $orgs[$params["org"]]; // convert org_index into real name
|
| 442 |
|
| 443 |
$firstdate = CarbonDate::fromYYYYMMDD($params["firstdate"]);
|
| 444 |
$lastdate = _add_months($firstdate, $params["period"]);
|
| 445 |
|
| 446 |
// build a list of all known orgs (scaleable?)
|
| 447 |
|
| 448 |
$reporter = new CarbonCrag();
|
| 449 |
$reporter->initialize($params);
|
| 450 |
|
| 451 |
$map = carbon_report_base($uid, $org, $reporter->header);
|
| 452 |
|
| 453 |
$reporter->settle($map, $uid, $org, $firstdate, $lastdate, $params["target"], $params["cost"]);
|
| 454 |
|
| 455 |
drupal_goto("carbon/transfer");
|
| 456 |
}
|
| 457 |
|
| 458 |
?>
|